Skip to content

Commit

Permalink
DBC22-1483: event query optimization (#227)
Browse files Browse the repository at this point in the history
* DBC22-1483: optimized update queries to only what's needed

* DBC22-1483: unit test fix
  • Loading branch information
ray-oxd authored Dec 22, 2023
1 parent e5f4a6f commit e467b18
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 23 deletions.
17 changes: 17 additions & 0 deletions src/backend/apps/event/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,23 @@ class EVENT_DIRECTION:
'last_updated'
]

EVENT_UPDATE_FIELDS = [
'description',
'event_type',
'event_sub_type',
'status',
'severity',
'direction',
'last_updated',
'location',
'route_at',
'route_from',
'route_to',
'schedule',
'start',
'end',
]


class EVENT_DISPLAY_CATEGORY:
MAJOR_DELAYS = 'majorEvents'
Expand Down
55 changes: 43 additions & 12 deletions src/backend/apps/event/tasks.py
Original file line number Diff line number Diff line change
@@ -1,45 +1,76 @@
import logging

from apps.event.enums import EVENT_DIFF_FIELDS, EVENT_STATUS
from apps.event.enums import EVENT_DIFF_FIELDS, EVENT_STATUS, EVENT_UPDATE_FIELDS
from apps.event.models import Event
from apps.event.serializers import EventSerializer
from apps.feed.client import FeedClient
from apps.shared.enums import CacheKey
from django.contrib.gis.geos import LineString, Point
from django.core.cache import cache
from django.core.exceptions import ObjectDoesNotExist

logger = logging.getLogger(__name__)


def populate_event_from_data(event_data):
event_id = event_data.get('id')
def compare_data(current_field_data, new_field_data):
if isinstance(current_field_data, Point):
new_point = Point(new_field_data['coordinates'])
return current_field_data.equals(new_point)

elif isinstance(current_field_data, LineString):
new_ls = LineString(new_field_data['coordinates'])
return current_field_data.equals(new_ls)

else:
return current_field_data == new_field_data


def build_data_diff(current_obj, new_obj_data):
data_diff = {}
for field in EVENT_UPDATE_FIELDS:
current_field_data = getattr(current_obj, field)
new_field_data = new_obj_data[field] if field in new_obj_data else None
if not compare_data(current_field_data, new_field_data):
data_diff[field] = new_field_data

return data_diff


def populate_event_from_data(new_event_data):
event_id = new_event_data.get('id')

try:
event = Event.objects.get(id=event_id)

# Only update if existing data differs for at least one of the fields
for field in EVENT_DIFF_FIELDS:
if not compare_data(getattr(event, field), new_event_data[field]):
# Found diff, update and stop loop
data_diff = build_data_diff(event, new_event_data)
Event.objects.filter(id=event_id).update(**data_diff)
break

except ObjectDoesNotExist:
event = Event(id=event_id)

# Only update if existing data differs for at least one of the fields
for field in EVENT_DIFF_FIELDS:
if getattr(event, field) != event_data[field]:
event_serializer = EventSerializer(event, data=event_data)
event_serializer.is_valid(raise_exception=True)
event_serializer.save()
return
event_serializer = EventSerializer(event, data=new_event_data)
event_serializer.is_valid(raise_exception=True)
event_serializer.save()


def populate_all_event_data():
feed_data = FeedClient().get_event_list()['events']

active_event_ids = []
for event_data in feed_data:
populate_event_from_data(event_data)

# Event is active
if "id" in event_data:
active_event_ids.append(event_data["id"])

# Mark events absent in the feed as inactive
Event.objects.all().exclude(id__in=active_event_ids)\
Event.objects.filter(status=EVENT_STATUS.ACTIVE)\
.exclude(id__in=active_event_ids)\
.update(status=EVENT_STATUS.INACTIVE)

# Rebuild cache
Expand Down
19 changes: 8 additions & 11 deletions src/backend/apps/event/tests/test_event_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,20 +38,17 @@ def setUp(self):
tzinfo=zoneinfo.ZoneInfo(key="America/Vancouver")
),
)
self.event.schedule = {"intervals": [
"2023-05-23T14:00/2023-07-22T14:00"
]
}
self.event.schedule = {"intervals": [
"2023-05-23T14:00/2023-07-22T14:00"
]
}
self.event.schedule = {
"intervals": [
"2023-05-23T14:00/2023-07-22T14:00"
]
}
self.event_two = copy(self.event)

self.event.id = "1"
self.event.route_from = "at Test Road"
self.event.route_to = "Test Avenue"

self.event.save()

self.event_two.id = "2"
Expand All @@ -70,14 +67,14 @@ def setUp(self):
self.serializer_two = EventSerializer(self.event_two)

def test_serializer_data(self):
assert len(self.serializer.data) == 18
assert len(self.serializer.data) == 19
# route_from beings with 'at '
assert self.serializer.data['route_display'] == \
"Test Road to Test Avenue"
assert self.serializer.data['direction_display'] == \
"Northbound"

assert len(self.serializer_two.data) == 18
assert len(self.serializer_two.data) == 19
# route_from doesn't being with 'at '
assert self.serializer_two.data['route_display'] == \
"Test Road Two to Test Avenue Two"
Expand Down

0 comments on commit e467b18

Please sign in to comment.