Compare commits

...

2 Commits

Author SHA1 Message Date
2d16029ed7 membershipworks: Switch EventExt.details_timestamp to an annotation
All checks were successful
Ruff / ruff (push) Successful in 22s
to avoid issues with saving new objects with GeneratedFields when the
pk is set
2024-02-01 11:14:49 -05:00
8961542d14 Use decorator to keep track of group names for django-q2 tasks 2024-02-01 11:10:22 -05:00
8 changed files with 36 additions and 15 deletions

View File

@ -9,3 +9,13 @@ def ensure_scheduled(name: str, func, **kwargs):
**kwargs, **kwargs,
}, },
) )
def q_task_group(task_group: str):
"""Decorator to more cleanly add `q_task_group` to a function"""
def inner(func):
func.q_task_group = task_group
return func
return inner

View File

@ -10,7 +10,7 @@ def post_migrate_callback(sender, **kwargs):
from .tasks.scrapehidevents import q_getMessagesAllDoors from .tasks.scrapehidevents import q_getMessagesAllDoors
ensure_scheduled( ensure_scheduled(
"Fetch HID Events", q_getMessagesAllDoors.q_task_group,
q_getMessagesAllDoors, q_getMessagesAllDoors,
schedule_type=Schedule.MINUTES, schedule_type=Schedule.MINUTES,
minutes=15, minutes=15,

View File

@ -5,6 +5,7 @@ from django.utils import timezone
from django_q.tasks import async_task from django_q.tasks import async_task
from cmsmanage.django_q2_helper import q_task_group
from doorcontrol.models import Door, HIDEvent from doorcontrol.models import Door, HIDEvent
@ -34,6 +35,7 @@ def getMessages(door: Door):
} }
@q_task_group("Fetch HID Events")
def q_getMessagesAllDoors(): def q_getMessagesAllDoors():
# TODO: this should probably use async_iter # TODO: this should probably use async_iter
for door in Door.objects.all(): for door in Door.objects.all():

View File

@ -45,7 +45,7 @@ class BaseMembershipWorksAdmin(DjangoObjectActions, ReadOnlyAdmin):
if tool_name == "refresh_membershipworks_data": if tool_name == "refresh_membershipworks_data":
try: try:
last_run_time = naturaltime( last_run_time = naturaltime(
Task.objects.filter(group="Scrape Data from MembershipWorks") Task.objects.filter(group=scrape_membershipworks.q_task_group)
.values_list("started", flat=True) .values_list("started", flat=True)
.latest("started") .latest("started")
) )
@ -56,7 +56,7 @@ class BaseMembershipWorksAdmin(DjangoObjectActions, ReadOnlyAdmin):
@action @action
def refresh_membershipworks_data(self, request, obj): def refresh_membershipworks_data(self, request, obj):
async_task(scrape_membershipworks, group="Scrape Data from MembershipWorks") async_task(scrape_membershipworks, group=scrape_membershipworks.q_task_group)
self.message_user( self.message_user(
request, request,
"Queued refresh, please wait a few seconds/minutes then refresh the page", "Queued refresh, please wait a few seconds/minutes then refresh the page",
@ -146,7 +146,7 @@ class EventAdmin(DjangoObjectActions, 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") fields.append("_details_timestamp")
return fields return fields
@admin.display(ordering="duration") @admin.display(ordering="duration")
@ -160,6 +160,10 @@ class EventAdmin(DjangoObjectActions, admin.ModelAdmin):
obj.url, obj.url,
) )
@admin.display(description="Last details fetch")
def _details_timestamp(self, obj):
return naturaltime(obj.details_timestamp)
@takes_instance_or_queryset @takes_instance_or_queryset
def fetch_details(self, request, queryset): def fetch_details(self, request, queryset):
scrape_event_details(queryset) scrape_event_details(queryset)

View File

@ -11,19 +11,19 @@ def post_migrate_callback(sender, **kwargs):
from .tasks.ucsAccounts import sync_accounts from .tasks.ucsAccounts import sync_accounts
ensure_scheduled( ensure_scheduled(
"Scrape MembershipWorks Data", scrape_membershipworks.q_task_group,
scrape_membershipworks, scrape_membershipworks,
schedule_type=Schedule.HOURLY, schedule_type=Schedule.HOURLY,
) )
ensure_scheduled( ensure_scheduled(
"Scrape MembershipWorks Events", scrape_events.q_task_group,
scrape_events, scrape_events,
schedule_type=Schedule.HOURLY, schedule_type=Schedule.HOURLY,
) )
ensure_scheduled( ensure_scheduled(
"Sync UCS Accounts", sync_accounts.q_task_group,
sync_accounts, sync_accounts,
schedule_type=Schedule.MINUTES, schedule_type=Schedule.MINUTES,
minutes=15, minutes=15,

View File

@ -511,6 +511,14 @@ class EventExtManager(models.Manager["EventExt"]):
* F("count"), * F("count"),
models.DurationField(), models.DurationField(),
), ),
# TODO: this could be a GeneratedField, but that
# currently breaks saving when the primary key is
# provided (Django 5.0.1)
details_timestamp=Func(
Func(F("details___ts"), function="FROM_UNIXTIME"),
template="CONVERT_TZ(%(expressions)s, @@session.time_zone, 'UTC')",
output_field=models.DateTimeField(),
),
) )
) )
@ -534,14 +542,6 @@ class EventExt(Event):
max_digits=13, decimal_places=4, default=0 max_digits=13, decimal_places=4, default=0
) )
details = models.JSONField(null=True, blank=True) 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

@ -5,6 +5,7 @@ from django.conf import settings
from django.db import transaction from django.db import transaction
from django.db.models import QuerySet from django.db.models import QuerySet
from cmsmanage.django_q2_helper import q_task_group
from membershipworks.membershipworks_api import FieldType, MembershipWorks from membershipworks.membershipworks_api import FieldType, MembershipWorks
from membershipworks.models import ( from membershipworks.models import (
Event, Event,
@ -89,6 +90,7 @@ def scrape_transactions(membershipworks: MembershipWorks):
Transaction.from_api_dict(csv_transaction).save() Transaction.from_api_dict(csv_transaction).save()
@q_task_group("Scrape MembershipWorks Data")
@transaction.atomic @transaction.atomic
def scrape_membershipworks(*args, **options): def scrape_membershipworks(*args, **options):
membershipworks = MembershipWorks() membershipworks = MembershipWorks()
@ -111,6 +113,7 @@ def scrape_event_details(queryset: QuerySet[EventExt]):
event.save() event.save()
@q_task_group("Scrape MembershipWorks Events")
def scrape_events(): def scrape_events():
membershipworks = MembershipWorks() membershipworks = MembershipWorks()
membershipworks.login( membershipworks.login(

View File

@ -8,6 +8,7 @@ from django.conf import settings
from udm_rest_client.exceptions import NoObject, UdmError from udm_rest_client.exceptions import NoObject, UdmError
from udm_rest_client.udm import UDM from udm_rest_client.udm import UDM
from cmsmanage.django_q2_helper import q_task_group
from membershipworks.models import Flag, Member from membershipworks.models import Flag, Member
USER_BASE = "cn=users,dc=sawtooth,dc=claremontmakerspace,dc=org" USER_BASE = "cn=users,dc=sawtooth,dc=claremontmakerspace,dc=org"
@ -132,5 +133,6 @@ async def async_accounts():
await sync_member(user_mod, member) await sync_member(user_mod, member)
@q_task_group("Sync UCS Accounts")
def sync_accounts(): def sync_accounts():
asyncio.run(async_accounts()) asyncio.run(async_accounts())