Python : get the next working time slot - python

Handling a staff planning (Python 2.7), I have for a given person a list of working days (and times) in isoWeek.
With a given date I'm trying to determine when will be the next date the staff person will be available.
working_days = {
1:
{
'start': datetime.time(8, 0),
'end': datetime.time(17, 15)
},
2:
{
'start': datetime.time(7, 45),
'end': datetime.time(17, 0)
},
3:
{
'start': datetime.time(8, 0),
'end': datetime.time(16, 45)
},
4:
{
'start': datetime.time(10, 0),
'end': datetime.time(15, 30)
},
5:
{
'start': datetime.time(8, 30),
'end': datetime.time(17, 15)
}
}
searched_date = datetime.datetime(2018, 6, 13, 20, 57, 00) // Wednesday
searched_date_week_day = searched_date.isoweekday()
expected result : datetime.datetime(2018, 6, 14, 10, 00, 00) // Thursday at 10
I would have liked to just use the weekdays to achieve this result but I have my doubts on it.
What I wrote is tricked on the simplest example (my searched time is greater than the end working time of the staff)
next_working_day = next((x for x in working_days if x >= searched_date_week_day), None)
gives: 3 , Wednesday
Considering there will me more tricky cases (let's imagine my searched date is on a Sunday, isoweek 7, it doesn't work), is my only way out is to transform my working_days in some datetime.datetime list?
Fiddle

Using your Fiddle as a starting point:
import datetime
working_days = [{'start': datetime.time(8, 0), 'end': datetime.time(17, 0), 'day': 1}, {'start': datetime.time(8, 0), 'end': datetime.time(17, 0), 'day': 2}, {'start': datetime.time(8, 0), 'end': datetime.time(17, 0), 'day': 3}, {'start': datetime.time(8, 0), 'end': datetime.time(17, 0), 'day': 4}]
searched_date = datetime.datetime(2018, 6, 13, 20, 57, 00)
searched_date_week_day = searched_date.isoweekday()
i = 0
while i < len(working_days) and working_days[i]['day'] < searched_date_week_day:
i = i + 1
# if reached the end then next working day must be first day of next week
if i == len(working_days):
next_working_day = working_days[0]
else:
next_working_day = working_days[(i+1) % len(working_days)]
print next_working_day
It assumes that the entries in your list are in day order.

Related

Boto3 not returning all the resources that are requested

I am trying to import SQS queues based on resource groups and add all the visible messages available among the queues. The problem is boto3 is not returning all the queues sometimes. I know it is the problem with boto3 because I am getting correct values most of the time when I run the same code. In my case, boto3 is supposed to return 3 queues but sometimes either be or two of the queues are missing. Here is the code I have written
import boto3
import time
from datetime import timedelta, datetime
#clients for SQS, Cloudwatch, AutoScalingGroups, and ResourceGroups
sqs = boto3.client('sqs')
cloudwatch = boto3.client('cloudwatch')
asg = boto3.client('autoscaling')
rg = boto3.client('resource-groups')
#Importing SQS Queues with the tag Env:Production
response = rg.list_group_resources(Group='env_prod')
resources = response.get('Resources')
soa = 0.0
noi = 0.0
ABPI = 100
def SumOfAverages(resources, soa, response, cloudwatch):
for idents in resources:
identifier = idents.get('Identifier')
resourcetype = identifier.get('ResourceType')
if resourcetype == 'AWS::SQS::Queue':
RArn = identifier.get('ResourceArn')
step0 = RArn.split(':')
step1 = step0[5]
response1 = cloudwatch.get_metric_statistics(
Namespace='AWS/SQS',
MetricName='ApproximateNumberOfMessagesVisible',
Dimensions=[
{
'Name': 'QueueName',
'Value': step1
},
],
StartTime=datetime.utcnow() - timedelta(minutes=1),
EndTime=datetime.utcnow(),
Period=60,
Statistics=[
'Average',
],
Unit='Count'
)
datapoints = response1['Datapoints']
for values in datapoints:
averages = values['Average']
soa += averages
return(soa)
result = SumOfAverages(resources, soa, response, cloudwatch)
print(result)
Response:
[{'Timestamp': datetime.datetime(2021, 5, 8, 6, 34, tzinfo=tzutc()), 'Average': 122.0, 'Unit': 'Count'}]
[{'Timestamp': datetime.datetime(2021, 5, 8, 6, 34, tzinfo=tzutc()), 'Average': 101.0, 'Unit': 'Count'}]
[{'Timestamp': datetime.datetime(2021, 5, 8, 6, 34, tzinfo=tzutc()), 'Average': 94.0, 'Unit': 'Count'}]
[{'Timestamp': datetime.datetime(2021, 5, 8, 6, 34, tzinfo=tzutc()), 'Average': 122.0, 'Unit': 'Count'}]
[{'Timestamp': datetime.datetime(2021, 5, 8, 6, 34, tzinfo=tzutc()), 'Average': 101.0, 'Unit': 'Count'}]
[]

