Skip to content

Commit 9e81d58

Browse files
committed
fixed event submissions and cleaned up dates and utils
1 parent 03309ae commit 9e81d58

File tree

64 files changed

+2342
-4047
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+2342
-4047
lines changed

.github/workflows/send-newsletter.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ name: Send Newsletter
22

33
on:
44
schedule:
5-
- cron: '0 19 * * *' # 2pm EST (7pm UTC)
5+
- cron: '0 11 * * *' # 9am EST (4pm UTC)
66
workflow_dispatch: # Optional manual trigger
77

88
jobs:

backend/apps/clubs/views.py

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -22,11 +22,7 @@ def get_clubs(request):
2222
search_term = request.GET.get("search", "").strip()
2323
category_filter = request.GET.get("category", "").strip()
2424
cursor = request.GET.get("cursor", "").strip()
25-
limit = int(request.GET.get("limit", "50"))
26-
27-
# Limit boundary check
28-
if limit > 100:
29-
limit = 100
25+
limit = 50
3026

3127
queryset = Clubs.objects.all().order_by("id")
3228

backend/apps/core/auth.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,14 +55,20 @@ def _wrapped_view(request, *args, **kwargs):
5555
error = getattr(request, "error_message", "User not authenticated")
5656
return JsonResponse({"detail": error}, status=401)
5757
request.user = user
58+
59+
# Extract user_id and is_admin from auth_payload (set by JwtAuthBackend)
60+
auth_payload = getattr(request, "auth_payload", {})
61+
request.user_id = auth_payload.get("sub") or auth_payload.get("id")
62+
request.is_admin = auth_payload.get("role") == "admin"
63+
5864
return view_func(request, *args, **kwargs)
5965
return _wrapped_view
6066

6167

6268
def optional_jwt(view_func):
6369
"""
6470
Optional JWT authentication decorator.
65-
If a token is provided and valid, populates request.auth_payload.
71+
If a token is provided and valid, populates request.auth_payload, request.user_id, and request.is_admin.
6672
If no token or invalid token, continues without error (for public endpoints).
6773
"""
6874
@wraps(view_func)
@@ -78,16 +84,24 @@ def _wrapped_view(request, *args, **kwargs):
7884

7985
if state.is_signed_in:
8086
request.auth_payload = state.payload
87+
# Extract user_id (tries 'sub' first, falls back to 'id')
88+
request.user_id = state.payload.get("sub") or state.payload.get("id")
89+
# Check if user is admin
90+
request.is_admin = state.payload.get("role") == "admin"
8191
django_user = AnonymousUser()
82-
django_user.username = state.payload.get("sub") or "clerk_user"
92+
django_user.username = request.user_id or "clerk_user"
8393
request.user = django_user
8494
else:
8595
# No token or invalid token - that's fine, just continue
8696
request.auth_payload = {}
97+
request.user_id = None
98+
request.is_admin = False
8799
request.user = AnonymousUser()
88100
except Exception:
89101
# Any error during auth - that's fine, just continue
90102
request.auth_payload = {}
103+
request.user_id = None
104+
request.is_admin = False
91105
request.user = AnonymousUser()
92106

