Merge branch 'add_timeslots' into 'master'
Add timeslots See merge request bestuur/kers!10
This commit is contained in:
commit
f3347c7ff0
9 changed files with 212 additions and 62 deletions
|
@ -1,7 +1,15 @@
|
||||||
from django.contrib import admin
|
|
||||||
from django import forms
|
from django import forms
|
||||||
|
from django.contrib import admin
|
||||||
|
|
||||||
from .models import Event, EventRegistration
|
from .models import Event, EventRegistration, TimeSlot
|
||||||
|
|
||||||
|
|
||||||
|
class TimeSlotFormSet(forms.BaseInlineFormSet):
|
||||||
|
def __init__(self, *args, **kwargs):
|
||||||
|
kwargs["initial"] = [
|
||||||
|
{"time": TimeSlot.EVENING}
|
||||||
|
]
|
||||||
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
class RegistrationFormSet(forms.BaseInlineFormSet):
|
class RegistrationFormSet(forms.BaseInlineFormSet):
|
||||||
|
@ -12,12 +20,25 @@ class RegistrationFormSet(forms.BaseInlineFormSet):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
class TimeSlotInline(admin.StackedInline):
|
||||||
|
model = TimeSlot
|
||||||
|
extra = 1
|
||||||
|
formset = TimeSlotFormSet
|
||||||
|
|
||||||
|
|
||||||
class RegistrationInline(admin.TabularInline):
|
class RegistrationInline(admin.TabularInline):
|
||||||
model = EventRegistration
|
model = EventRegistration
|
||||||
extra = 1
|
extra = 1
|
||||||
formset = RegistrationFormSet
|
formset = RegistrationFormSet
|
||||||
|
#
|
||||||
|
|
||||||
class EventAdmin(admin.ModelAdmin):
|
class EventAdmin(admin.ModelAdmin):
|
||||||
|
inlines = [TimeSlotInline]
|
||||||
|
|
||||||
|
|
||||||
|
class TimeSlotAdmin(admin.ModelAdmin):
|
||||||
inlines = [RegistrationInline]
|
inlines = [RegistrationInline]
|
||||||
|
|
||||||
|
|
||||||
admin.site.register(Event, EventAdmin)
|
admin.site.register(Event, EventAdmin)
|
||||||
|
admin.site.register(TimeSlot, TimeSlotAdmin)
|
||||||
|
|
104
events/migrations/0002_auto_20200804_0004.py
Normal file
104
events/migrations/0002_auto_20200804_0004.py
Normal file
|
@ -0,0 +1,104 @@
|
||||||
|
# Generated by Django 3.0.8 on 2020-08-03 22:04
|
||||||
|
|
||||||
|
import django.db.models.deletion
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
def forwards_migrate_data(apps, schema_editor):
|
||||||
|
EventRegistration = apps.get_model('events', 'EventRegistration')
|
||||||
|
TimeSlot = apps.get_model('events', 'TimeSlot')
|
||||||
|
Event = apps.get_model('events', 'Event')
|
||||||
|
|
||||||
|
grouped = {}
|
||||||
|
for event in Event.objects.all():
|
||||||
|
if event.note in grouped:
|
||||||
|
grouped[event.note].append(event)
|
||||||
|
else:
|
||||||
|
grouped[event.note] = [event]
|
||||||
|
|
||||||
|
for note, events in grouped.items():
|
||||||
|
event = events[0]
|
||||||
|
new_event = Event(date=event.date, capacity=event.capacity, note=event.note,
|
||||||
|
responsible_person=event.responsible_person)
|
||||||
|
new_event.save()
|
||||||
|
|
||||||
|
# Create a corresponding timeslot for every event with the same description
|
||||||
|
for event in events:
|
||||||
|
new_timeslot = TimeSlot(time=event.time, event=new_event)
|
||||||
|
new_timeslot.save()
|
||||||
|
|
||||||
|
# Connect registrations to the timeslots instead of the events.
|
||||||
|
for registration in EventRegistration.objects.filter(event_id=event.id).all():
|
||||||
|
registration.time_slot = new_timeslot
|
||||||
|
registration.save()
|
||||||
|
|
||||||
|
def forwards_remove_old_data(apps, schema_editor):
|
||||||
|
# Remove old events here so the cascade doesnt remove the registrations.
|
||||||
|
EventRegistration = apps.get_model('events', 'EventRegistration')
|
||||||
|
TimeSlot = apps.get_model('events', 'TimeSlot')
|
||||||
|
Event = apps.get_model('events', 'Event')
|
||||||
|
|
||||||
|
# Remove all events without timeslot
|
||||||
|
for event in Event.objects.exclude(id__in=TimeSlot.objects.values('event_id').all()):
|
||||||
|
event.delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
dependencies = [
|
||||||
|
('events', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='TimeSlot',
|
||||||
|
fields=[
|
||||||
|
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('time', models.IntegerField(choices=[(0, 'voormiddag'), (1, 'namiddag'), (2, 'avond')], default=0)),
|
||||||
|
('event', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.Event'))
|
||||||
|
],
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='eventregistration',
|
||||||
|
name='time_slot',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='events.TimeSlot', null=True),
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='timeslot',
|
||||||
|
constraint=models.UniqueConstraint(fields=('time', 'event'), name='no_duplicate_timeslots'),
|
||||||
|
),
|
||||||
|
migrations.AddConstraint(
|
||||||
|
model_name='eventregistration',
|
||||||
|
constraint=models.UniqueConstraint(fields=('time_slot', 'user'), name='register_only_once_per_timeslot'),
|
||||||
|
),
|
||||||
|
|
||||||
|
migrations.RunPython(forwards_migrate_data, hints={'target_db': 'default'}),
|
||||||
|
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='event',
|
||||||
|
name='time',
|
||||||
|
),
|
||||||
|
|
||||||
|
# Django bugs out when removing constraints that are made on fk's. We manualy fix it here 2 'AlterField' calls
|
||||||
|
# https://code.djangoproject.com/ticket/31335
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='eventregistration',
|
||||||
|
name='event',
|
||||||
|
field=models.ForeignKey(
|
||||||
|
db_constraint=False,
|
||||||
|
on_delete=django.db.models.deletion.CASCADE,
|
||||||
|
related_name='eventregistrations',
|
||||||
|
related_query_name='eventregistration',
|
||||||
|
to='events.Event',
|
||||||
|
),
|
||||||
|
),
|
||||||
|
migrations.RemoveConstraint(
|
||||||
|
model_name='eventregistration',
|
||||||
|
name='register_only_once',
|
||||||
|
),
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='eventregistration',
|
||||||
|
name='event',
|
||||||
|
),
|
||||||
|
|
||||||
|
migrations.RunPython(forwards_remove_old_data, hints={'target_db': 'default'}),
|
||||||
|
]
|
|
@ -4,23 +4,37 @@ from users.models import CustomUser
|
||||||
|
|
||||||
|
|
||||||
class Event(models.Model):
|
class Event(models.Model):
|
||||||
|
date = models.DateField()
|
||||||
|
capacity = models.IntegerField(default=6)
|
||||||
|
note = models.TextField(max_length=1024, default="")
|
||||||
|
responsible_person = models.ForeignKey(CustomUser, null=True, on_delete=models.SET_NULL)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return f"{self.date}"
|
||||||
|
|
||||||
|
|
||||||
|
class TimeSlot(models.Model):
|
||||||
MORNING, AFTERNOON, EVENING = range(3)
|
MORNING, AFTERNOON, EVENING = range(3)
|
||||||
TIME_SLOTS = {
|
TIME_SLOTS = {
|
||||||
MORNING: "voormiddag",
|
MORNING: "voormiddag",
|
||||||
AFTERNOON: "namiddag",
|
AFTERNOON: "namiddag",
|
||||||
EVENING: "avond",
|
EVENING: "avond",
|
||||||
}
|
}
|
||||||
date = models.DateField()
|
|
||||||
time = models.IntegerField(choices=TIME_SLOTS.items(), default=MORNING)
|
time = models.IntegerField(choices=TIME_SLOTS.items(), default=MORNING)
|
||||||
capacity = models.IntegerField(default=6)
|
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
||||||
note = models.TextField(max_length=1024, default="")
|
|
||||||
responsible_person = models.ForeignKey(CustomUser, null=True, on_delete=models.SET_NULL)
|
class Meta:
|
||||||
|
constraints = [
|
||||||
|
models.UniqueConstraint(
|
||||||
|
fields=["time", "event"], name="no_duplicate_timeslots"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"{self.date} {self.TIME_SLOTS[self.time]}"
|
return f"{self.event.date} {self.TIME_SLOTS[self.time]}"
|
||||||
|
|
||||||
def time_str(self):
|
def time_str(self):
|
||||||
return Event.TIME_SLOTS[self.time]
|
return TimeSlot.TIME_SLOTS[self.time]
|
||||||
|
|
||||||
def count_with_status(self, status):
|
def count_with_status(self, status):
|
||||||
return self.eventregistration_set.filter(state=status).count()
|
return self.eventregistration_set.filter(state=status).count()
|
||||||
|
@ -38,7 +52,7 @@ class Event(models.Model):
|
||||||
registrations = self.eventregistration_set.filter(user=user).all()
|
registrations = self.eventregistration_set.filter(user=user).all()
|
||||||
if not registrations:
|
if not registrations:
|
||||||
return None
|
return None
|
||||||
assert len(registrations) == 1, "Registrations should be unique per user and event"
|
assert len(registrations) == 1, "Registrations should be unique per user and timeslot"
|
||||||
return registrations[0]
|
return registrations[0]
|
||||||
|
|
||||||
|
|
||||||
|
@ -49,19 +63,19 @@ class EventRegistration(models.Model):
|
||||||
INTERESTED: "op wachtlijst",
|
INTERESTED: "op wachtlijst",
|
||||||
ADMITTED: "bevestigd",
|
ADMITTED: "bevestigd",
|
||||||
}
|
}
|
||||||
event = models.ForeignKey(Event, on_delete=models.CASCADE)
|
time_slot = models.ForeignKey(TimeSlot, on_delete=models.CASCADE)
|
||||||
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
|
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
|
||||||
state = models.CharField(max_length=1, choices=REGISTRATION_STATE.items())
|
state = models.CharField(max_length=1, choices=REGISTRATION_STATE.items())
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
constraints = [
|
constraints = [
|
||||||
models.UniqueConstraint(
|
models.UniqueConstraint(
|
||||||
fields=["event", "user"], name="register_only_once"
|
fields=["time_slot", "user"], name="register_only_once_per_timeslot"
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f"Reservation[{self.user.username}:{self.event.date}:{self.state}]"
|
return f"Reservation[{self.user.username}:{self.time_slot.event.date}:{self.state}]"
|
||||||
|
|
||||||
def state_str(self):
|
def state_str(self):
|
||||||
return EventRegistration.REGISTRATION_STATE[self.state]
|
return EventRegistration.REGISTRATION_STATE[self.state]
|
||||||
|
|
|
@ -2,39 +2,42 @@ from datetime import date, timedelta
|
||||||
from typing import List
|
from typing import List
|
||||||
|
|
||||||
from KeRS.celery import app
|
from KeRS.celery import app
|
||||||
from events.models import Event, EventRegistration
|
from events.models import Event, EventRegistration, TimeSlot
|
||||||
from users.models import CustomUser
|
from users.models import CustomUser
|
||||||
|
|
||||||
|
|
||||||
def calc_score(user: CustomUser):
|
def calc_score(user: CustomUser):
|
||||||
registrations_last_month = EventRegistration.objects.filter(user_id=user.id,
|
registrations_last_month: List[EventRegistration] = EventRegistration.objects \
|
||||||
event__date__gt=date.today() - timedelta(days=30),
|
.filter(user_id=user.id,
|
||||||
event__date__lte=date.today(),
|
time_slot__event__date__gt=date.today() - timedelta(days=30),
|
||||||
state=EventRegistration.ADMITTED)
|
time_slot__event__date__lte=date.today(),
|
||||||
|
state=EventRegistration.ADMITTED)
|
||||||
score = 0
|
score = 0
|
||||||
for r in registrations_last_month:
|
for r in registrations_last_month:
|
||||||
days_ago = (date.today() - r.event.date).days
|
days_ago = (date.today() - r.time_slot.event.date).days
|
||||||
score += 1 / (days_ago + 1)
|
score += 1 / (days_ago + 1)
|
||||||
|
|
||||||
return score
|
return score
|
||||||
|
|
||||||
|
|
||||||
@app.task(bind=True)
|
@app.task(bind=True)
|
||||||
def assign_reservations(self):
|
def assign_reservations(self, dry_run=False):
|
||||||
"""
|
"""
|
||||||
Check if there are any events the next day.
|
Check 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.
|
If so, calculate the current score for every interested user and assign the ones with the lowest scores.
|
||||||
:param self:
|
:param dry_run: If this is set to true, no persistent changes will be made.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
print("Assigning reservations")
|
print("Assigning reservations")
|
||||||
print("======================")
|
print("======================")
|
||||||
# Get all events of tomorrow
|
# Get all events of tomorrow
|
||||||
events = Event.objects.filter(date=date.today() + timedelta(days=1))
|
events: List[Event] = Event.objects.filter(date=date.today() + timedelta(days=1))
|
||||||
|
# Get all timeslots of tomorrow
|
||||||
|
timeslots: List[TimeSlot] = TimeSlot.objects.filter(event_id__in=map(lambda e: e.id, events))
|
||||||
|
|
||||||
# Reservations
|
# Reservations
|
||||||
registrations: List[EventRegistration] = EventRegistration.objects.filter(
|
registrations: List[EventRegistration] = EventRegistration.objects.filter(
|
||||||
event_id__in=map(lambda event: event.id, events),
|
time_slot_id__in=map(lambda timeslot: timeslot.id, timeslots),
|
||||||
state=EventRegistration.INTERESTED)
|
state=EventRegistration.INTERESTED)
|
||||||
if len(registrations) == 0:
|
if len(registrations) == 0:
|
||||||
print("NO REGISTRATIONS?")
|
print("NO REGISTRATIONS?")
|
||||||
|
@ -46,17 +49,18 @@ def assign_reservations(self):
|
||||||
print(f"Scores: {scores}")
|
print(f"Scores: {scores}")
|
||||||
print(f"Queue: {queue}")
|
print(f"Queue: {queue}")
|
||||||
|
|
||||||
for event in events:
|
for timeslot in timeslots:
|
||||||
print(f"EVENT: {event.date} - {event.capacity}")
|
print(f"EVENT: {timeslot.event.date} - {timeslot.event.capacity}")
|
||||||
event_registrations = list(filter(lambda r: r.event == event, registrations))
|
timeslot_registrations = list(filter(lambda r: r.time_slot == timeslot, registrations))
|
||||||
event_users = set(map(lambda r: r.user, event_registrations))
|
timeslot_users = set(map(lambda r: r.user, timeslot_registrations))
|
||||||
event_queue = list(filter(lambda element: element[0] in event_users, queue))
|
timeslot_queue = list(filter(lambda element: element[0] in timeslot_users, queue))
|
||||||
|
|
||||||
for user in event_queue[0:event.capacity]:
|
for user in timeslot_queue[0:timeslot.event.capacity]:
|
||||||
print(f"Selected {user[0]}")
|
print(f"Selected {user[0]}")
|
||||||
r = EventRegistration.objects.get(
|
r = EventRegistration.objects.get(
|
||||||
event_id=event.id,
|
time_slot_id=timeslot.id,
|
||||||
user_id=user[0].id
|
user_id=user[0].id
|
||||||
)
|
)
|
||||||
r.state = EventRegistration.ADMITTED
|
r.state = EventRegistration.ADMITTED
|
||||||
r.save()
|
if not dry_run:
|
||||||
|
r.save()
|
||||||
|
|
|
@ -9,19 +9,25 @@
|
||||||
<ul>
|
<ul>
|
||||||
{% for x in events %}
|
{% for x in events %}
|
||||||
<li id="{{x.event.id}}">
|
<li id="{{x.event.id}}">
|
||||||
<h3>{{ x.event.date|date:"d F Y" }}, {{ x.event.time_str }}
|
<h3>{{ x.event.date|date:"d F Y" }}
|
||||||
{% if user.is_staff %}
|
{% if user.is_staff %}
|
||||||
<a href="/admin/events/event/{{ x.event.id }}/change/">Modificeren</a>
|
<a href="/admin/events/event/{{ x.event.id }}/change/">Modificeren</a>
|
||||||
{% endif %}</h3>
|
{% endif %}
|
||||||
|
</h3>
|
||||||
|
|
||||||
<p>Verantwoordelijke: {{ x.event.responsible_person.username }}</p>
|
<p>Verantwoordelijke: {{ x.event.responsible_person.username }}</p>
|
||||||
|
|
||||||
{% if x.event.note %}
|
{% if x.event.note %}
|
||||||
<p>Beschrijving: {{ x.event.note }}</p>
|
<p>Beschrijving: {{ x.event.note }}</p>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
{% include "events/registrations.html" with event=x.event my_registration=x.my_registration %}
|
<ul>
|
||||||
</li>
|
{% for y in x.timeslots %}
|
||||||
|
{% include "events/registrations.html" with event=x.event timeslot=y.timeslot my_registration=y.my_registration %}
|
||||||
|
{% endfor %}
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
</li>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</ul>
|
</ul>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
|
@ -1,16 +1,16 @@
|
||||||
{% if not my_registration %}
|
{% if not my_registration %}
|
||||||
<form action="{% url 'events:register' event.id %}" method="post">
|
<form action="{% url 'events:register' timeslot.id %}" method="post">
|
||||||
{% else %}
|
{% else %}
|
||||||
<form action="{% url 'events:deregister' event.id %}" method="post">
|
<form action="{% url 'events:deregister' timeslot.id %}" method="post">
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% csrf_token %}
|
{% csrf_token %}
|
||||||
<p>{{ event.count_admitted }}/{{ event.capacity }} bevestigd{% if event.count_interested %},
|
<p>{{ timeslot.time_str }}, {{ timeslot.count_admitted }}/{{ event.capacity }} bevestigd{% if timeslot.count_interested %},
|
||||||
{{ event.count_interested }} op wachtlijst
|
{{ event.count_interested }} op wachtlijst
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{% if not user.is_authenticated %}
|
{% if not user.is_authenticated %}
|
||||||
<p>Je moet inloggen voor je je kan inschrijven.</p>
|
<p><i>Je moet inloggen voor je je kan inschrijven.</i></p>
|
||||||
{% elif not user.has_ugent_info %}
|
{% elif not user.has_ugent_info %}
|
||||||
<p class="text--important">Je moet je studentennummer en echte naam invullen in je profiel voor je je kan inschrijven.</p>
|
<p class="text--important">Je moet je studentennummer en echte naam invullen in je profiel voor je je kan inschrijven.</p>
|
||||||
{% elif not my_registration %}
|
{% elif not my_registration %}
|
||||||
|
|
|
@ -5,6 +5,6 @@ from . import views
|
||||||
app_name = "events"
|
app_name = "events"
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path("", views.index, name="index"),
|
path("", views.index, name="index"),
|
||||||
path("<int:event_id>/register", views.register, name="register"),
|
path("<int:timeslot_id>/register", views.register, name="register"),
|
||||||
path("<int:event_id>/deregister", views.deregister, name="deregister"),
|
path("<int:timeslot_id>/deregister", views.deregister, name="deregister"),
|
||||||
]
|
]
|
||||||
|
|
|
@ -1,46 +1,44 @@
|
||||||
from django.http import HttpResponse, HttpResponseRedirect
|
from django.http import HttpResponse, HttpResponseRedirect
|
||||||
from django.shortcuts import render, get_object_or_404
|
from django.shortcuts import render, get_object_or_404
|
||||||
|
|
||||||
from django.utils import timezone
|
|
||||||
from django.urls import reverse
|
from django.urls import reverse
|
||||||
import datetime
|
from django.utils import timezone
|
||||||
|
|
||||||
from .models import Event, EventRegistration, CustomUser
|
from .models import Event, EventRegistration, TimeSlot
|
||||||
|
|
||||||
from events.tasks import assign_reservations
|
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
events = Event.objects.filter(date__gte=timezone.now().date()).order_by("date")[:20]
|
events = Event.objects.filter(date__gte=timezone.now().date()).order_by("date")[:20]
|
||||||
|
|
||||||
events_data = [
|
events_data = [
|
||||||
{ "event": event, "my_registration": event.registration_of(request.user) }
|
{"event": event, "timeslots": [{"timeslot": timeslot,
|
||||||
|
"my_registration": timeslot.registration_of(request.user)}
|
||||||
|
for timeslot in TimeSlot.objects.filter(event_id=event.id)]}
|
||||||
for event in events
|
for event in events
|
||||||
]
|
]
|
||||||
return render(request, "events/index.html", {"events": events_data})
|
return render(request, "events/index.html", {"events": events_data})
|
||||||
|
|
||||||
|
|
||||||
def register(request, event_id):
|
def register(request, timeslot_id):
|
||||||
if request.method != "POST":
|
if request.method != "POST":
|
||||||
return HttpResponse(status_code=405)
|
return HttpResponse(status_code=405)
|
||||||
|
|
||||||
if not request.user.has_ugent_info:
|
if not request.user.has_ugent_info:
|
||||||
raise ValueError("User has missing UGent info missing")
|
raise ValueError("User has missing UGent info missing")
|
||||||
|
|
||||||
event = get_object_or_404(Event, id=event_id)
|
timeslot = get_object_or_404(TimeSlot, id=timeslot_id)
|
||||||
|
|
||||||
event.eventregistration_set.create(
|
timeslot.eventregistration_set.create(
|
||||||
state=EventRegistration.INTERESTED,
|
state=EventRegistration.INTERESTED,
|
||||||
event=event,
|
time_slot=timeslot,
|
||||||
user=request.user,
|
user=request.user,
|
||||||
)
|
)
|
||||||
return HttpResponseRedirect(reverse("events:index") + f"#{event.id}")
|
return HttpResponseRedirect(reverse("events:index") + f"#{timeslot.id}")
|
||||||
|
|
||||||
|
|
||||||
def deregister(request, event_id):
|
def deregister(request, timeslot_id):
|
||||||
if request.method != "POST":
|
if request.method != "POST":
|
||||||
return HttpResponse(status_code=405)
|
return HttpResponse(status_code=405)
|
||||||
|
|
||||||
registration = get_object_or_404(EventRegistration, event=event_id, user=request.user)
|
registration = get_object_or_404(EventRegistration, time_slot=timeslot_id, user=request.user)
|
||||||
registration.delete()
|
registration.delete()
|
||||||
return HttpResponseRedirect(reverse("events:index") + f"#{event_id}")
|
return HttpResponseRedirect(reverse("events:index") + f"#{timeslot_id}")
|
||||||
|
|
|
@ -23,6 +23,9 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.username
|
return self.username
|
||||||
|
|
||||||
|
def __repr__(self):
|
||||||
|
return "<User: {}>".format(self.username)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def has_ugent_info(self):
|
def has_ugent_info(self):
|
||||||
return self.real_name and self.student_number
|
return self.real_name and self.student_number
|
||||||
|
|
Loading…
Reference in a new issue