MongoDB Update N first elements in array

I have the yesterday price history in MongoDB collection:
{
ticker: 'APPL':
hist: [{'Time': datetime.datetime(2020, 7, 15, 0, 0),
'Close': 10.58,
'Volume': 71055},
{'Time': datetime.datetime(2020, 7, 14, 0, 0),
'Close': 10.28,
'Volume': 89012},
{'Time': datetime.datetime(2020, 7, 13, 0, 0),
'Close': 10.26,
'Volume': 12198}
}]
}
and today I have updated price:
{
'AAPL': [{'Time': datetime.datetime(2020, 7, 16, 0, 0),
'Close': 9.78,
'Volume': 8214},
{'Time': datetime.datetime(2020, 7, 15, 0, 0),
'Close': 11.03,
'Volume': 71033},
{'Time': datetime.datetime(2020, 7, 14, 0, 0),
'Close': 10.25,
'Volume': 89026}]
}
The expected result:
{
ticker: 'APPL':
hist: [{'Time': datetime.datetime(2020, 7, 16, 0, 0),
'Close': 9.78,
'Volume': 8214},
{'Time': datetime.datetime(2020, 7, 15, 0, 0),
'Close': 11.03,
'Volume': 71033},
{'Time': datetime.datetime(2020, 7, 14, 0, 0),
'Close': 10.25,
'Volume': 89026},
{'Time': datetime.datetime(2020, 7, 13, 0, 0),
'Close': 10.26,
'Volume': 12198}
}]
}
How to update the existing date and insert the new date the collection without deleting the n-first array first?
Another example, I have the 1-year length array of data, but I need to update the last 7 days in the array and also add today's data.
Any help would be greatly appreciated. Thank you.

Merging overlapping interval objects with dependencies