93107
return view_func(request, *args, **kwargs)
@@ -98,8 +112,7 @@ def admin_required(view_func):
98112
@wraps(view_func)
99113
@jwt_required
100114
def _wrapped_view(request, *args, **kwargs):
101-
role = request.auth_payload.get("role")
102-
if role != "admin":
115+
if not getattr(request, "is_admin", False):
103116
return JsonResponse({"message": "Admin only"}, status=403)
104117
return view_func(request, *args, **kwargs)
105118
return _wrapped_view
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
# Generated by Django 4.2.7 on 2025-11-07 12:42
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("events", "0027_ignoredpost"),
10+
]
11+
12+
operations = [
13+
migrations.RemoveIndex(
14+
model_name="events",
15+
name="events_school_status_dtend_idx",
16+
),
17+
migrations.RemoveIndex(
18+
model_name="events",
19+
name="events_school_status_dtstart_nullend_idx",
20+
),
21+
migrations.RemoveIndex(
22+
model_name="events",
23+
name="events_school_status_dtend_utc_dtstart_utc_idx",
24+
),
25+
migrations.RemoveField(
26+
model_name="eventdates",
27+
name="dtend",
28+
),
29+
migrations.RemoveField(
30+
model_name="eventdates",
31+
name="dtstart",
32+
),
33+
migrations.RemoveField(
34+
model_name="events",
35+
name="all_day",
36+
),
37+
migrations.RemoveField(
38+
model_name="events",
39+
name="dtend",
40+
),
41+
migrations.RemoveField(
42+
model_name="events",
43+
name="dtend_utc",
44+
),
45+
migrations.RemoveField(
46+
model_name="events",
47+
name="dtstamp",
48+
),
49+
migrations.RemoveField(
50+
model_name="events",
51+
name="dtstart",
52+
),
53+
migrations.RemoveField(
54+
model_name="events",
55+
name="dtstart_utc",
56+
),
57+
migrations.RemoveField(
58+
model_name="events",
59+
name="duration",
60+
),
61+
migrations.RemoveField(
62+
model_name="events",
63+
name="rdate",
64+
),
65+
migrations.RemoveField(
66+
model_name="events",
67+
name="rrule",
68+
),
69+
migrations.RemoveField(
70+
model_name="events",
71+
name="tz",
72+
),
73+
migrations.RemoveField(
74+
model_name="eventsubmission",
75+
name="extracted_data",
76+
),
77+
migrations.RemoveField(
78+
model_name="eventsubmission",
79+
name="screenshot_url",
80+
),
81+
migrations.RemoveField(
82+
model_name="eventsubmission",
83+
name="source_url",
84+
),
85+
migrations.RemoveField(
86+
model_name="eventsubmission",
87+
name="status",
88+
),
89+
]
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Generated by Django 4.2.7 on 2025-11-07 21:43
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("events", "0028_remove_events_events_school_status_dtend_idx_and_more"),
10+
]
11+
12+
operations = [
13+
migrations.RemoveField(
14+
model_name="events",
15+
name="latitude",
16+
),
17+
migrations.RemoveField(
18+
model_name="events",
19+
name="longitude",
20+
),
21+
]
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# Generated by Django 4.2.7 on 2025-01-XX XX:XX
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("events", "0029_remove_events_latitude_remove_events_longitude"),
10+
]
11+
12+
operations = [
13+
migrations.RemoveField(
14+
model_name="eventsubmission",
15+
name="admin_notes",
16+
),
17+
]
18+
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 4.2.7 on 2025-11-08 22:26
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('events', '0030_remove_eventsubmission_admin_notes'),
10+
]
11+
12+
operations = [
13+
migrations.RemoveField(
14+
model_name='events',
15+
name='raw_json',
16+
),
17+
]

backend/apps/events/models.py

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -17,25 +17,6 @@ class Events(models.Model):
1717
null=True, blank=True, help_text="'Student Center Ballroom, 123 University Ave'"
1818
)
1919

20-
# iCalendar datetime fields (RFC 5545 standard)
21-
dtstamp = models.DateTimeField(
22-
default=timezone.now, help_text="'time created in UTC, 2024-03-15T10:30:00Z'"
23-
)
24-
dtstart = models.DateTimeField(
25-
default=timezone.now, help_text="'2024-03-20T09:00:00'"
26-
)
27-
dtend = models.DateTimeField(
28-
blank=True, null=True, help_text="'2024-03-20T17:00:00'"
29-
)
30-
dtstart_utc = models.DateTimeField(
31-
default=timezone.now, help_text="'2024-03-20T14:00:00Z'"
32-
)
33-
dtend_utc = models.DateTimeField(
34-
blank=True, null=True, help_text="'2024-03-20T22:00:00Z'"
35-
)
36-
all_day = models.BooleanField(default=False, help_text="True")
37-
duration = models.DurationField(blank=True, null=True, help_text="'8:00:00'")
38-
3920
# Event categorization
4021
categories = models.JSONField(
4122
default=list,
@@ -44,17 +25,6 @@ class Events(models.Model):
4425
help_text="['Career', 'Networking']",
4526
)
4627

47-
# Timezone information
48-
tz = models.CharField(
49-
max_length=64, null=True, blank=True, help_text="'America/New_York'"
50-
)
51-
52-
# Recurrence rules (iCalendar RFC 5545)
53-
rrule = models.TextField(null=True, blank=True, help_text="'FREQ=WEEKLY;BYDAY=MO'")
54-
rdate = models.JSONField(
55-
null=True, blank=True, help_text="['2024-03-25', '2024-04-01']"
56-
)
57-
5828
# Event status
5929
status = models.CharField(
6030
max_length=32,
@@ -63,14 +33,6 @@ class Events(models.Model):
6333
help_text="Event status (e.g., 'CONFIRMED', 'TENTATIVE', 'CANCELLED')",
6434
)
6535

