Add basic algorithm and celery stuff
This commit is contained in:
parent
6240f53ef5
commit
ba17e04183
10 changed files with 133 additions and 5 deletions
|
@ -0,0 +1,7 @@
|
|||
from __future__ import absolute_import, unicode_literals
|
||||
|
||||
# This will make sure the app is always imported when
|
||||
# Django starts so that shared_task will use this app.
|
||||
from .celery import app as celery_app
|
||||
|
||||
__all__ = ('celery_app',)
|
|
@ -40,6 +40,7 @@ INSTALLED_APPS = [
|
|||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django_celery_beat'
|
||||
] + OWN_APPS
|
||||
|
||||
MIDDLEWARE = [
|
||||
|
|
6
Makefile
Normal file
6
Makefile
Normal file
|
@ -0,0 +1,6 @@
|
|||
celery:
|
||||
celery -A KeRS worker -l info
|
||||
beat:
|
||||
celery -A KeRS beat -l info --scheduler django_celery_beat.schedulers:DatabaseScheduler
|
||||
server:
|
||||
python manage.py runserver
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.0.8 on 2020-07-21 21:36
|
||||
# Generated by Django 3.0.8 on 2020-07-22 00:49
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
@ -26,7 +26,7 @@ class Migration(migrations.Migration):
|
|||
name='EventRegistration',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('shirt_size', models.CharField(choices=[('I', 'Interested'), ('A', 'Admitted'), ('D', 'Denied')], max_length=1)),
|
||||
('state', models.CharField(choices=[('I', 'Interested'), ('A', 'Admitted'), ('D', 'Denied')], max_length=1)),
|
||||
('event_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.Event')),
|
||||
('user_id', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
|
|
23
events/migrations/0002_auto_20200722_0101.py
Normal file
23
events/migrations/0002_auto_20200722_0101.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 3.0.8 on 2020-07-22 01:01
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('events', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RenameField(
|
||||
model_name='eventregistration',
|
||||
old_name='event_id',
|
||||
new_name='event',
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='eventregistration',
|
||||
old_name='user_id',
|
||||
new_name='user',
|
||||
),
|
||||
]
|
|
@ -14,6 +14,9 @@ class EventRegistration(models.Model):
|
|||
('A', 'Admitted'),
|
||||
('D', 'Denied'),
|
||||
)
|
||||
event_id = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||
user_id = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
|
||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
|
||||
state = models.CharField(max_length=1, choices=REGISTRATION_STATE)
|
||||
|
||||
def __str__(self):
|
||||
return f'Reservation[{self.user.username}:{self.event.date}:{self.state}]'
|
||||
|
|
71
events/tasks.py
Normal file
71
events/tasks.py
Normal file
|
@ -0,0 +1,71 @@
|
|||
from datetime import date, timedelta
|
||||
from typing import List
|
||||
|
||||
from KeRS.celery import app
|
||||
from events.models import Event, EventRegistration
|
||||
from users.models import CustomUser
|
||||
|
||||
|
||||
def calc_score(user: CustomUser):
|
||||
registrations_last_month = EventRegistration.objects.all().filter(user_id=user.id,
|
||||
event__date__gt=date.today() - timedelta(days=30),
|
||||
event__date__lte=date.today(),
|
||||
state__exact='A')
|
||||
score = 0
|
||||
for r in registrations_last_month:
|
||||
days_ago = (date.today() - r.event.date.date()).days
|
||||
score += 1 / (days_ago + 1)
|
||||
|
||||
return score
|
||||
|
||||
|
||||
@app.task(bind=True)
|
||||
def assign_reservations(self):
|
||||
"""
|
||||
Chech if there are any events the next day.
|
||||
If so, calculate the current score for every interested user and assign the ones with the lowest scores.
|
||||
:param self:
|
||||
:return:
|
||||
"""
|
||||
print("Assigning reservations")
|
||||
print("======================")
|
||||
# Get all events of tomorrow
|
||||
events = Event.objects.all().filter(date__date=date.today() + timedelta(days=1))
|
||||
|
||||
# Reservations
|
||||
registrations: List[EventRegistration] = EventRegistration.objects.all().filter(
|
||||
event_id__in=map(lambda event: event.id, events),
|
||||
state__exact='I')
|
||||
if len(registrations) == 0:
|
||||
print("NO REGISTRATIONS?")
|
||||
|
||||
# Relevant users
|
||||
users = set(map(lambda r: r.user, registrations))
|
||||
scores = list(map(calc_score, users))
|
||||
queue = sorted(list(zip(users, scores)), key=lambda tup: tup[1])
|
||||
print(f"Scores: {scores}")
|
||||
print(f"Queue: {queue}")
|
||||
|
||||
for event in events:
|
||||
print(f"EVENT: {event.date} - {event.capacity}")
|
||||
event_registrations = list(filter(lambda r: r.event == event, registrations))
|
||||
event_users = set(map(lambda r: r.user, event_registrations))
|
||||
event_queue = list(filter(lambda element: element[0] in event_users, queue))
|
||||
|
||||
for user in event_queue[0:event.capacity]:
|
||||
print(f"Selected {user[0]}")
|
||||
r = EventRegistration.objects.get(
|
||||
event_id=event.id,
|
||||
user_id=user[0].id
|
||||
)
|
||||
r.state = 'A'
|
||||
r.save()
|
||||
|
||||
for user in event_queue[event.capacity:]:
|
||||
print(f"Denied {user[0]}")
|
||||
r = EventRegistration.objects.get(
|
||||
event_id=event.id,
|
||||
user_id=user[0].id
|
||||
)
|
||||
r.state = 'D'
|
||||
r.save()
|
|
@ -4,4 +4,5 @@ from . import views
|
|||
|
||||
urlpatterns = [
|
||||
path('', views.index, name='index'),
|
||||
path('test/', views.view_score_stuff, name='score_stuff'),
|
||||
]
|
||||
|
|
|
@ -3,6 +3,15 @@ from django.shortcuts import render
|
|||
# Create your views here.
|
||||
from django.http import HttpResponse
|
||||
|
||||
from events.tasks import assign_reservations
|
||||
|
||||
|
||||
def index(request):
|
||||
return HttpResponse("Hello, world. You're at the polls index.")
|
||||
return HttpResponse(
|
||||
|
||||
)
|
||||
|
||||
def view_score_stuff(request):
|
||||
return HttpResponse(
|
||||
assign_reservations()
|
||||
)
|
||||
|
|
|
@ -1,6 +1,13 @@
|
|||
amqp==2.6.0
|
||||
asgiref==3.2.10
|
||||
billiard==3.6.3.0
|
||||
celery==4.4.6
|
||||
Django==3.0.8
|
||||
future==0.18.2
|
||||
kombu==4.6.11
|
||||
mysqlclient==2.0.1
|
||||
pytz==2020.1
|
||||
PyYAML==5.3.1
|
||||
redis==3.5.3
|
||||
sqlparse==0.3.1
|
||||
vine==1.3.0
|
||||
|
|
Loading…
Reference in a new issue