i need to merge interval objects to get distinct ranges of intervals based on extra parameters. How is the best way to do that?
It's about unambiguous statement whether in a given hour state is true. The returned list must have non-duplicated intervals.
Interval object description:
{
'startDate': datetime.datetime, # start of interval
'endDate': datetime.datetime, # end of interval
'prioritized': bool # if True - it's always important, override no-prioritized intervals
'state': bool # result of interval
}
In the examples below i changed startDate/endDate to strings to make them look better.
Interval list look like:
interval_list = [
{'startDate': '10:00:00', 'endDate': '12:00:00', 'prioritized': False, 'state': False},
{'startDate': '11:00:00', 'endDate': '18:00:00', 'prioritized': True, 'state': True},
{'startDate': '13:00:00', 'endDate': '17:00:00', 'prioritized': False, 'state': False},
{'startDate': '17:00:00', 'endDate': '20:00:00', 'prioritized': False, 'state': True},
{'startDate': '19:30:00', 'endDate': '19:45:00', 'prioritized': True, 'state': False}
]
I am trying to achieve the following:
merge(interval_list) should return:
[
{'startDate': '10:00:00', 'endDate': '11:00:00', 'state': False},
{'startDate': '11:00:00', 'endDate': '19:30:00', 'state': True},
{'startDate': '19:30:00', 'endDate': '19:45:00', 'state': False},
{'startDate': '19:45:00', 'endDate': '20:00:00', 'state': True},
]
I have following not completed code right now:
def merge_range(ranges: list):
ranges = sorted(ranges, key=lambda x: x['startDate'])
last_interval = dict(ranges[0])
for current_interval in sorted(ranges, key=lambda x: x['startDate']):
if current_interval['startDate'] > last_interval['endDate']:
yield dict(last_interval)
last_interval['startDate'] = current_interval['startDate']
last_interval['endDate'] = current_interval['endDate']
last_interval['prioritized'] = current_interval['prioritized']
last_interval['state'] = current_interval['state']
else:
if current_interval['state'] == last_interval['state']:
last_interval['endDate'] = max(last_interval['endDate'], current_interval['endDate'])
else:
pass # i stopped here
yield dict(last_interval)
And use it by merged_interval_list = list(merge_range(interval_list))
Is it a good way ?
I got an answer for this question:
For first i separate events to prioritized and non-prioritize lists.
Based on the priority list, I create a negation of the interval on a given day.
Next i set prioritized list as main list and start iterate over non-prioritize list.
import datetime
from pprint import pprint
df = "%Y-%m-%d %H:%M:%S"
ds = "%Y-%m-%d"
events = {}
prioritized_events = {}
events["2019-05-10"] = [{
'startDate': datetime.datetime.strptime("2019-05-10 01:00:00", df),
'endDate': datetime.datetime.strptime("2019-05-10 02:00:00", df),
'state': True
}, {
'startDate': datetime.datetime.strptime("2019-05-10 10:00:00", df),
'endDate': datetime.datetime.strptime("2019-05-10 12:00:00", df),
'state': False
}, {
'startDate': datetime.datetime.strptime("2019-05-10 13:00:00", df),
'endDate': datetime.datetime.strptime("2019-05-10 17:00:00", df),
'state': False
}, {
'startDate': datetime.datetime.strptime("2019-05-10 17:00:00", df),
'endDate': datetime.datetime.strptime("2019-05-10 20:00:00", df),
'state': True
}]
prioritized_events["2019-05-10"] = [{
'startDate': datetime.datetime.strptime("2019-05-10 11:00:00", df),
'endDate': datetime.datetime.strptime("2019-05-10 18:00:00", df),
'state': True
}, {
'startDate': datetime.datetime.strptime("2019-05-10 19:30:00", df),
'endDate': datetime.datetime.strptime("2019-05-10 20:00:00", df),
'state': False
}]
allowed_intervals = []
for event_date in prioritized_events:
minimal_time = datetime.datetime.combine(datetime.datetime.strptime(event_date, ds), datetime.time.min)
maximum_time = datetime.datetime.combine(datetime.datetime.strptime(event_date, ds), datetime.time.max)
for ev in prioritized_events[event_date]:
if ev['startDate'] != minimal_time:
allowed_intervals.append({
'startDate': minimal_time,
'endDate': ev['startDate']
})
minimal_time = ev['endDate']
if prioritized_events[event_date][len(prioritized_events[event_date]) - 1]['endDate'] != maximum_time:
allowed_intervals.append({
'startDate': prioritized_events[event_date][len(prioritized_events[event_date]) - 1]['endDate'],
'endDate': maximum_time
})
for event_date in events:
if event_date not in prioritized_events:
prioritized_events[event_date] = events[event_date]
else:
for ev in events[event_date]:
start = ev['startDate']
end = ev['endDate']
state = ev['state']
done = False
for allowed_interval in allowed_intervals:
if start >= allowed_interval['startDate'] and end <= allowed_interval['endDate']:
prioritized_events[event_date].append({
'startDate': start,
'endDate': end,
'state': state
})
done = True
break
elif allowed_interval['startDate'] <= start < allowed_interval['endDate'] < end:
prioritized_events[event_date].append({
'startDate': start,
'endDate': allowed_interval['endDate'],
'state': state
})
start = allowed_interval['endDate']
elif start < allowed_interval['startDate'] and start < allowed_interval['endDate'] < end:
prioritized_events[event_date].append({
'startDate': allowed_interval['startDate'],
'endDate': allowed_interval['endDate'],
'state': state
})
start = allowed_interval['endDate']
elif start < allowed_interval['startDate'] and start < allowed_interval['endDate'] and allowed_interval['startDate'] < end <= allowed_interval['endDate']:
prioritized_events[event_date].append({
'startDate': allowed_interval['startDate'],
'endDate': end,
'state': state
})
start = end
if done:
continue
prioritized_events[event_date] = sorted(prioritized_events[event_date], key=lambda k: k['startDate'])
And now sorted list:
pprint(prioritized_events["2019-05-10"])
returns:
[
{'startDate': datetime.datetime(2019, 5, 10, 1, 0),
'endDate': datetime.datetime(2019, 5, 10, 2, 0),
'state': True
},
{'startDate': datetime.datetime(2019, 5, 10, 10, 0),
'endDate': datetime.datetime(2019, 5, 10, 11, 0),
'state': False
},
{'startDate': datetime.datetime(2019, 5, 10, 11, 0),
'endDate': datetime.datetime(2019, 5, 10, 18, 0),
'state': True
},
{'startDate': datetime.datetime(2019, 5, 10, 18, 0),
'endDate': datetime.datetime(2019, 5, 10, 19, 30),
'state': True
},
{'startDate': datetime.datetime(2019, 5, 10, 19, 30),
'endDate': datetime.datetime(2019, 5, 10, 20, 0),
'state': False
}
]
When we deal with time intervals, the main idea is to sort the dates (start and end) along with their status: start or end. Here, we need an access to the original interval too, to handle priorities and states.
Let's try with this list:
interval_list = [
{'startDate': '10:00:00', 'endDate': '12:00:00', 'prioritized': False, 'state': False},
{'startDate': '11:00:00', 'endDate': '18:00:00', 'prioritized': True, 'state': True},
{'startDate': '13:00:00', 'endDate': '17:00:00', 'prioritized': False, 'state': False},
{'startDate': '17:00:00', 'endDate': '20:00:00', 'prioritized': False, 'state': True},
{'startDate': '19:30:00', 'endDate': '19:45:00', 'prioritized': True, 'state': False}
]
First, we convert datestrings to dates (as you did):
import datetime
day = '2019-05-10'
def get_datetime(d, t):
return datetime.datetime.strptime(d+" "+t, "%Y-%m-%d %H:%M:%S")
for interval in interval_list:
interval['startDate'] = get_datetime(day, interval['startDate'])
interval['endDate'] = get_datetime(day, interval['endDate'])
Now, we build a new list with the needed information:
L = sorted(
[(interval['startDate'], 1, i) for i, interval in enumerate(interval_list)]
+[(interval['endDate'], -1, i) for i, interval in enumerate(interval_list)]
)
L is the following list of tuples (date, dir, index) (dir: 1 means it's a start date, -1 means it's an end date):
[(datetime.datetime(2019, 5, 10, 10, 0), 1, 0), (datetime.datetime(2019, 5, 10, 11, 0), 1, 1), (datetime.datetime(2019, 5, 10, 12, 0), -1, 0), (datetime.datetime(2019, 5, 10, 13, 0), 1, 2), (datetime.datetime(2019, 5, 10, 17, 0), -1, 2), (datetime.datetime(2019, 5, 10, 17, 0), 1, 3), (datetime.datetime(2019, 5, 10, 18, 0), -1, 1), (datetime.datetime(2019, 5, 10, 19, 30), 1, 4), (datetime.datetime(2019, 5, 10, 19, 45), -1, 4), (datetime.datetime(2019, 5, 10, 20, 0), -1, 3)]
Now we can iterate over L and keep a track of the current state and priority to yield dates when state is modified according to given priority:
def interval_info(i):
interval = interval_list[i]
return interval['state'], interval['prioritized']
T = []
stack = []
for boundary_date, direction, i in L:
state, prioritized = interval_info(i) # state and priority of the current date
if direction == 1: # start date
if stack:
prev_state, prev_prioritized = interval_info(stack[-1]) # previous infos
if state != prev_state and prioritized >= prev_prioritized: # enter a new state with a greater or equal priority
T.append((boundary_date, state)) # enter in new state
else: # begin of covered area
T.append((boundary_date, state)) # enter in new state
stack.append(i) # add the opened interval
elif direction == -1: # end date
stack.remove(i) # remove the closed interval (i is a *value* in stack)
if stack:
prev_state, prev_prioritized = interval_info(stack[-1])
if state != prev_state and not prev_prioritized: # leave a non priority state
T.append((boundary_date, prev_state)) # re-enter in prev state
else: # end of covered area
T.append((boundary_date, None)) # enter in None state
The value of T is:
[(datetime.datetime(2019, 5, 10, 10, 0), False), (datetime.datetime(2019, 5, 10, 11, 0), True), (datetime.datetime(2019, 5, 10, 19, 30), False), (datetime.datetime(2019, 5, 10, 19, 45), True), (datetime.datetime(2019, 5, 10, 20, 0), None)]
You can then easily produce the output you wanted. Hope it helps!
EDIT: Bonus: how to convert start dates to time intervals:
>>> import datetime
>>> T = [(datetime.datetime(2019, 5, 10, 10, 0), False), (datetime.datetime(2019, 5, 10, 11, 0), True), (datetime.datetime(2019, 5, 10, 19, 30), False), (datetime.datetime(2019, 5, 10, 19, 45), True), (datetime.datetime(2019, 5, 10, 20, 0), None)]
>>> [{'startDate': s[0], 'endDate': e[0], 'state': s[1]} for s,e in zip(T, T[1:])]
[{'startDate': datetime.datetime(2019, 5, 10, 10, 0), 'endDate': datetime.datetime(2019, 5, 10, 11, 0), 'state': False}, {'startDate': datetime.datetime(2019, 5, 10, 11, 0), 'endDate': datetime.datetime(2019, 5, 10, 19, 30), 'state': True}, {'startDate': datetime.datetime(2019, 5, 10, 19, 30), 'endDate': datetime.datetime(2019, 5, 10, 19, 45), 'state': False}, {'startDate': datetime.datetime(2019, 5, 10, 19, 45), 'endDate': datetime.datetime(2019, 5, 10, 20, 0), 'state': True}]
You just have to zip every start date with the the next one to get intervals.

