Skip to content

Commit 5735754

Browse files
Merge pull request #115 from cuappdev/master
hopefully fixing prod
2 parents 08b2f50 + 60a9c68 commit 5735754

7 files changed

Lines changed: 73 additions & 51 deletions

File tree

.pre-commit-config.yaml

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,25 @@
11
repos:
22
# Updates all subsequent hooks
3-
- repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update
4-
rev: v0.5.1
3+
- repo: https://gitlab.com/vojko.pribudic.foss/pre-commit-update
4+
rev: v0.8.0
55
hooks:
6-
- id: pre-commit-update
6+
- id: pre-commit-update
77

88
# Linter hook
9-
- repo: https://github.com/astral-sh/ruff-pre-commit
10-
rev: v0.6.7
9+
- repo: https://github.com/astral-sh/ruff-pre-commit
10+
rev: v0.13.0
1111
hooks:
12-
- id: ruff
12+
- id: ruff
1313
args: [--fix]
1414

1515
# Formatter hook
16-
- repo: https://github.com/psf/black-pre-commit-mirror
17-
rev: 24.8.0
16+
- repo: https://github.com/psf/black-pre-commit-mirror
17+
rev: 25.1.0
1818
hooks:
19-
- id: black
19+
- id: black
2020

2121
# Static code analysis hook
22-
- repo: https://github.com/PyCQA/bandit
23-
rev: 1.7.10
22+
- repo: https://github.com/PyCQA/bandit
23+
rev: 1.8.6
2424
hooks:
25-
- id: bandit
25+
- id: bandit

requirements.txt

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ inflection==0.5.1
2626
itypes==1.2.0
2727
Jinja2==3.1.4
2828
MarkupSafe==2.1.5
29+
memory-profiler==0.61.0
2930
mypy-extensions==0.4.3
3031
nodeenv==1.9.1
3132
oauthlib==3.1.1
@@ -35,6 +36,7 @@ pathspec==0.9.0
3536
platformdirs==4.2.2
3637
pre-commit==3.8.0
3738
protobuf==3.19.1
39+
psutil==7.1.0
3840
psycopg2-binary==2.9.3
3941
pyasn1==0.4.8
4042
pyasn1-modules==0.2.8
@@ -51,6 +53,8 @@ sqlparse==0.4.2
5153
tomli==1.2.3
5254
typing_extensions==4.0.1
5355
uritemplate==4.1.1
54-
urllib3
56+
urllib3==1.26.20
5557
virtualenv==20.26.3
56-
google-auth
58+
orjson
59+
drf-orjson
60+
djangorestframework-orjson

src/eatery/controllers/populate_eatery.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from eatery.serializers import EaterySerializer
44
from eatery.models import Eatery
55
from django.core.exceptions import ObjectDoesNotExist
6+
from memory_profiler import profile
67

78

89
class PopulateEateryController:
@@ -88,6 +89,7 @@ def add_eatery_store(self):
8889
else:
8990
print(serialized.errors)
9091

92+
@profile
9193
def process(self, json_eateries):
9294
for json_eatery in json_eateries:
9395
self.generate_eatery(json_eatery)

src/eatery/serializers.py

Lines changed: 1 addition & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,10 @@
11
from rest_framework import serializers
22
from eatery.models import Eatery
3-
from event.models import Event
43
from event.serializers import (
54
EventSerializer,
65
EventSerializerSimple,
76
EventSerializerOptimized,
87
)
9-
from datetime import timedelta, datetime
10-
from zoneinfo import ZoneInfo
118

129

1310
class EaterySerializer(serializers.ModelSerializer):
@@ -107,25 +104,7 @@ class EaterySerializerByDay(serializers.ModelSerializer):
107104
allow_null=True,
108105
default="https://images-prod.healthline.com/hlcmsresource/images/AN_images/health-benefits-of-apples-1296x728-feature.jpg",
109106
)
110-
events = serializers.SerializerMethodField()
111-
112-
def get_events(self, obj):
113-
day_offset = self.context.get("day")
114-
now = datetime.now(ZoneInfo("America/New_York"))
115-
day = now.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(
116-
days=day_offset
117-
)
118-
day_unix = int(day.timestamp())
119-
day_end_unix = int((day + timedelta(days=1)).timestamp())
120-
print(f"Now: {now}")
121-
print(f"Day: {day}")
122-
print(f"Day Unix: {day_unix}")
123-
print(f"Day End Unix: {day_end_unix}")
124-
events = Event.objects.filter(
125-
eatery=obj.id, start__gte=day_unix, start__lt=day_end_unix
126-
)
127-
serializer = EventSerializerOptimized(instance=events, many=True)
128-
return serializer.data
107+
events = EventSerializerOptimized(many=True, source="filtered_events")
129108

130109
class Meta:
131110
model = Eatery

src/eatery/views.py

Lines changed: 44 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
EaterySerializerByDay,
55
EaterySerializerOptimized,
66
)
7+
from django.db.models import Prefetch
78
from eatery.util.json import FieldType, error_json, success_json, verify_json_fields
89
from django.http import JsonResponse
910
from django.shortcuts import get_object_or_404
@@ -15,7 +16,11 @@
1516
from .permissions import EateryPermission
1617
from eatery.datatype.Eatery import EateryID
1718
from eatery.models import Eatery
19+
from event.models import Event
1820
from .controllers.update_eatery import UpdateEateryController
21+
from memory_profiler import profile
22+
from datetime import timedelta, datetime
23+
from zoneinfo import ZoneInfo
1924

