From a708e7c8df7fbf8712de487fac8a17f9ba5dbcee Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 21 Mar 2019 21:55:00 +0100 Subject: [PATCH 1/5] added ects field to attendant model, it will be updated when chekIn --- tickets/migrations/0012_attendant_ects.py | 19 ++++++++++++++ tickets/models.py | 31 ++++++++++++++++++++--- 2 files changed, 47 insertions(+), 3 deletions(-) create mode 100644 tickets/migrations/0012_attendant_ects.py diff --git a/tickets/migrations/0012_attendant_ects.py b/tickets/migrations/0012_attendant_ects.py new file mode 100644 index 00000000..77eeb364 --- /dev/null +++ b/tickets/migrations/0012_attendant_ects.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.7 on 2019-03-21 13:26 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0011_attendant_registered_as_volunteer'), + ] + + operations = [ + migrations.AddField( + model_name='attendant', + name='ects', + field=models.FloatField(default=0.0, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(3.0)]), + ), + ] diff --git a/tickets/models.py b/tickets/models.py index 5f2bf366..78d18fac 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -1,10 +1,11 @@ import hashlib +from django.core.validators import MinValueValidator, MaxValueValidator from django.db import models from django.utils.crypto import get_random_string - -from editions.models import Edition, SessionFormat, Session +from TryIT.settings_global import EDITION_YEAR +from editions.models import Edition, SessionFormat, Session, Track from tickets.functions import secret_key_mail from volunteers.models import VolunteerRole @@ -49,6 +50,9 @@ class Attendant(models.Model): identity = models.CharField(max_length=9, blank=True) phone = models.CharField(max_length=13, blank=True) + # Ects + ects = models.FloatField(default=0.0, validators=[MinValueValidator(0.0), MaxValueValidator(3.0)]) + # Optional for volunteers registered_as_volunteer = models.BooleanField(default=False) active = models.BooleanField(default=False) @@ -103,10 +107,14 @@ def sign(self): sha1 = hashlib.sha1(key.encode('utf-8')) self.signature = sha1.hexdigest() + + def save(self, *args, **kwargs): # sign before save self.secret_key = get_random_string(16) self.sign() + + super(Ticket, self).save(*args, **kwargs) @@ -115,7 +123,7 @@ class CheckIn(models.Model): attendant = models.ForeignKey(Attendant, on_delete=models.PROTECT) session = models.ForeignKey(Session, on_delete=models.PROTECT) - validator = models.ForeignKey(Validator, on_delete=models.PROTECT ) + validator = models.ForeignKey(Validator, on_delete=models.PROTECT) class Meta: unique_together = ('attendant', 'session') @@ -123,6 +131,23 @@ class Meta: def __str__(self): return str(self.time_stamp) + " - " + self.attendant.lastname + " - " + self.session.title + def update_ects(self): + track = Track.objects.filter()[1] # get Principal track, determines talks accounted for ECTS + number_of_sessions = Session.objects \ + .filter(edition__year=EDITION_YEAR) \ + .filter(track=track).count() + maximum_ects = 3.0 if self.attendant.active else 2.0 + ects_by_session = maximum_ects / number_of_sessions + + if self.attendant.ects < maximum_ects: + attendance_ects = self.attendant.ects + ects_by_session # cant reuse self.atteendance.ects, is limited to 3.0 + self.attendant.ects = min(attendance_ects, maximum_ects) + self.attendant.save() + + def save(self, *args, **kwargs): + self.update_ects() + super(CheckIn, self).save(*args, **kwargs) + class School(models.Model): code = models.CharField(max_length=4, primary_key=True) From 537141e00644e672a5153c78761cf15cf8338ce1 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 22 Mar 2019 08:58:39 +0100 Subject: [PATCH 2/5] Decimal field instead FLoatField for saving ects --- tickets/migrations/0013_auto_20190322_0313.py | 19 +++++++++ tickets/models.py | 41 +++++++++++++------ 2 files changed, 48 insertions(+), 12 deletions(-) create mode 100644 tickets/migrations/0013_auto_20190322_0313.py diff --git a/tickets/migrations/0013_auto_20190322_0313.py b/tickets/migrations/0013_auto_20190322_0313.py new file mode 100644 index 00000000..ec0ee2b2 --- /dev/null +++ b/tickets/migrations/0013_auto_20190322_0313.py @@ -0,0 +1,19 @@ +# Generated by Django 2.1.7 on 2019-03-22 03:13 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('tickets', '0012_attendant_ects'), + ] + + operations = [ + migrations.AlterField( + model_name='attendant', + name='ects', + field=models.DecimalField(decimal_places=2, default=0.0, max_digits=3, validators=[django.core.validators.MinValueValidator(0.0), django.core.validators.MaxValueValidator(3.0)]), + ), + ] diff --git a/tickets/models.py b/tickets/models.py index 78d18fac..1b95d8b0 100644 --- a/tickets/models.py +++ b/tickets/models.py @@ -51,7 +51,8 @@ class Attendant(models.Model): phone = models.CharField(max_length=13, blank=True) # Ects - ects = models.FloatField(default=0.0, validators=[MinValueValidator(0.0), MaxValueValidator(3.0)]) + ects = models.DecimalField(default=0.00, validators=[MinValueValidator(0.00), MaxValueValidator(3.00)], + max_digits=3, decimal_places=2) # Optional for volunteers registered_as_volunteer = models.BooleanField(default=False) @@ -68,6 +69,19 @@ class Meta: def __str__(self): return self.name + " " + self.lastname + def __init__(self, *args, **kwargs): + super(Attendant, self).__init__(*args, **kwargs) + self.__original_active_status = self.active + + def update_ects(self): + # check if a volunteer has been active, so will increase ects + if not self.__original_active_status and self.__original_active_status != self.active and self.upm_student: + self.ects += 1 + + def save(self, *args, **kwargs): + self.update_ects() + super(Attendant, self).save(*args, **kwargs) + class TicketType(models.Model): name = models.CharField(max_length=200) @@ -132,17 +146,20 @@ def __str__(self): return str(self.time_stamp) + " - " + self.attendant.lastname + " - " + self.session.title def update_ects(self): - track = Track.objects.filter()[1] # get Principal track, determines talks accounted for ECTS - number_of_sessions = Session.objects \ - .filter(edition__year=EDITION_YEAR) \ - .filter(track=track).count() - maximum_ects = 3.0 if self.attendant.active else 2.0 - ects_by_session = maximum_ects / number_of_sessions - - if self.attendant.ects < maximum_ects: - attendance_ects = self.attendant.ects + ects_by_session # cant reuse self.atteendance.ects, is limited to 3.0 - self.attendant.ects = min(attendance_ects, maximum_ects) - self.attendant.save() + if self.attendant.upm_student: + track = Track.objects.filter()[1] # get Principal track, determines talks accounted for ECTS + number_of_sessions = Session.objects \ + .filter(edition__year=EDITION_YEAR) \ + .filter(track=track).count() + + maximum_ects = 3.0 if self.attendant.active else 2.0 + + ects_by_session = round(maximum_ects / number_of_sessions, 2) + + if self.attendant.ects < maximum_ects: + attendance_ects = self.attendant.ects + ects_by_session # cant reuse self.atteendance.ects, is limited to 3.0 + self.attendant.ects = min(attendance_ects, maximum_ects) + self.attendant.save() def save(self, *args, **kwargs): self.update_ects() From c65c7ea581318e19c5350cbcdf97e88059324328 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 22 Mar 2019 08:59:43 +0100 Subject: [PATCH 3/5] two new transicional or emergency situation menu actions --- tickets/admin.py | 86 ++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 83 insertions(+), 3 deletions(-) diff --git a/tickets/admin.py b/tickets/admin.py index 9c8358cc..05604630 100644 --- a/tickets/admin.py +++ b/tickets/admin.py @@ -1,4 +1,6 @@ from django.contrib import admin +from import_export import resources +from import_export.admin import ImportExportMixin, ImportExportModelAdmin from tickets.models import TicketType, Ticket, CheckIn, Validator, Attendant, Degree, School from volunteers.models import VolunteerRole @@ -26,12 +28,18 @@ class ValidatorAdmin(admin.ModelAdmin): list_display = ('name', 'volunteer') -class AttendantAdmin(admin.ModelAdmin): + + + +class AttendantAdmin(ImportExportModelAdmin, admin.ModelAdmin,): + + import__fields = ('name', 'lastname') + list_display = ('edition', 'name', 'lastname', 'email', 'phone', 'is_student', 'is_student_upm', 'college', 'degree', 'grade', 'identity', "get_rolelist", "android_phone", - "phone", "active" + "phone", "active", 'ects', ) # list_filter = ('college', 'degree', 'grade', 'student', 'upm_student') list_filter = ("rolelist", "registered_as_volunteer", "active", @@ -41,7 +49,9 @@ class AttendantAdmin(admin.ModelAdmin): list_display_links = ["name"] list_editable = ["active"] search_fields = ('name', 'lastname', 'email', 'phone', 'identity', ) - actions = ['convert_to_validator', 'convert_to_assistant'] + actions = ['convert_to_validator', 'convert_to_assistant', 'set_max_ects', 'calculate_ects'] + + def is_student(self, obj): @@ -97,6 +107,73 @@ def convert_to_assistant(self, request, queryset): convert_to_assistant.short_description = "Convert to/delete assistant" + # if for some reason you need to change ects of someone + def set_max_ects(self, request, queryset): + for obj in queryset: + from editions.models import Track + track = Track.objects.filter()[1] # get Principal track, determines talks accounted for ECTS + from editions.models import Session + from TryIT.settings_global import EDITION_YEAR + number_of_sessions = Session.objects \ + .filter(edition__year=EDITION_YEAR) \ + .filter(track=track).count() + + maximum_ects = 3.0 if obj.active else 2.0 + + ects_by_session = 2.0 / number_of_sessions + + needed_talks = abs((maximum_ects - obj.ects) % ects_by_session) + import math + print(math.ceil(needed_talks)) + + for k in range(0, math.ceil(needed_talks)): + + checkin = CheckIn() + import datetime + checkin.time_stamp = datetime.datetime.now() + checkin.attendant = obj + import random + choosing = True + while choosing: + chosen = random.choice(Session.objects.filter(edition__year=EDITION_YEAR,)) + if not CheckIn.objects.all().filter(attendant=obj, session=chosen).exists(): + print("roger") + checkin.session = chosen + checkin.validator = Validator.objects.get(pk=random.choice(range(100, 150))) + choosing = False + try: + checkin.save() + obj.ects = maximum_ects + obj.save() + except: + # Checkin already registered, ignore + pass + + # "migrate" function to fill all ects fields tih it required value + def calculate_ects(self, request, queryset): + for obj in queryset: + if obj.upm_student: + from editions.models import Track + track = Track.objects.filter()[1] # get Principal track, determines talks accounted for ECTS + from editions.models import Session + from TryIT.settings_global import EDITION_YEAR + number_of_sessions = Session.objects \ + .filter(edition__year=EDITION_YEAR) \ + .filter(track=track).count() + + + ects_by_session = 2.0 / number_of_sessions + + ntalks = CheckIn.objects.all().filter(attendant=obj, session__edition=obj.edition).count() + + obj.ects = round(ntalks*ects_by_session, 2) + obj.save() + + + + class Meta: + model = Attendant + class SchoolAdmin(admin.ModelAdmin): list_display = ('code', 'name') @@ -106,6 +183,9 @@ class DegreeAdmin(admin.ModelAdmin): list_display = ('code', 'degree', 'school') + + + admin.site.register(TicketType, TicketTypeAdmin) admin.site.register(Ticket, TicketAdmin) admin.site.register(CheckIn, CheckinAdmin) From 0d389453f8eed090faff007d0ca0c8e2ba419667 Mon Sep 17 00:00:00 2001 From: Daniel Date: Fri, 22 Mar 2019 09:00:42 +0100 Subject: [PATCH 4/5] Adapted serializers to new ticket/models design --- attendance/serializers.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/attendance/serializers.py b/attendance/serializers.py index fd266c59..4e62d543 100644 --- a/attendance/serializers.py +++ b/attendance/serializers.py @@ -33,16 +33,30 @@ def get_ntalks(self, obj): return sessions.count() first_day_of_event = serializers.SerializerMethodField() + def get_first_day_of_event(self, obj): return obj.attendant.edition.start_date - # TODO when volunteers are linked to attendant, finish this - #def is_volunteer(self, obj): - # return True if str(obj.attendant.identity) else False + is_volunteer = serializers.SerializerMethodField() + + def get_is_volunteer(self, obj): + return obj.attendant.active + + total_ects = serializers.SerializerMethodField() + + def get_total_ects(self, obj): + return obj.attendant.ects + + ects_by_talks = serializers.SerializerMethodField() + + def get_ects_by_talks(self, obj): + + ects_by_session = round(2.0 / self.get_ntalks(obj), 2) + return ects_by_session class Meta: model = CheckIn - fields = ('edition', 'ntalks', 'talks', 'first_day_of_event') + fields = ('edition', 'ntalks', 'talks', 'first_day_of_event', 'is_volunteer', 'ects_by_talks', 'total_ects',) class CheckInSerializer(ModelSerializer): From 1f3c33990de7fc68538d786f7d18f3c3aeb9e94e Mon Sep 17 00:00:00 2001 From: Daniel Date: Thu, 9 May 2019 13:08:55 +0200 Subject: [PATCH 5/5] bug fix --- tickets/admin.py | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/tickets/admin.py b/tickets/admin.py index 05604630..6a82b7e9 100644 --- a/tickets/admin.py +++ b/tickets/admin.py @@ -122,9 +122,8 @@ def set_max_ects(self, request, queryset): ects_by_session = 2.0 / number_of_sessions - needed_talks = abs((maximum_ects - obj.ects) % ects_by_session) + needed_talks = maximum_ects - obj.ects % ects_by_session import math - print(math.ceil(needed_talks)) for k in range(0, math.ceil(needed_talks)): @@ -137,7 +136,6 @@ def set_max_ects(self, request, queryset): while choosing: chosen = random.choice(Session.objects.filter(edition__year=EDITION_YEAR,)) if not CheckIn.objects.all().filter(attendant=obj, session=chosen).exists(): - print("roger") checkin.session = chosen checkin.validator = Validator.objects.get(pk=random.choice(range(100, 150))) choosing = False @@ -149,7 +147,7 @@ def set_max_ects(self, request, queryset): # Checkin already registered, ignore pass - # "migrate" function to fill all ects fields tih it required value + # "migrate" function to fill all ects fields to it required value def calculate_ects(self, request, queryset): for obj in queryset: if obj.upm_student: @@ -182,14 +180,10 @@ class SchoolAdmin(admin.ModelAdmin): class DegreeAdmin(admin.ModelAdmin): list_display = ('code', 'degree', 'school') - - - - -admin.site.register(TicketType, TicketTypeAdmin) admin.site.register(Ticket, TicketAdmin) admin.site.register(CheckIn, CheckinAdmin) admin.site.register(Validator, ValidatorAdmin) admin.site.register(Attendant, AttendantAdmin) admin.site.register(School, SchoolAdmin) admin.site.register(Degree, DegreeAdmin) +