Sort list of Dict By Multiple Keys, Including List

I would like to sort this list of dicts by a list key and then by date.
I am trying to sort the dicts by 'label' according the label_order and then by descending 'date'.
label_order = [3, 4, 2, 1]
data = [
{'label': 1, 'data': 5, 'date': datetime(2018, 12, 31)},
{'label': 3, 'data': 2, 'date': datetime(2017, 12, 31)},
{'label': 3, 'data': 1, 'date': datetime(2018, 12, 31)},
{'label': 4, 'data': 3, 'date': datetime(2018, 12, 31)},
{'label': 4, 'data': 4, 'date': datetime(2018, 12, 25)},
]
After sorting would look like this:
data = [
{'label': 3, 'data': 1, 'date': datetime(2018, 12, 31)},
{'label': 3, 'data': 2, 'date': datetime(2017, 12, 31)},
{'label': 4, 'data': 3, 'date': datetime(2018, 12, 31)},
{'label': 4, 'data': 4, 'date': datetime(2018, 12, 25)},
{'label': 1, 'data': 5, 'date': datetime(2018, 12, 31)},
]
I've tried lambda expressions and itemgetter, but I am having difficulty combining the right strategies for the sort key. Maybe it is just trying to do too much at one time.
Any help or direction would be appreciated.
A more efficient approach is to build a dict that maps items in label_order to indices, so that you can use the indices as keys when performing the sort:
keys = {n: i for i, n in enumerate(label_order)}
sorted(data, key=lambda d: (-keys[d['label']], d['date']), reverse=True)
This returns:
[{'label': 3, 'data': 1, 'date': datetime(2018, 12, 31)},
{'label': 3, 'data': 2, 'date': datetime(2017, 12, 31)},
{'label': 4, 'data': 3, 'date': datetime(2018, 12, 31)},
{'label': 4, 'data': 4, 'date': datetime(2018, 12, 25)},
{'label': 1, 'data': 5, 'date': datetime(2018, 12, 31)}]
It's a little tricky to sort dates in reverse order. Instead, let's use the negative of the label's index so they're sorted in descending order. Then we can reverse the sorting and get the results in the order we actually want!
from datetime import datetime
label_order = [3, 4, 2, 1]
data = [
{'label': 1, 'data': 5, 'date': datetime(2018, 12, 31)},
{'label': 3, 'data': 2, 'date': datetime(2017, 12, 31)},
{'label': 3, 'data': 1, 'date': datetime(2018, 12, 31)},
{'label': 4, 'data': 3, 'date': datetime(2018, 12, 31)},
{'label': 4, 'data': 4, 'date': datetime(2018, 12, 25)},
]
def descending_sort_key(item):
return -label_order.index(item['label']), item['date']
data.sort(key=descending_sort_key, reverse=True)
Voila - no date math or other trickery.

