membershipworks: Add storage and scraping for Event details
This commit is contained in:
parent
334f8d4bf8
commit
e10bcd5b49
@ -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
|
||||||
|
|
||||||
|
29
membershipworks/migrations/0011_eventext_details.py
Normal file
29
membershipworks/migrations/0011_eventext_details.py
Normal 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(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
]
|
@ -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"
|
||||||
|
@ -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()
|
||||||
|
Loading…
Reference in New Issue
Block a user