import functools from sys import api_version from rest_framework.decorators import api_view from datetime import datetime import random from shutil import register_unpack_format import string from uuid import uuid4 from django.shortcuts import render import pytz from rest_framework.response import Response from rest_framework.views import APIView from rest_framework import status from .models import Room, Parcours, TempCorrection from .serializers import CreateRoomSerializer, ParcoursCreateSerializer, RoomSerializer, ParcoursFullSerializer, ParcoursSerializer from django.contrib.sessions.models import Session from api.Generateur import Generateur from exercices.models import Exercice from exercices.serializers import ExerciceSerializer from channels.layers import get_channel_layer from asgiref.sync import async_to_sync from .utils import getNow # Create your views here. class RoomAPI(APIView): def post(self, request, format=None): name = request.data.get('name') private = request.data.get('private') results = request.data.get('results') if request.user.is_authenticated: serial = CreateRoomSerializer(data={'name': name, 'private': private, 'nick':request.user.username, 'public_results': results }) if serial.is_valid(): room = Room(name=name,private= private, public_result=results,owner={ "name": request.user.username, 'code': request.user.id_code, 'clientId': request.user.clientId}) room.save() clientId = request.user.clientId code = request.user.id_code room.userMembers.add(request.user) else: return Response({'error': serial.errors}, status=status.HTTP_400_BAD_REQUEST) else: nick = request.data.get('nick') serial = CreateRoomSerializer( data={'name': name, 'private': private, 'nick': nick, 'public_results': results}) if serial.is_valid(): code = ''.join( random.choices(string.ascii_uppercase, k=6)) clientId = str(uuid4()) room = Room(name=name, private=private, public_result=results, owner={'name': nick, 'code': code, "clientId": clientId}, anonymousMembers=[{'nick': nick, 'owner': True, 'code': code, "clientId": clientId}] ) room.online=[code] room.save() else: return Response({'error': serial.errors}, status=status.HTTP_400_BAD_REQUEST) return Response({"data": {'id_code': room.id_code, "clientId": clientId, "code": code}}, status=status.HTTP_200_OK) def get(self, request, format=None): code = request.GET.get('code') room = None try: room = Room.objects.filter(id_code=code)[0] except IndexError: return Response({'error': 'Aucune salle trouvée', 'code': '4044'}, status=status.HTTP_404_NOT_FOUND) clientId = request.GET.get('clientId') participants = room.anonymousMembers users = room.userMembers.all() userInRoom = False if request.user.is_authenticated: userInRoom = len(request.user.room_set.filter( id_code=room.id_code)) != 0 if clientId not in [p['clientId'] for p in participants] and not userInRoom: return Response({'error': 'Non autorisé', 'code': '401'}, status=status.HTTP_401_UNAUTHORIZED) admin = False if clientId == room.owner['clientId']: admin = True online = room.online return Response({"data": {"room": {**RoomSerializer(room).data, "waiters": room.waiters if admin == True else [], "participants": [{'nick': p['nick'], 'owner': p['clientId'] == room.owner['clientId'], 'online': p['code'] in online, "code": p['code'] if admin == True else "", 'status': "anonymous"} for p in participants] + [{"status": "user", 'nick': u.username, 'owner': u.clientId == room.owner['clientId'], 'online': u.id_code in online, 'code': u.id_code if admin == True else ""} for u in users], 'parcours': [ParcoursSerializer(p, context={'clientId': clientId}).data for p in room.parcours_set.all()]}}}, status=status.HTTP_200_OK) def delete(self, request, format=None): code = request.data.get('id_code') clientId = request.data.get('clientId') try: room = Room.objects.filter(id_code=code)[0] except: return Response({'error': 'Not found'}, status=status.HTTP_404_NOT_FOUND) if request.user.is_authenticated: if request.user.id_code == room.owner['code'] and request.user.clientId == room.owner['clientId']: room.delete() else: return Response({'error': 'Unauthorized'}, status=status.HTTP_401_UNAUTHORIZED) else: if clientId == room.owner['clientId']: room.delete() else: return Response({'error': 'Unauthorized'}, status=status.HTTP_401_UNAUTHORIZED) channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)(room.id_code, { 'type': "room_deleted"}) return Response({"data": ''}, status=status.HTTP_200_OK) def patch(self, request, format=None): code = request.data.get('pin') nick = request.data.get('nick') room = None try: room = Room.objects.filter(id_code=code)[0] except IndexError: return Response({'error': 'No room'}, status=status.HTTP_502_BAD_GATEWAY) if 'roomsJoined' not in request.session: request.session['roomsJoined'] = [] elif 'roomsJoined' in request.session: request.session['roomsJoined'] = list( filter(lambda s: len(Room.objects.filter(id_code=s['id_code'])) != 0, request.session['roomsJoined'])) if room.id_code not in list(map(lambda r: r['id_code'], request.session['roomsJoined'])): code = ''.join( random.choices(string.ascii_uppercase, k=6)) while code in list(map(lambda p: p['code'], room.participants)): code = ''.join( random.choices(string.ascii_uppercase, k=6)) room.participants = [*room.participants, { 'nick': nick, 'owner': False, 'code': code }] try: request.session['roomsJoined'] = [*request.session['roomsJoined'], {'id_code': room.id_code, 'nick': nick, 'owner': False, 'code': code}] except: request.session['roomsJoined'] = [{'id_code': room.id_code, 'nick': nick, 'owner': False, 'code': code}] room.save() return Response({"data": {'id_code': room.id_code}}, status=status.HTTP_200_OK) @api_view(['GET']) def RoomExist(request, id_code): if Room.objects.filter(id_code=id_code).count() > 0: userInRoom = False clientId = request.query_params.get('clientId', None) if request.user.is_authenticated: room = Room.objects.filter(id_code=id_code)[0] userInRoom = len(request.user.room_set.filter( id_code=room.id_code)) != 0 else: room = Room.objects.filter(id_code=id_code)[0] userInRoom = len([p for p in room.anonymousMembers if p['clientId'] == clientId]) != 0 return Response({'data': {"id_code": id_code, 'is_auth': userInRoom}}, status=status.HTTP_200_OK) else: return Response({'error': 'Aucune salle trouvée', 'code': '4044'}, status=status.HTTP_404_NOT_FOUND) @api_view(['GET']) def getCorrectionInfo(request): parcours_id = request.query_params['parcours_id'] user_code = request.query_params['user_code'] correct_code = request.query_params['correct_code'] parcours = Parcours.objects.filter(id_code=parcours_id) if len(parcours) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) parcours = parcours[0] challenger = list( filter(lambda p: p['code'] == user_code, parcours.challenger)) if(len(challenger) == 0): return Response({'error': 'Vous n\'êtes pas un participant'}, status=status.HTTP_401_UNAUTHORIZED) trys = challenger[0]['exos'] challenge = list(filter(lambda c: c['code'] == correct_code, trys)) if(len(challenge) == 0): return Response({'error': 'Not found'}, status=status.HTTP_404_NOT_FOUND) challenge = challenge[0] return Response({'data': {**challenge, "parcours_id": parcours.id_code, "success_condition": parcours.success_condition, 'user_code': user_code, 'parcours_name': parcours.name, 'challenger_name': challenger[0]['nick']}}, status=status.HTTP_200_OK) @api_view(['GET']) def getEditParcours(request): parcours_id = request.query_params['id_code'] parcours = Parcours.objects.filter(id_code=parcours_id) if len(parcours) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) parcours = parcours[0] exos = parcours.exercices exosList = [] for e in exos: exo = Exercice.objects.filter(id_code=e['id_code']) if len(exo) != 0: exosList.append( {**ExerciceSerializer(exo[0]).data, 'number': e['number']}) return Response({'data': {**ParcoursSerializer(parcours).data, 'exercices': exosList}}, status=status.HTTP_200_OK) @api_view(['POST']) def lockRoom(request): room_id = request.data['id_code'] if request.user.is_authenticated: clientId = request.user.clientId else: clientId = request.data['clientId'] room = Room.objects.filter(id_code=room_id) if len(room) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) room = room[0] if room.owner['clientId'] != clientId: return Response({'error': 'Pas autorisé en fait'}, status = status.HTTP_401_UNAUTHORIZED) room.private = room.private == False room.save() return Response({'data': {'private': room.private}}, status=status.HTTP_200_OK) @api_view(['POST']) def publicResultToggler(request): room_id = request.data['id_code'] if request.user.is_authenticated: clientId = request.user.clientId else: clientId = request.data['clientId'] room = Room.objects.filter(id_code=room_id) if len(room) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) room = room[0] if room.owner['clientId'] != clientId: return Response({'error': 'Pas autorisé en fait'}, status = status.HTTP_401_UNAUTHORIZED) room.public_result = room.public_result == False room.save() return Response({'data': {'private': room.public_result}}, status=status.HTTP_200_OK) @api_view(['POST']) def changeRoomName(request): room_id = request.data['id_code'] if request.user.is_authenticated: clientId = request.user.clientId else: clientId = request.data['clientId'] room = Room.objects.filter(id_code=room_id) if len(room) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) room = room[0] if room.owner['clientId'] != clientId: return Response({'error': 'Pas autorisé en fait'}, status = status.HTTP_401_UNAUTHORIZED) name = request.data['name'] if name == '': return Response({'error': 'Not null pls'}, status = status.HTTP_400_BAD_REQUEST) if len(name) > 30: return Response({'error': 'TRop long frr'}, status = status.HTTP_400_BAD_REQUEST) room.name = name room.save() return Response({'data': {'name':name}}, status=status.HTTP_200_OK) class ChallengeAPI(APIView): def get(self, request, format=None): id_code = request.GET.get('code') user_code = request.GET.get('user_code') parcours = Parcours.objects.filter(id_code=id_code) if len(parcours) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) parcours = parcours[0] room = parcours.room participants = list( filter(lambda p: p['code'] == user_code, room.anonymousMembers)) userInRoom = True if request.user.is_authenticated: userInRoom = len(request.user.room_set.filter( id_code=room.id_code)) != 0 if len(participants) == 0 and not userInRoom: return Response({'error': 'Vous n\'êtes pas un participant'}, status=status.HTTP_401_UNAUTHORIZED) exos = parcours.exercices exos = list(map(lambda e: Generateur(Exercice.objects.filter( id_code=e['id_code'])[0].exo_model.name, e['number'], 'web'), exos)) def parseCorrection(calcul): """Fait en sorte de séparer la correction présente dans le calcul""" calculEx = calcul['calcul'].replace('[', ' [').replace(']', '] ') splitted = calculEx.split() if len(list(filter(lambda e: e.startswith("[") and e.endswith(']'), splitted))) == 0: splitted.append('[]') inputs = [] for i in range(len(splitted)): c = splitted[i] if c.startswith('[') and c.endswith(']'): corr = c.replace('[', '').replace(']', '') splitted[i] = f'[{len(inputs)}]' inputs.append( {'order': len(inputs), 'correction': corr, 'value': ""}) calculEx = ' '.join(splitted) return {'calcul': calculEx, 'inputs': inputs} exosWcorrection = [] for i in range(len(exos)): ex = exos[i] tempsExs = list(map(parseCorrection, ex)) tmp = {'order': i, 'exos': [{'order': ind, **e} for ind, e in enumerate(tempsExs)]} exosWcorrection.append(tmp) #exos[i] = {'order': i, 'exos': list(map(lambda e, id: {'order': id,'calcul': e['calcul'], 'inputs': list(map(lambda i :{'order': i['order'],'value':i['value']}, e['inputs']))}, tempsExs))} #exos[i] = list(map(lambda e: {'calcul': e['calcul'], 'inputs': list(map(lambda i :{'order': i['order'],'value':i['value']}, e['inputs']))}, tempsExs)) exos[i] = {'order': i, 'exos': [{'order': ind, 'calcul': e['calcul'], 'inputs': [ {'value': inp['value'], 'order': inp['order'], 'correction': ""} for inp in e['inputs']]} for ind, e in enumerate(tempsExs)]} tempCorr = TempCorrection(correction=exosWcorrection) tempCorr.save() return Response({"data": {**ParcoursSerializer(parcours).data, "exos": exos, 'correctId': tempCorr.id_code}}, status=status.HTTP_200_OK) def post(self, request, format=None): exos = request.data.get('exos') parcours_id = request.data.get('id_code') user_code = request.data.get('user_code') if request.user.is_authenticated: user_code = request.user.id_code correct_code = request.data.get('correct_code') parcours = Parcours.objects.filter(id_code=parcours_id) if len(parcours) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) parcours = parcours[0] room = parcours.room corrections = TempCorrection.objects.filter(id_code=correct_code) if len(corrections) == 0: return Response({'error': 'Correction indisponible !'}, status=status.HTTP_404_NOT_FOUND) correctionDB = corrections[0] participants = list( filter(lambda p: p['code'] == user_code, room.anonymousMembers)) userInRoom = True if request.user.is_authenticated: userInRoom = len(request.user.room_set.filter( id_code=room.id_code)) != 0 if len(participants) == 0 and not userInRoom: return Response({'error': 'Vous n\'êtes pas un participant'}, status=status.HTTP_401_UNAUTHORIZED) # Corriger selon la correction les exos envoyé dans exos.exos student_answer = exos['result'] correction = correctionDB.correction def corrige(ans): correctionExos = list( filter(lambda c: c['order'] == ans['order'], correction))[0] corrigedList = [] for a in ans['exos']: #a = ans['exos'][i] exoCorrect = list( filter(lambda c: c['order'] == a['order'], correctionExos['exos']))[0] correctInputs = exoCorrect['inputs'] studentsInput = sorted( a['inputs'], key=lambda i: i.get('order')) corrigedInputs = [{**inp, "value": studentsInput[inp['order']]['value'], "isCorrect": (str(studentsInput[inp['order']]['value']) == str( inp['correction'])) if inp['correction'] != "" else None} for inp in correctInputs] corrigedList.append({**a, 'inputs': corrigedInputs}) return {**ans, "exos": corrigedList} corriged = list((map(corrige, student_answer))) # l6 = uniquement les isCorrect l2 = [exs['exos'] for exs in corriged] l4 = [ex['inputs'] for exs in l2 for ex in exs] l5 = [ex for exs in l4 for ex in exs] l6 = [e['isCorrect'] for e in l5] note = { "total": len(l6), "value": l6.count(True), "isTrust": l6.count(None) == 0 } adding_challenger = Parcours.objects.challenge( parcours_id, user_code, {"result": corriged, "timer": exos['timer'], "note": note}, request.user.is_authenticated) correctionDB.delete() now = datetime.now() now = str(now.astimezone(pytz.timezone('Europe/Berlin'))) channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)(f"owner__{room.id_code}", { 'type': "challenge_parcours", 'id_code': parcours_id, 'moyenne': Parcours.objects.getAverage(parcours.id_code, participants[0]['code'] if not request.user.is_authenticated else request.user.id_code), "participant": participants[0] if not request.user.is_authenticated else {'nick': request.user.username, 'code': request.user.id_code, 'clientId': request.user.clientId}, "validate": adding_challenger['validate'], 'exos': {'note': note, "code": adding_challenger['code'], 'canCorrige': True, 'endAt': now, "timer": exos['timer']}}) return Response({"data": {"exos": corriged, "note": note}}, status=status.HTTP_200_OK) def put(self, request, format=None): parcours_id = request.data.get('parcours_id') result = request.data.get('result') challenge_code = request.data.get('challenge_code') clientId = request.data.get('clientId') user_code = request.data.get('user_code') note = request.data.get('note') parcours = Parcours.objects.filter(id_code=parcours_id) if len(parcours) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) parcours = parcours[0] room = parcours.room if room.owner['clientId'] != clientId: return Response({'error': 'Pas les droits'}, status=status.HTTP_401_UNAUTHORIZED) challenger = [p for p in parcours.challenger if p['code'] == user_code] if(len(challenger) == 0): return Response({'error': 'Not found'}, status=status.HTTP_401_UNAUTHORIZED) trys = challenger[0]['exos'] challenge = [c for c in trys if c['code'] == challenge_code] if(len(challenge) == 0): return Response({'error': 'Not found'}, status=status.HTTP_404_NOT_FOUND) challenge = challenge[0] condition = parcours.success_condition parcours.challenger = [p if p['code'] != user_code else { **p, "exos": [e if e['code'] != challenge_code else {**e, "result": result, "note": note} for e in p['exos']]} for p in parcours.challenger] parcours.save() moyenne = Parcours.objects.getAverage( parcours.id_code, challenger[0]['code']) parcours.challenger = [p if p['code'] != user_code else { **p, 'validate': moyenne['value'] * 20 / moyenne['total'] >= condition} for p in parcours.challenger] parcours.save() challenger = [p for p in parcours.challenger if p['code'] == user_code] trys = challenger[0]['exos'] challenge = [c for c in trys if c['code'] == challenge_code] challenge = challenge[0] return Response({'data': {**challenge, "parcours_id": parcours.id_code, 'user_code': user_code, 'parcours_name': parcours.name, 'challenger_name': challenger[0]['nick']}}, status=status.HTTP_200_OK) class ParcoursAPI(APIView): def delete(self, request, format=None): id_code = request.data.get('id_code') code = request.data.get('code') parcours = Parcours.objects.filter(id_code=id_code) if len(parcours) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) parcours = parcours[0] room = parcours.room if room.owner['code'] != code: return Response({'error': 'Pas owner'}, status=status.HTTP_401_UNAUTHORIZED) channel_layer = get_channel_layer() async_to_sync(channel_layer.group_send)(parcours.room.id_code, {'type': 'delete_parcours', "id_code": id_code}) parcours.delete() return Response({'data': {'id_code': id_code}}, status=status.HTTP_200_OK) def post(self, request, format=None): name = request.data.get('name') code = request.data.get('code') room_code = request.data.get('room_code') exos = request.data.get('exos') timer = request.data.get('timer') success = request.data.get('success_condition') try: room = Room.objects.filter(id_code=room_code)[0] except: return Response({'error': ""}, status=status.HTTP_404_NOT_FOUND) if room.owner['code'] != code: return Response({'error': ''}, status=status.HTTP_401_UNAUTHORIZED) serial = ParcoursCreateSerializer( data={'name': name, 'room': room.id, 'timer': timer, 'exercices': exos, 'success_condition': success}) if not serial.is_valid(): return Response(serial.errors, status=status.HTTP_400_BAD_REQUEST) parcours = serial.create(serial.validated_data) layer = get_channel_layer() async_to_sync(layer.group_send)(room_code, { 'type': 'new_parcours', 'parcours': ParcoursFullSerializer(parcours).data}) layer = get_channel_layer() return Response({"data": {'id_code': parcours.id_code}}, status=status.HTTP_200_OK) def put(self, request, format=None): name = request.data.get('name') # ...futur: pour admin quand j'aurais fait la création de room "clean" code = request.data.get('code') id_code = request.data.get('id_code') exos = request.data.get('exos') timer = request.data.get('timer') success_condition = request.data.get('success_condition') try: parcours = Parcours.objects.filter(id_code=id_code)[0] except: return Response({'error': ""}, status=status.HTTP_404_NOT_FOUND) room = parcours.room if code != room.owner['code']: return Response({'error': ''}, status=status.HTTP_401_UNAUTHORIZED) parcours.challenger = [] parcours.name = name parcours.timer = timer parcours.exercices = exos parcours.success_condition = success_condition parcours.save() layer = get_channel_layer() async_to_sync(layer.group_send)(room.id_code, { 'type': 'edit_parcours', 'parcours': ParcoursFullSerializer(parcours).data}) layer = get_channel_layer() return Response({"data": {'id_code': parcours.id_code}}, status=status.HTTP_200_OK) def get(self, request, format=None): id_code = request.GET.get('code') user_code = request.GET.get('user_code') parcours = Parcours.objects.filter(id_code=id_code) if len(parcours) == 0: return Response({'error': 'Pas de parcours lol'}, status=status.HTTP_404_NOT_FOUND) parcours = parcours[0] room = parcours.room participants = list( filter(lambda p: p['code'] == user_code, room.anonymousMembers)) userInRoom = True if request.user.is_authenticated: userInRoom = len(request.user.room_set.filter( id_code=room.id_code)) != 0 if len(participants) == 0 and not userInRoom: return Response({'error': 'Vous n\'êtes pas un participant'}, status=status.HTTP_401_UNAUTHORIZED) challenger_data = parcours.challenger if user_code == parcours.room.owner['code']: return Response({"data": ParcoursFullSerializer(parcours).data}, status=status.HTTP_200_OK) else: if parcours.room.public_result == False: return Response({"data": ParcoursFullSerializer(parcours, context={'user_code': user_code}).data, }, status=status.HTTP_200_OK) else: return Response({"data": ParcoursFullSerializer(parcours).data, }, status=status.HTTP_200_OK)