Removing duplicates dictionary from a list except one dictionary can be duplicate using python

I have a list like
d=[{'date': datetime.datetime(2016, 6, 3, 8, 32, 35), 'temp': 39.1}, {'date': datetime.datetime(2016, 6, 3, 9, 32, 35), 'temp': 39.1}, {'date': datetime.datetime(2016, 6, 3, 9, 32, 35), 'temp': 39.1}, {'date': None, 'temp': None}, {'date': datetime.datetime(2016, 6, 3, 11, 32, 35), 'temp': 39.1},{'date': None, 'temp': None}]
My output should be
d=[{'date': datetime.datetime(2016, 6, 3, 8, 32, 35), 'temp': 39.1}, {'date': datetime.datetime(2016, 6, 3, 9, 32, 35), 'temp': 39.1},{'date': None, 'temp': None}, {'date': datetime.datetime(2016, 6, 3, 11, 32, 35), 'temp': 39.1},{'date': None, 'temp': None}]
i.e it should remove all duplicate entries from a list except it should not remove if there is {'date': None, 'temp': None} as duplicates.
I had tried
result = [i for n, i in enumerate(d) if i not in d[n + 1:]]
But it is removing including {'date': None, 'temp': None} if there is duplicate. How can i do this using python?
If you are okay with non-list-comprehension solution, then try this:
>>> result = []
>>> for i in d:
... if (i not in result) or (i == {'date': None, 'temp': None}):
... result.append(i)
You could simply add an or:
result = [i for n, i in enumerate(d) if i not in d[n + 1:] or i == {'date': None, 'temp': None}]

Categories

Resources