from app.metrics.contentful import get_all_entries, get_all_published_entries, update_entry_using_json_response, publish_entry from datetime import datetime, timezone def update_event_sort_order(event): event_entry_id = event['sys']['id'] event_entry_fields = event['fields'] event_entry_metadata = event['metadata'] if 'startDate' in event_entry_fields and 'upcomingSortOrder' in event_entry_fields: start_date = event_entry_fields['startDate']['en-US'] end_date = None if 'endDate' in event_entry_fields: end_date = event_entry_fields['endDate']['en-US'] upcoming_sort_order = calculate_sort_order(start_date, end_date) event_entry_fields['upcomingSortOrder']['en-US'] = upcoming_sort_order event_state = { 'fields': event_entry_fields, 'metadata': event_entry_metadata } return update_entry_using_json_response('event', event_entry_id, event_state) else: return event def update_all_events_sort_order(): all_event_entries = get_all_entries("event") all_published_event_entries = get_all_published_entries("event") # Create dict with id's as the key so we do not have to iterate through each time we publish an entry published_event_id_to_fields_mapping = {} for published_event in all_published_event_entries: published_event_id = published_event['sys']['id'] published_event_id_to_fields_mapping[published_event_id] = published_event for entry in all_event_entries: original_fields_dict = entry['fields'] original_metadata_dict = entry['metadata'] if 'startDate' in original_fields_dict and 'upcomingSortOrder' in original_fields_dict and entry['sys']['id']: entry_id = entry['sys']['id'] entry_had_existing_changes = False entry_is_published = False if 'publishedAt' in entry['sys']: entry_is_published = True # convert UTC time strings into datetime objects entry_updated_at = datetime.strptime(entry['sys']['updatedAt'], '%Y-%m-%dT%H:%M:%S.%fZ') entry_published_at = datetime.strptime(entry['sys']['publishedAt'], '%Y-%m-%dT%H:%M:%S.%fZ') if (entry_updated_at - entry_published_at).total_seconds() > 0: entry_had_existing_changes = True start_date = original_fields_dict['startDate']['en-US'] end_date = None if 'endDate' in original_fields_dict: end_date = original_fields_dict['endDate']['en-US'] upcoming_sort_order = calculate_sort_order(start_date, end_date) original_fields_dict['upcomingSortOrder']['en-US'] = upcoming_sort_order if entry_is_published: # if entry has changes that are not yet published then we want to publish only the already published state published_fields_state = published_event_id_to_fields_mapping[entry_id]['fields'] published_fields_state['upcomingSortOrder']['en-US'] = upcoming_sort_order updated_state = { 'fields': published_fields_state, 'metadata': published_event_id_to_fields_mapping[entry_id]['metadata'] } updated_entry = update_entry_using_json_response('event', entry_id, updated_state) publish_entry(entry_id, updated_entry['sys']['version']) # after publishing, update it again with the pre-existing changes that were already there. Or if it is a draft then just update it in order to set the sort order if entry_had_existing_changes or not entry_is_published: original_state = { 'fields': original_fields_dict, 'metadata': original_metadata_dict } update_entry_using_json_response('event', entry_id, original_state) def calculate_sort_order(start_date, end_date=None): # convert from ISO time format provided by contentful in UTC timezone start_date_datetime = datetime.fromisoformat(start_date).astimezone(timezone.utc) now = datetime.now().astimezone(timezone.utc) time_from_event_in_seconds = (start_date_datetime - now).total_seconds() time_from_event_in_days = int(time_from_event_in_seconds / 86400) # in order to maintain the correct event sorting for upcoming (closet first, followed by closest in the future, followed by closest in the past), # we cannot simply keep track of the time from the event. Instead we take the inverse of the dates in the future so that they are less than the nearest future dates. upcoming_sort_order = 1.1 # if start date is in the future if time_from_event_in_days > 0: upcoming_sort_order = 1/time_from_event_in_days # is start date has passed if time_from_event_in_days < 0: if end_date is None: upcoming_sort_order = time_from_event_in_days else: end_date_datetime = datetime.fromisoformat(end_date).astimezone(timezone.utc) end_time_from_now_in_seconds = (end_date_datetime - now).total_seconds() end_time_from_now_in_days = int(end_time_from_now_in_seconds / 86400) # if the event is ongoing (meaning its end date has not yet passed) has_event_ended = end_time_from_now_in_days < 0 if not has_event_ended: # show ongoing events first upcoming_sort_order = 1 else: upcoming_sort_order = time_from_event_in_days return upcoming_sort_order