66-
# Geographic location (regular numeric fields)
67-
latitude = models.FloatField(null=True, blank=True, help_text="40.7128")
68-
longitude = models.FloatField(null=True, blank=True, help_text="-74.0059")
69-
70-
# Data provenance and raw extraction
71-
raw_json = models.JSONField(
72-
default=dict, help_text="{'title': 'Career Fair', 'location': 'Student Center'}"
73-
)
7436
source_url = models.TextField(
7537
null=True, blank=True, help_text="'https://university.edu/events/career-fair'"
7638
)
@@ -126,15 +88,7 @@ class Events(models.Model):
12688

12789
class Meta:
12890
db_table = "events"
129-
indexes = [
130-
models.Index(fields=["school", "status", "dtend_utc"], name="events_school_status_dtend_idx"),
131-
models.Index(fields=["school", "status", "dtend_utc", "dtstart_utc"], name="events_school_status_dtend_utc_dtstart_utc_idx"),
132-
models.Index(
133-
fields=["school", "status", "dtstart_utc"],
134-
condition=models.Q(dtend_utc__isnull=True),
135-
name="events_school_status_dtstart_nullend_idx",
136-
),
137-
]
91+
indexes = []
13892

13993
def __str__(self):
14094
return f"{self.title[:50] if self.title else 'untitled'}"
@@ -151,11 +105,6 @@ class EventSubmission(models.Model):
151105

152106
# Submission details
153107
id = models.BigAutoField(primary_key=True)
154-
screenshot_url = models.URLField(help_text="S3 URL of uploaded screenshot")
155-
source_url = models.URLField(help_text="URL to original event source")
156-
status = models.CharField(
157-
max_length=20, choices=STATUS_CHOICES, default="pending", db_index=True
158-
)
159108
submitted_by = models.CharField(
160109
max_length=255, help_text="Clerk user ID who submitted this event"
161110
)
@@ -165,12 +114,6 @@ class EventSubmission(models.Model):
165114
reviewed_at = models.DateTimeField(null=True, blank=True)
166115
reviewed_by = models.CharField(max_length=255, null=True, blank=True)
167116

168-
# Extracted event data (populated after OpenAI processing)
169-
extracted_data = models.JSONField(
170-
null=True, blank=True, help_text="Event data extracted by OpenAI"
171-
)
172-
173-
# Required link to created event
174117
created_event = models.ForeignKey(
175118
Events,
176119
null=False,
@@ -179,9 +122,6 @@ class EventSubmission(models.Model):
179122
related_name="submission",
180123
)
181124

182-
# Admin notes
183-
admin_notes = models.TextField(blank=True)
184-
185125
class Meta:
186126
db_table = "event_submissions"
187127
ordering = ["-submitted_at"]
@@ -193,9 +133,6 @@ def __str__(self):
193133
class EventDates(models.Model):
194134
"""
195135
Stores individual occurrence dates for events.
196-
For events with rrule or rdate, this table contains all computed dates.
197-
For simple events without recurrence, contains a single date entry.
198-
This enables efficient querying of upcoming events without calculating rrule/rdate.
199136
"""
200137

201138
id = models.BigAutoField(primary_key=True)
@@ -206,12 +143,6 @@ class EventDates(models.Model):
206143
db_index=True,
207144
help_text="Reference to the parent event",
208145
)
209-
dtstart = models.DateTimeField(
210-
help_text="Local start time for this occurrence"
211-
)
212-
dtend = models.DateTimeField(
213-
blank=True, null=True, help_text="Local end time for this occurrence"
214-
)
215146
dtstart_utc = models.DateTimeField(
216147
db_index=True, help_text="UTC start time for this occurrence"
217148
)

backend/apps/events/urls.py

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
urlpatterns = [
1010
path("", views.get_events, name="events"),
1111
path("<int:event_id>/", views.get_event, name="event_detail"),
12-
path("<int:event_id>/submission/", views.get_event_submission, name="get_event_submission"),
1312
path("<int:event_id>/update/", views.update_event, name="update_event"),
1413
path("<int:event_id>/delete/", views.delete_event, name="delete_event"),
1514
path("export.ics", views.export_events_ics, name="export_events_ics"),
@@ -27,9 +26,6 @@
2726
path("submissions/<int:submission_id>/", views.delete_submission, name="delete_submission"),
2827
# Event interest endpoints
2928
path("my-interests/", views.get_my_interested_event_ids, name="get_my_interested_event_ids"),
30-
path("<int:event_id>/interest/", views.get_event_interest, name="get_event_interest"),
3129
path("<int:event_id>/interest/mark/", views.mark_event_interest, name="mark_event_interest"),
3230
path("<int:event_id>/interest/unmark/", views.unmark_event_interest, name="unmark_event_interest"),
33-
# Test endpoints
34-
path("test-similarity/", views.test_similarity, name="test_similarity"),
3531
]

0 commit comments

Comments
 (0)