membershipworks: Add storage and scraping for Event details

This commit is contained in:
Adam Goldsmith 2024-01-29 21:48:19 -05:00
parent 334f8d4bf8
commit e10bcd5b49
4 changed files with 76 additions and 4 deletions

View File

@ -2,7 +2,11 @@ from django.contrib import admin
from django.contrib.humanize.templatetags.humanize import naturaltime from django.contrib.humanize.templatetags.humanize import naturaltime
from django.utils.html import format_html from django.utils.html import format_html
from django_object_actions import DjangoObjectActions, action from django_object_actions import (
DjangoObjectActions,
action,
takes_instance_or_queryset,
)
from django_q.models import Task from django_q.models import Task
from django_q.tasks import async_task from django_q.tasks import async_task
@ -15,7 +19,10 @@ from .models import (
Member, Member,
Transaction, Transaction,
) )
from .tasks.scrape import scrape_membershipworks from .tasks.scrape import (
scrape_event_details,
scrape_membershipworks,
)
class ReadOnlyAdmin(admin.ModelAdmin): class ReadOnlyAdmin(admin.ModelAdmin):
@ -104,7 +111,7 @@ class EventInstructorAdmin(admin.ModelAdmin):
@admin.register(EventExt) @admin.register(EventExt)
class EventAdmin(admin.ModelAdmin): class EventAdmin(DjangoObjectActions, admin.ModelAdmin):
inlines = [EventMeetingTimeInline] inlines = [EventMeetingTimeInline]
list_display = [ list_display = [
"title", "title",
@ -123,8 +130,10 @@ class EventAdmin(admin.ModelAdmin):
show_facets = admin.ShowFacets.ALWAYS show_facets = admin.ShowFacets.ALWAYS
search_fields = ["eid", "title", "url"] search_fields = ["eid", "title", "url"]
date_hierarchy = "start" date_hierarchy = "start"
exclude = ["url"] exclude = ["url", "details"]
autocomplete_fields = ["instructor"] autocomplete_fields = ["instructor"]
change_actions = ["fetch_details"]
actions = ["fetch_details"]
@property @property
def readonly_fields(self): def readonly_fields(self):
@ -137,6 +146,7 @@ class EventAdmin(admin.ModelAdmin):
else: else:
fields.append(field.name) fields.append(field.name)
fields.insert(fields.index("end") + 1, "duration") fields.insert(fields.index("end") + 1, "duration")
fields.append("details_timestamp")
return fields return fields
@admin.display(ordering="duration") @admin.display(ordering="duration")
@ -150,6 +160,10 @@ class EventAdmin(admin.ModelAdmin):
obj.url, obj.url,
) )
@takes_instance_or_queryset
def fetch_details(self, request, queryset):
scrape_event_details(queryset)
def has_add_permission(self, request, obj=None): def has_add_permission(self, request, obj=None):
return False return False

View File

@ -0,0 +1,29 @@
# Generated by Django 5.0.1 on 2024-01-29 19:19
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("membershipworks", "0010_alter_eventext_options"),
]
operations = [
migrations.AddField(
model_name="eventext",
name="details",
field=models.JSONField(blank=True, null=True),
),
migrations.AddField(
model_name="eventext",
name="details_timestamp",
field=models.GeneratedField(
db_persist=False,
expression=models.Func(
models.Func(models.F("details___ts"), function="FROM_UNIXTIME"),
template="CONVERT_TZ(%(expressions)s, @@session.time_zone, 'UTC')",
),
output_field=models.DateTimeField(),
),
),
]

View File

@ -9,6 +9,7 @@ from django.db.models import (
Exists, Exists,
ExpressionWrapper, ExpressionWrapper,
F, F,
Func,
OuterRef, OuterRef,
Q, Q,
Subquery, Subquery,
@ -493,6 +494,15 @@ class EventExt(Event):
instructor_flat_rate = models.DecimalField( instructor_flat_rate = models.DecimalField(
max_digits=13, decimal_places=4, default=0 max_digits=13, decimal_places=4, default=0
) )
details = models.JSONField(null=True, blank=True)
details_timestamp = models.GeneratedField(
expression=Func(
Func(F("details___ts"), function="FROM_UNIXTIME"),
template="CONVERT_TZ(%(expressions)s, @@session.time_zone, 'UTC')",
),
output_field=models.DateTimeField(),
db_persist=False,
)
class Meta: class Meta:
verbose_name = "event" verbose_name = "event"

View File

@ -3,6 +3,7 @@ from datetime import datetime, timedelta
from django.conf import settings from django.conf import settings
from django.db import transaction from django.db import transaction
from django.db.models import QuerySet
from membershipworks.membershipworks_api import FieldType, MembershipWorks from membershipworks.membershipworks_api import FieldType, MembershipWorks
from membershipworks.models import ( from membershipworks.models import (
@ -99,6 +100,17 @@ def scrape_membershipworks(*args, **options):
scrape_transactions(membershipworks) scrape_transactions(membershipworks)
def scrape_event_details(queryset: QuerySet[EventExt]):
membershipworks = MembershipWorks()
membershipworks.login(
settings.MEMBERSHIPWORKS_USERNAME, settings.MEMBERSHIPWORKS_PASSWORD
)
for event in queryset:
event.details = membershipworks.get_event_by_eid(event.eid)
event.save()
def scrape_events(): def scrape_events():
membershipworks = MembershipWorks() membershipworks = MembershipWorks()
membershipworks.login( membershipworks.login(
@ -150,3 +162,10 @@ def scrape_events():
# if there is exactly one meeting time, it should match the event start/end # if there is exactly one meeting time, it should match the event start/end
elif meeting_times_count == 1: elif meeting_times_count == 1:
event_ext.meeting_times.update(start=event_ext.start, end=event_ext.end) event_ext.meeting_times.update(start=event_ext.start, end=event_ext.end)
# event has no details, or last retrieval was before the event happened
if event_ext.details is None or event_ext.details_timestamp < (
event_ext.end or event_ext.start
):
event_ext.details = membershipworks.get_event_by_eid(event.eid)
event_ext.save()