diff --git a/django/university/controllers/ExchangeController.py b/django/university/controllers/ExchangeController.py index 020a0ba..0917c6b 100644 --- a/django/university/controllers/ExchangeController.py +++ b/django/university/controllers/ExchangeController.py @@ -3,6 +3,7 @@ from django.core.paginator import Paginator from university.controllers.ClassController import ClassController +from university.controllers.SigarraController import SigarraController from university.exchange.utils import ExchangeStatus, check_class_schedule_overlap, course_unit_by_id from university.models import DirectExchange, DirectExchangeParticipants, ExchangeExpirations, MarketplaceExchange from django.utils import timezone @@ -108,17 +109,10 @@ def getStudentClass(student_schedules, username, courseUnitId): @staticmethod def create_direct_exchange_participants(student_schedules, exchanges, inserted_exchanges, exchange_db_model, auth_user): - if ExchangeController.exchange_overlap(student_schedules, auth_user): - return (ExchangeStatus.CLASSES_OVERLAP, None) - for curr_exchange in exchanges: other_student = curr_exchange["other_student"]["mecNumber"] - course_unit = course_unit_by_id(curr_exchange["courseUnitId"]) - if ExchangeController.exchange_overlap(student_schedules, other_student): - return (ExchangeStatus.CLASSES_OVERLAP, None) - inserted_exchanges.append(DirectExchangeParticipants( participant_name=curr_exchange["other_student"]["name"], participant_nmec=curr_exchange["other_student"]["mecNumber"], @@ -162,3 +156,36 @@ def exchange_overlap(student_schedules, username) -> bool: return False + @staticmethod + def update_schedule_accepted_exchanges(student, schedule): + accepted_options = DirectExchangeParticipants.objects.filter(participant_nmec=student, accepted=True, direct_exchange__accepted=True) + + (status, trailing) = ExchangeController.update_schedule(schedule, accepted_options) + if status == ExchangeStatus.FETCH_SCHEDULE_ERROR: + return (ExchangeStatus.FETCH_SCHEDULE_ERROR, trailing) + + return (ExchangeStatus.SUCCESS, None) + + @staticmethod + def update_schedule(student_schedule, exchanges): + for exchange in exchanges: + for i, schedule in enumerate(student_schedule): + ocurr_id = int(schedule["ocorrencia_id"]) + if ocurr_id == int(exchange.course_unit_id): + class_type = schedule["tipo"] + + res = SigarraController().get_class_schedule(int(exchange.course_unit_id), exchange.class_participant_goes_to) + if res.status_code != 200: + return (ExchangeStatus.FETCH_SCHEDULE_ERROR, None) + + (tp_schedule, t_schedule) = res.data + tp_schedule.extend(t_schedule) + new_schedules = tp_schedule + + for new_schedule in new_schedules: + for turma in new_schedule["turmas"]: + if turma["turma_sigla"] == exchange.class_participant_goes_to and schedule["tipo"] == class_type: + del student_schedule[i] + student_schedule.append(new_schedule) + + return (ExchangeStatus.SUCCESS, None) diff --git a/django/university/controllers/ExchangeValidationController.py b/django/university/controllers/ExchangeValidationController.py index 9c37470..85d27d3 100644 --- a/django/university/controllers/ExchangeValidationController.py +++ b/django/university/controllers/ExchangeValidationController.py @@ -3,7 +3,9 @@ from university.exchange.utils import exchange_overlap, build_student_schedule_dict, ExchangeStatus, exchange_status_message -from university.models import DirectExchangeParticipants +from university.models import DirectExchangeParticipants, DirectExchange + +from django.db import transaction class ExchangeValidationResponse: def __init__(self, status: bool, message: ExchangeStatus): @@ -11,6 +13,50 @@ def __init__(self, status: bool, message: ExchangeStatus): self.message = exchange_status_message(message) class ExchangeValidationController: + """ + Imagine the scenario where a person has two active pending exchanges: + + - 1LEIC01 -> 1LEIC06 (AM I) + - 1LEIC01 -> 1LEIC09 (AM I) + + If the user accepts the first exchange and the exchange is accepted, the other exchange will be invalidated and users should be warned about that. + + + All of the exchanges that include classes that were changed by the accepted exchange need to be revalidated or even canceled. + """ + def cancel_conflicting_exchanges(self, accepted_exchange_id: int): + conflicting_exchanges = [] + + with transaction.atomic(): + accepted_exchange_participants = DirectExchangeParticipants.objects.filter(direct_exchange__id=accepted_exchange_id) + for participant in accepted_exchange_participants: + # 1. Are there any exchanges that include classes that a participant changed from? + conflicting = DirectExchangeParticipants.objects.exclude(direct_exchange__id=accepted_exchange_id).filter( + participant_nmec=participant.participant_nmec, + class_participant_goes_from=participant.class_participant_goes_from + ) + conflicting_exchanges.extend(list(map(lambda conflicting_exchange: conflicting_exchange.direct_exchange, conflicting))) + + # 2. Revalidate all of the other exchanges who include classes from the participant but not classes + # that are the "class_participant_goes_from" of the exchange + for participant in accepted_exchange_participants: + exchanges = DirectExchange.objects.exclude(id=accepted_exchange_id).filter( + directexchangeparticipants__participant_nmec=participant.participant_nmec + ) + exchanges = [exchange for exchange in exchanges if exchange not in conflicting_exchanges] + + for exchange in exchanges: + if not self.validate_direct_exchange(exchange.id).status: + conflicting_exchanges.append(exchange) + + # 3. Cancel all the conflicting exchanges + for conflicting_exchange in conflicting_exchanges: + self.cancel_exchange(conflicting_exchange) + + def cancel_exchange(self, exchange: DirectExchange): + exchange.canceled = True + exchange.save() + """ This class will contain methods to validate the direct exchanges that are already inside of the database. @@ -20,7 +66,7 @@ class ExchangeValidationController: will validate the request format. """ def validate_direct_exchange(self, exchange_id: int) -> ExchangeValidationResponse: - exchange_participants = list(DirectExchangeParticipants.objects.filter(direct_exchange__id=exchange_id).all()) + exchange_participants = DirectExchangeParticipants.objects.filter(direct_exchange__id=exchange_id).all() # 1. Build new schedule of each student schedule = {} @@ -28,22 +74,26 @@ def validate_direct_exchange(self, exchange_id: int) -> ExchangeValidationRespon if participant.participant_nmec not in schedule.keys(): schedule[participant.participant_nmec] = build_student_schedule_dict(SigarraController().get_student_schedule(int(participant.participant_nmec)).data) + # Get new schedule from accepted changes + ExchangeController.update_schedule_accepted_exchanges(participant.participant_nmec, schedule) + # 2. Check if users are inside classes they will exchange from with for username in schedule.keys(): - participant_entries = list(DirectExchangeParticipants.objects.filter(participant_nmec=username).all()) + participant_entries = list(exchange_participants.filter(participant_nmec=username)) for entry in participant_entries: if (entry.class_participant_goes_from, int(entry.course_unit_id)) not in list(schedule[username].keys()): return ExchangeValidationResponse(False, ExchangeStatus.STUDENTS_NOT_ENROLLED) # 3. Alter the schedule of the users according to the exchange metadata - schedule[username][(entry.class_participant_goes_to, int(entry.course_unit_id))] = SigarraController().get_class_schedule(int(entry.course_unit_id), entry.class_participant_goes_to).data - del schedule[username][(entry.class_participant_goes_from, int(entry.course_unit_id))] + class_schedule = SigarraController().get_class_schedule(int(entry.course_unit_id), entry.class_participant_goes_to).data[0][0] # For other courses we will need to have pratical class as a list in the dictionary + schedule[username][(entry.class_participant_goes_to, int(entry.course_unit_id))] = class_schedule + del schedule[username][(entry.class_participant_goes_from, int(entry.course_unit_id))] # 4. Verify if the exchanges will have overlaps after building the new schedules for username in schedule.keys(): - if exchange_overlap(schedule, username): + if ExchangeController.exchange_overlap(schedule, username): return ExchangeValidationResponse(False, ExchangeStatus.CLASSES_OVERLAP) return ExchangeValidationResponse(True, ExchangeStatus.SUCCESS) \ No newline at end of file diff --git a/django/university/controllers/SigarraController.py b/django/university/controllers/SigarraController.py index 7741134..ebf863a 100644 --- a/django/university/controllers/SigarraController.py +++ b/django/university/controllers/SigarraController.py @@ -125,6 +125,9 @@ def get_course_unit_classes(self, course_unit_id: int) -> SigarraResponse: return SigarraResponse(response.json(), 200) + """ + Returns a tuple with (pratical class, theoretical class) + """ def get_class_schedule(self, course_unit_id: int, class_name: str) -> SigarraResponse: (semana_ini, semana_fim) = self.semester_weeks() diff --git a/django/university/exchange/utils.py b/django/university/exchange/utils.py index fdce25f..fd6aeb7 100644 --- a/django/university/exchange/utils.py +++ b/django/university/exchange/utils.py @@ -1,6 +1,6 @@ from datetime import date from university.controllers.SigarraController import SigarraController -from university.models import CourseUnit, DirectExchangeParticipants, MarketplaceExchange, MarketplaceExchangeClass, Professor, DirectExchange +from university.models import CourseUnit, MarketplaceExchange, MarketplaceExchangeClass, Professor, DirectExchange from enum import Enum import requests @@ -206,31 +206,3 @@ def convert_sigarra_schedule(schedule_data): new_schedule_data.append(new_schedule) return new_schedule_data - -def update_schedule_accepted_exchanges(student, schedule): - direct_exchange_ids = DirectExchangeParticipants.objects.filter( - participant_nmec=student, direct_exchange__accepted=True - ).values_list('direct_exchange', flat=True) - direct_exchanges = DirectExchange.objects.filter(id__in=direct_exchange_ids).order_by('date') - - for exchange in direct_exchanges: - participants = DirectExchangeParticipants.objects.filter(direct_exchange=exchange, participant_nmec=student).order_by('date') - (status, trailing) = update_schedule(schedule, participants) - if status == ExchangeStatus.FETCH_SCHEDULE_ERROR: - return (ExchangeStatus.FETCH_SCHEDULE_ERROR, trailing) - - return (ExchangeStatus.SUCCESS, None) - -def update_schedule(student_schedule, exchanges): - for exchange in exchanges: - for i, schedule in enumerate(student_schedule): - if schedule["ucurr_sigla"] == exchange.course_unit: - class_type = schedule["tipo"] - - # TODO if old_class schedule is different from current schedule, abort - - for turma in schedule["turmas"]: - if turma["turma_sigla"] == exchange.class_participant_goes_to and schedule["tipo"] == class_type: - student_schedule[i] = schedule - - return (ExchangeStatus.SUCCESS, None) diff --git a/django/university/routes/MarketplaceExchangeView.py b/django/university/routes/MarketplaceExchangeView.py index 25bf187..f5705ba 100644 --- a/django/university/routes/MarketplaceExchangeView.py +++ b/django/university/routes/MarketplaceExchangeView.py @@ -10,7 +10,7 @@ from university.controllers.ExchangeController import ExchangeController from university.controllers.SigarraController import SigarraController -from university.exchange.utils import ExchangeStatus, build_marketplace_submission_schedule, build_student_schedule_dict, exchange_overlap, incorrect_class_error, update_schedule_accepted_exchanges +from university.exchange.utils import ExchangeStatus, build_marketplace_submission_schedule, build_student_schedule_dict, exchange_overlap, incorrect_class_error from university.models import CourseUnit, MarketplaceExchange, MarketplaceExchangeClass, UserCourseUnits, Class, ExchangeUrgentRequests, ExchangeUrgentRequestOptions from university.serializers.MarketplaceExchangeClassSerializer import MarketplaceExchangeClassSerializer @@ -120,11 +120,10 @@ def submit_marketplace_exchange_request(self, request): student_schedules[curr_student] = build_student_schedule_dict(sigarra_res.data) student_schedule = list(student_schedules[curr_student].values()) - update_schedule_accepted_exchanges(curr_student, student_schedule) + ExchangeController.update_schedule_accepted_exchanges(curr_student, student_schedule) student_schedules[curr_student] = build_student_schedule_dict(student_schedule) (status, new_marketplace_schedule) = build_marketplace_submission_schedule(student_schedules, exchanges, curr_student) - print("Student schedules: ", student_schedules[curr_student]) if status == ExchangeStatus.STUDENTS_NOT_ENROLLED: return JsonResponse({"error": incorrect_class_error()}, status=400, safe=False) diff --git a/django/university/routes/exchange/AdminExchangeRequestAcceptView.py b/django/university/routes/exchange/AdminExchangeRequestAcceptView.py index cba300a..086b694 100644 --- a/django/university/routes/exchange/AdminExchangeRequestAcceptView.py +++ b/django/university/routes/exchange/AdminExchangeRequestAcceptView.py @@ -2,9 +2,17 @@ from django.views import View from university.controllers.AdminExchangeStateChangeController import AdminExchangeStateChangeController +from university.models import DirectExchange, ExchangeUrgentRequests, CourseUnitEnrollments class AdminExchangeRequestAcceptView(View): def put(self, request, request_type, id): AdminExchangeStateChangeController().update_state_to(request_type, id, "treated") - - return HttpResponse(status=200) \ No newline at end of file + + if request_type == "direct_exchange": + DirectExchange.objects.filter(id=id).update(accepted=True) + elif request_type == "urgent_exchange": + ExchangeUrgentRequests.objects.filter(id=id).update(accepted=True) + elif request_type == "enrollment": + CourseUnitEnrollments.objects.filter(id=id).update(accepted=True) + + return HttpResponse(status=200) diff --git a/django/university/routes/exchange/AdminExchangeRequestRejectView.py b/django/university/routes/exchange/AdminExchangeRequestRejectView.py index 1b57df3..3954889 100644 --- a/django/university/routes/exchange/AdminExchangeRequestRejectView.py +++ b/django/university/routes/exchange/AdminExchangeRequestRejectView.py @@ -2,9 +2,17 @@ from django.views import View from university.controllers.AdminExchangeStateChangeController import AdminExchangeStateChangeController +from university.models import DirectExchange, ExchangeUrgentRequests, CourseUnitEnrollments class AdminExchangeRequestRejectView(View): def put(self, request, request_type, id): AdminExchangeStateChangeController().update_state_to(request_type, id, "rejected") + + if request_type == "direct_exchange": + DirectExchange.objects.filter(id=id).update(accepted=False) + elif request_type == "urgent_exchange": + ExchangeUrgentRequests.objects.filter(id=id).update(accepted=False) + elif request_type == "enrollment": + CourseUnitEnrollments.objects.filter(id=id).update(accepted=False) return HttpResponse(status=200) \ No newline at end of file diff --git a/django/university/routes/exchange/DirectExchangeView.py b/django/university/routes/exchange/DirectExchangeView.py index a379444..2c92640 100644 --- a/django/university/routes/exchange/DirectExchangeView.py +++ b/django/university/routes/exchange/DirectExchangeView.py @@ -21,7 +21,7 @@ from university.serializers.DirectExchangeParticipantsSerializer import DirectExchangeSerializer from university.controllers.ExchangeValidationController import ExchangeValidationController from university.controllers.StudentController import StudentController -from university.exchange.utils import ExchangeStatus, build_new_schedules, build_student_schedule_dict, build_student_schedule_dicts, incorrect_class_error, update_schedule_accepted_exchanges, exchange_status_message +from university.exchange.utils import ExchangeStatus, build_new_schedules, build_student_schedule_dict, build_student_schedule_dicts, incorrect_class_error class DirectExchangeView(View): def __init__(self): @@ -105,9 +105,10 @@ def post(self, request): if status == ExchangeStatus.FETCH_SCHEDULE_ERROR: return HttpResponse(status=trailing) + # Update student schedule with exchange updates that are not in sigarra currently for student in student_schedules.keys(): student_schedule = list(student_schedules[student].values()) - update_schedule_accepted_exchanges(student, student_schedule) + ExchangeController.update_schedule_accepted_exchanges(student, student_schedule) student_schedules[student] = build_student_schedule_dict(student_schedule) with transaction.atomic(): @@ -116,20 +117,25 @@ def post(self, request): issuer_name=f"{request.user.first_name} {request.user.last_name}", issuer_nmec=request.user.username, date=timezone.now(), - admin_state="untreated" + admin_state="untreated", + canceled=False ) + inserted_exchanges = [] + ExchangeController.create_direct_exchange_participants( + student_schedules, exchanges, inserted_exchanges, exchange_model, request.user.username + ) + + # Change the schedules to the final result of the exchange so it is easier to detect overlaps (status, trailing) = build_new_schedules( student_schedules, exchanges, request.user.username) if status == ExchangeStatus.STUDENTS_NOT_ENROLLED: return JsonResponse({"error": incorrect_class_error()}, status=400, safe=False) - - inserted_exchanges = [] - (status, trailing) = ExchangeController.create_direct_exchange_participants(student_schedules, exchanges, inserted_exchanges, exchange_model, request.user.username) - - if status == ExchangeStatus.CLASSES_OVERLAP: - return JsonResponse({"error": "classes-overlap"}, status=400, safe=False) + + for username in student_schedules.keys(): + if ExchangeController.exchange_overlap(student_schedules, username): + return JsonResponse({"error": "classes-overlap"}, status=400, safe=False) exchange_model.save() @@ -151,28 +157,36 @@ def post(self, request): 'tts@exchange.com', [f'up{participant}@up.pt'] ) - return JsonResponse({"success": True}, safe=False) def put(self, request, id): # Validate if exchange is still valid if not ExchangeValidationController().validate_direct_exchange(id).status: + ExchangeValidationController().cancel_exchange(DirectExchange.objects.get(id=id)) return JsonResponse({"error": ExchangeValidationController().validate_direct_exchange(id).message}, status=400, safe=False) exchange = DirectExchange.objects.get(id=id) - participants = DirectExchangeParticipants.objects.filter(direct_exchange=exchange) - for participant in participants: - if participant.participant_nmec == request.user.username: - participant.accepted = True - participant.save() + try: + with transaction.atomic(): + # Update exchange accepted states + participants = DirectExchangeParticipants.objects.filter(direct_exchange=exchange) + for participant in participants: + if participant.participant_nmec == request.user.username: + participant.accepted = True + participant.save() - participants = DirectExchangeParticipants.objects.filter(direct_exchange=exchange) - if all(participant.accepted for participant in participants): - exchange.accepted = True + if all(participant.accepted for participant in participants): + exchange.accepted = True + exchange.save() - for participant in participants: - StudentController.populate_user_course_unit_data(int(participant.participant_nmec), erase_previous=True) + for participant in participants: + StudentController.populate_user_course_unit_data(int(participant.participant_nmec), erase_previous=True) - return JsonResponse({"success": True}, safe=False) + ExchangeValidationController().cancel_conflicting_exchanges(exchange.id) + + return JsonResponse({"success": True}, safe=False) + except Exception as e: + print("ERROR: ", e) + return JsonResponse({"success": False}, status=400, safe=False) diff --git a/django/university/routes/exchange/verify/DirectExchangeValidationView.py b/django/university/routes/exchange/verify/DirectExchangeValidationView.py index c9f322d..9cdab63 100644 --- a/django/university/routes/exchange/verify/DirectExchangeValidationView.py +++ b/django/university/routes/exchange/verify/DirectExchangeValidationView.py @@ -3,6 +3,8 @@ from university.controllers.ExchangeValidationController import ExchangeValidationController +from university.models import DirectExchange + class DirectExchangeValidationView(View): def get(self, request, id): validation = ExchangeValidationController().validate_direct_exchange(id) @@ -10,4 +12,5 @@ def get(self, request, id): if validation.status: return HttpResponse(status=200) + ExchangeValidationController().cancel_exchange(DirectExchange.objects.get(id=id)) return JsonResponse({"error": validation.message}, status=400, safe=False) \ No newline at end of file diff --git a/django/university/routes/exchange/verify/ExchangeVerifyView.py b/django/university/routes/exchange/verify/ExchangeVerifyView.py index 3288936..35b8c7b 100644 --- a/django/university/routes/exchange/verify/ExchangeVerifyView.py +++ b/django/university/routes/exchange/verify/ExchangeVerifyView.py @@ -4,12 +4,11 @@ from django.core.cache import cache +from django.db import transaction + from django.http import HttpResponse, JsonResponse from django.views import View -from django.utils.html import strip_tags -from django.core.mail import send_mail -from django.template.loader import render_to_string -from tts_be.settings import JWT_KEY, VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS, DOMAIN +from tts_be.settings import JWT_KEY, VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS from university.controllers.StudentController import StudentController from university.controllers.ExchangeValidationController import ExchangeValidationController @@ -20,48 +19,48 @@ def post(self, request, token): try: exchange_info = jwt.decode(token, JWT_KEY, algorithms=["HS256"]) + direct_exchange = DirectExchange.objects.get(id=exchange_info["exchange_id"]) + if not ExchangeValidationController().validate_direct_exchange(exchange_info["exchange_id"]).status: + ExchangeValidationController().cancel_exchange(direct_exchange) return JsonResponse({"verified": False}, safe=False, status=403) token_seconds_elapsed = time.time() - exchange_info["exp"] if token_seconds_elapsed > VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS: return JsonResponse({"verified": False}, safe=False, status=403) - participant = DirectExchangeParticipants.objects.filter(participant=request.session["username"]) - participant.update(accepted=True) + with transaction.atomic(): + participant = DirectExchangeParticipants.objects.filter(participant=request.session["username"]) + participant.update(accepted=True) - all_participants = DirectExchangeParticipants.objects.filter(direct_exchange_id=exchange_info["exchange_id"]) - - accepted_participants = 0 - for participant in all_participants: - accepted_participants += participant.accepted + all_participants = DirectExchangeParticipants.objects.filter(direct_exchange_id=exchange_info["exchange_id"]) + + accepted_participants = 0 + for participant in all_participants: + accepted_participants += participant.accepted - if accepted_participants == len(all_participants): - direct_exchange = DirectExchange.objects.filter(id=int(exchange_info["exchange_id"])) - direct_exchange.update(accepted=True) + if accepted_participants == len(all_participants): + direct_exchange.accepted = True + direct_exchange.save() - marketplace_exchange = direct_exchange.first().marketplace_exchange + # Change user schedule + for participant in all_participants: + StudentController.populate_user_course_unit_data(int(participant.participant_nmec), erase_previous=True) - if(marketplace_exchange != None): - direct_exchange_object = direct_exchange.first() - direct_exchange_object.marketplace_exchange = None - direct_exchange_object.save() - marketplace_exchange.delete() - for participant in all_participants: - StudentController.populate_user_course_unit_data(int(participant.participant_nmec), erase_previous=True) + ExchangeValidationController().cancel_conflicting_exchanges(exchange_info["exchange_id"]) - if cache.get(token) != None: - return JsonResponse({"verified": False}, safe=False, status=403) - - # Blacklist token since this token is usable only once - cache.set( - key=token, - value=token, - timeout=VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS - token_seconds_elapsed - ) - - return JsonResponse({"verified": True}, safe=False) + if cache.get(token) is not None: + return JsonResponse({"verified": False}, safe=False, status=403) + + # Blacklist token since this token is usable only once + cache.set( + key=token, + value=token, + timeout=VERIFY_EXCHANGE_TOKEN_EXPIRATION_SECONDS - token_seconds_elapsed + ) + + return JsonResponse({"verified": True}, safe=False) except Exception as e: print("Error: ", e) diff --git a/django/university/routes/student/exchange/StudentReceivedExchangesView.py b/django/university/routes/student/exchange/StudentReceivedExchangesView.py index a2708a0..2dbb769 100644 --- a/django/university/routes/student/exchange/StudentReceivedExchangesView.py +++ b/django/university/routes/student/exchange/StudentReceivedExchangesView.py @@ -6,6 +6,7 @@ from university.controllers.ExchangeController import DirectExchangePendingMotive from university.models import DirectExchange, DirectExchangeParticipants from university.serializers.DirectExchangeParticipantsSerializer import DirectExchangeParticipantsSerializer +from university.serializers.DirectExchangeSerializer import DirectExchangeSerializer class StudentReceivedExchangesView(APIView): def get(self, request): @@ -32,18 +33,12 @@ def build_pagination_payload(self, request, exchanges): "has_next": page_obj.has_next(), "has_previous": page_obj.has_previous(), }, - "data": [{ - "id": exchange.id, - "type": "directexchange", - "issuer_name": exchange.issuer_name, - "issuer_nmec": exchange.issuer_nmec, - "accepted": exchange.accepted, - "pending_motive": DirectExchangePendingMotive.get_value(DirectExchangePendingMotive.get_pending_motive(request.user.username, exchange)), - "options": [ - DirectExchangeParticipantsSerializer(participant).data for participant in exchange.options - ], - "date": exchange.date - } for exchange in page_obj] + "data": [ + { + **DirectExchangeSerializer(exchange).data, + "pending_motive": DirectExchangePendingMotive.get_value(DirectExchangePendingMotive.get_pending_motive(request.user.username, exchange)), + } + for exchange in page_obj] } diff --git a/django/university/routes/student/schedule/StudentScheduleView.py b/django/university/routes/student/schedule/StudentScheduleView.py index 1f0a932..2e7287b 100644 --- a/django/university/routes/student/schedule/StudentScheduleView.py +++ b/django/university/routes/student/schedule/StudentScheduleView.py @@ -7,7 +7,8 @@ from rest_framework import status from university.controllers.SigarraController import SigarraController -from university.exchange.utils import convert_sigarra_schedule, update_schedule_accepted_exchanges +from university.controllers.ExchangeController import ExchangeController +from university.exchange.utils import convert_sigarra_schedule class StudentScheduleView(APIView): def get(self, request, nmec=""): @@ -23,9 +24,10 @@ def get(self, request, nmec=""): old_schedule = hashlib.sha256(json.dumps(schedule_data, sort_keys=True).encode()).hexdigest() - update_schedule_accepted_exchanges(request.user.username, schedule_data) + ExchangeController.update_schedule_accepted_exchanges(request.user.username, schedule_data) new_schedule = hashlib.sha256(json.dumps(schedule_data, sort_keys=True).encode()).hexdigest() + sigarra_synchronized = old_schedule == new_schedule new_response = JsonResponse({"schedule": convert_sigarra_schedule(schedule_data), "noChanges": sigarra_synchronized}, safe=False) @@ -45,7 +47,7 @@ def retrieveCourseUnitClasses(sigarra_controller, username): schedule_data = sigarra_res.data - update_schedule_accepted_exchanges(username, schedule_data) + ExchangeController.update_schedule_accepted_exchanges(username, schedule_data) course_unit_classes = set() for scheduleItem in schedule_data: diff --git a/django/university/serializers/DirectExchangeSerializer.py b/django/university/serializers/DirectExchangeSerializer.py new file mode 100644 index 0000000..db3d7d2 --- /dev/null +++ b/django/university/serializers/DirectExchangeSerializer.py @@ -0,0 +1,27 @@ +from rest_framework import serializers + +from university.models import DirectExchangeParticipants +from university.serializers.DirectExchangeParticipantsSerializer import DirectExchangeParticipantsSerializer + +class DirectExchangeSerializer(serializers.Serializer): + id = serializers.IntegerField() + type = serializers.SerializerMethodField() + issuer_name = serializers.CharField() + issuer_nmec = serializers.CharField() + accepted = serializers.BooleanField() + canceled = serializers.BooleanField() + admin_state = serializers.CharField() + options = serializers.SerializerMethodField() + date = serializers.DateTimeField() + + def get_type(self, obj): + return "directexchange" + + def get_options(self, obj): + participants = DirectExchangeParticipants.objects.filter( + direct_exchange__id=obj.id + ) + + return [ + DirectExchangeParticipantsSerializer(participant).data for participant in participants + ] \ No newline at end of file diff --git a/postgres/sql/00_schema_postgres.sql b/postgres/sql/00_schema_postgres.sql index 938b9d2..cebc492 100644 --- a/postgres/sql/00_schema_postgres.sql +++ b/postgres/sql/00_schema_postgres.sql @@ -187,6 +187,7 @@ CREATE TABLE "public"."direct_exchange" ( "issuer_name" varchar(32) NOT NULL, "issuer_nmec" varchar(32) NOT NULL, "accepted" boolean NOT NULL, + "canceled" boolean DEFAULT false, "date" TIMESTAMP DEFAULT now(), "admin_state" varchar(32) NOT NULL DEFAULT 'untreated' );