2025

2126
class EateryViewSet(viewsets.ModelViewSet):
@@ -27,29 +32,37 @@ class EateryViewSet(viewsets.ModelViewSet):
2732
serializer_class = EaterySerializer
2833
permission_classes = [EateryPermission]
2934

35+
@profile
3036
def get_queryset(self):
3137
"""
3238
Override to add prefetch_related for optimization
3339
"""
3440
queryset = super().get_queryset()
35-
36-
# prefetch all related objects to avoid N+1 query problem
37-
return queryset.prefetch_related(
38-
'events__menu__items__dietary_preferences',
39-
'events__menu__items__allergens'
40-
)
4141

42+
# Prefetching only needed for list to prevent N+1
43+
if self.action == "list":
44+
return queryset.prefetch_related(
45+
"events__menu__items__dietary_preferences",
46+
"events__menu__items__allergens",
47+
)
48+
49+
return queryset
50+
51+
@profile
52+
@method_decorator(cache_page(60 * 60 * 2)) # cache for 2 hours
4253
def retrieve(self, request, *args, **kwargs):
4354
instance = self.get_object()
4455
serializer = EaterySerializerOptimized(instance)
4556
return Response(serializer.data)
4657

58+
@profile
4759
@method_decorator(cache_page(60 * 60 * 2)) # cache for 2 hours
4860
def list(self, request, *args, **kwargs):
4961
queryset = self.filter_queryset(self.get_queryset())
5062
serializer = EaterySerializerOptimized(queryset, many=True)
5163
return Response(serializer.data)
5264

65+
@profile
5366
def get_object(self):
5467
queryset = self.filter_queryset(self.get_queryset())
5568
lookup_url_kwarg = self.lookup_url_kwarg or self.lookup_field
@@ -109,10 +122,9 @@ class GetEateriesSimple(APIView):
109122
View all eateries with less information
110123
"""
111124

125+
@profile
112126
def get(self, request):
113-
eateries_queryset = Eatery.objects.prefetch_related(
114-
'events'
115-
).all()
127+
eateries_queryset = Eatery.objects.prefetch_related("events").all()
116128
eateries = EaterySerializerSimple(eateries_queryset, many=True)
117129
return Response(eateries.data)
118130

@@ -122,16 +134,34 @@ class GetEateriesByDay(APIView):
122134
Get all eatery information by day
123135
"""
124136

137+
@profile
125138
@method_decorator(cache_page(60 * 60 * 2)) # cache for 2 hours
126139
def get(self, request, day):
140+
now = datetime.now(ZoneInfo("America/New_York"))
141+
start_date = now.replace(hour=0, minute=0, second=0, microsecond=0) + timedelta(
142+
days=day
143+
)
144+
end_date = start_date + timedelta(days=1)
145+
146+
start_unix = int(start_date.timestamp())
147+
end_unix = int(end_date.timestamp())
148+
149+
filtered_events_prefetch = Prefetch(
150+
"events",
151+
queryset=Event.objects.filter(
152+
start__gte=start_unix, start__lt=end_unix
153+
).prefetch_related(
154+
"menu__items__dietary_preferences", "menu__items__allergens"
155+
),
156+
to_attr="filtered_events",
157+
)
158+
127159
eateries_queryset = Eatery.objects.prefetch_related(
128-
'events__menu__items__dietary_preferences',
129-
'events__menu__items__allergens'
160+
filtered_events_prefetch
130161
).exclude(events__event_description="Open")
131-
162+
132163
eateries = EaterySerializerByDay(
133164
eateries_queryset,
134165
many=True,
135-
context={"day": day},
136166
)
137-
return Response(eateries.data)
167+
return Response(eateries.data)

src/eatery_blue_backend/management/commands/populate_models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
import os
1212
import json
1313
import shutil
14+
from memory_profiler import profile
1415

1516

1617
class Command(BaseCommand):
@@ -121,6 +122,7 @@ def logger_wrapper(self, command_obj, log_title, args):
121122
print(f"Done ({int(datetime.now().timestamp()) - pre}s) ")
122123
return output
123124

125+
@profile
124126
def process(self):
125127
"""
126128
1. Get JSON from API

src/eatery_blue_backend/settings.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444

4545
# Application definition
4646

47+
REST_FRAMEWORK = {
48+
"DEFAULT_RENDERER_CLASSES": ("drf_orjson.renderers.ORJSONRenderer",),
49+
"DEFAULT_PARSER_CLASSES": ("drf_orjson.parsers.ORJSONParser",),
50+
}
51+
4752
INSTALLED_APPS = [
4853
"django.contrib.admin",
4954
"django.contrib.auth",
@@ -115,7 +120,7 @@
115120
"PRE_PING": True,
116121
"ECHO": False,
117122
"TIMEOUT": 30,
118-
}
123+
},
119124
}
120125
}
121126

0 commit comments

Comments
 (0)