From 65b51d974400dcd3a0fcc0f59d8ae8825d7ab0f1 Mon Sep 17 00:00:00 2001 From: Kraust Date: Mon, 2 Sep 2024 10:48:20 -0400 Subject: [PATCH] Bump OSCR version and Added Upload V2 API (#71) * New Upload v2 API * Updated Requirements --- VERSION | 2 +- combatlog/models/combatlog.py | 2 +- combatlog/serializers/combatlog.py | 20 ++++++++++- combatlog/views/combatlog.py | 56 ++++++++++++++++++++++++++---- ladder/fixtures/ladders.json | 28 +++++++++++++++ ladder/fixtures/ladders_solo.json | 28 +++++++++++++++ ladder/models/ladder.py | 2 +- requirements.txt | 2 +- shell.nix | 4 +++ 9 files changed, 133 insertions(+), 11 deletions(-) diff --git a/VERSION b/VERSION index c8c06a7..d5c5586 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2024.6.9.1 +2024.9.2.1 diff --git a/combatlog/models/combatlog.py b/combatlog/models/combatlog.py index 2ee0edb..91fed68 100644 --- a/combatlog/models/combatlog.py +++ b/combatlog/models/combatlog.py @@ -188,7 +188,7 @@ def update_metadata_file(self, file, force=False): if len(ladders) == 0: raise APIException( - f"{combat.map} {combat.difficulty} at {combat.start_time} has no matching ladder" + f"{combat.map} ({combat.difficulty} Difficulty) at {combat.start_time} has no matching ladder" ) for _, player in players: diff --git a/combatlog/serializers/combatlog.py b/combatlog/serializers/combatlog.py index ad7a7ea..55c017f 100644 --- a/combatlog/serializers/combatlog.py +++ b/combatlog/serializers/combatlog.py @@ -1,4 +1,4 @@ -""" CombatLog Serializers """ +"""CombatLog Serializers""" from rest_framework import serializers @@ -23,6 +23,12 @@ class CombatLogUploadSerializer(serializers.Serializer): file = serializers.FileField() +class CombatLogUploadV2Serializer(serializers.Serializer): + """CombatLog Upload Serializer""" + + file = serializers.FileField() + + class CombatLogUploadResponseSerializer(serializers.Serializer): """CombatLog Upload Response Serializer""" @@ -30,3 +36,15 @@ class CombatLogUploadResponseSerializer(serializers.Serializer): updated = serializers.BooleanField() detail = serializers.CharField() value = serializers.FloatField() + + +class CombatLogUploadV2ResponseSerializer(serializers.Serializer): + """CombatLog Upload Response Serializer""" + + results = CombatLogUploadResponseSerializer( + many=True, + required=False, + allow_null=True, + ) + combatlog = serializers.IntegerField(required=False, allow_null=True) + detail = serializers.CharField() diff --git a/combatlog/views/combatlog.py b/combatlog/views/combatlog.py index 033ce95..9dfac5a 100644 --- a/combatlog/views/combatlog.py +++ b/combatlog/views/combatlog.py @@ -1,4 +1,4 @@ -""" CombatLog Views """ +"""CombatLog Views""" import logging @@ -14,11 +14,11 @@ from rest_framework.viewsets import GenericViewSet from combatlog.models import CombatLog -from combatlog.serializers import ( - CombatLogSerializer, - CombatLogUploadResponseSerializer, - CombatLogUploadSerializer, -) +from combatlog.serializers import (CombatLogSerializer, + CombatLogUploadResponseSerializer, + CombatLogUploadSerializer, + CombatLogUploadV2ResponseSerializer, + CombatLogUploadV2Serializer) from core.pagination import PageNumberPagination LOGGER = logging.getLogger("django") @@ -65,6 +65,50 @@ def upload(self, request): return Response(serializer.data) + @swagger_auto_schema( + responses={200: CombatLogUploadV2ResponseSerializer()}, + ) + @action( + detail=False, + methods=["POST"], + serializer_class=CombatLogUploadV2Serializer, + parser_classes=(MultiPartParser,), + permission_classes=(), + ) + def uploadv2(self, request): + """ + Combat Log Upload + + Uploads a Combat Log for analysis. + """ + + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + + data = serializer.validated_data["file"].read() + + try: + with transaction.atomic(): + instance = CombatLog.objects.create() + res = instance.update_metadata(data) + results = CombatLogUploadResponseSerializer(data=res, many=True) + results.is_valid(raise_exception=True) + except Exception as e: + serializer = CombatLogUploadV2ResponseSerializer(data={"detail": str(e)}) + serializer.is_valid(raise_exception=True) + return Response(serializer.data) + + serializer = CombatLogUploadV2ResponseSerializer( + data={ + "results": results.data, + "combatlog": instance.pk, + "detail": "Combatlog uploaded successfully.", + } + ) + serializer.is_valid(raise_exception=True) + + return Response(serializer.data) + @swagger_auto_schema( responses={ "200": openapi.Response( diff --git a/ladder/fixtures/ladders.json b/ladder/fixtures/ladders.json index 42728d6..f347bc1 100644 --- a/ladder/fixtures/ladders.json +++ b/ladder/fixtures/ladders.json @@ -125,5 +125,33 @@ "internal_name": "Nukara Prime: Transdimensional Tactics", "internal_difficulty": null } + }, + { + "model": "ladder.Ladder", + "pk": 10, + "fields": { + "name": "Infected: The Conduit", + "difficulty": "Any", + "metric": "DPS", + "variant": "Default", + "is_solo": false, + "is_space": true, + "internal_name": "Infected Space", + "internal_difficulty": null + } + }, + { + "model": "ladder.Ladder", + "pk": 11, + "fields": { + "name": "Hive: Onslaught", + "difficulty": "Any", + "metric": "DPS", + "variant": "Default", + "is_solo": false, + "is_space": true, + "internal_name": "Hive Space", + "internal_difficulty": null + } } ] diff --git a/ladder/fixtures/ladders_solo.json b/ladder/fixtures/ladders_solo.json index 5227f78..2c43b91 100644 --- a/ladder/fixtures/ladders_solo.json +++ b/ladder/fixtures/ladders_solo.json @@ -125,5 +125,33 @@ "internal_name": "Nukara Prime: Transdimensional Tactics", "internal_difficulty": null } + }, + { + "model": "ladder.Ladder", + "pk": 1010, + "fields": { + "name": "Infected: The Conduit", + "difficulty": "Any", + "metric": "DPS", + "variant": "Default", + "is_solo": true, + "is_space": true, + "internal_name": "Infected Space", + "internal_difficulty": null + } + }, + { + "model": "ladder.Ladder", + "pk": 1011, + "fields": { + "name": "Hive: Onslaught", + "difficulty": "Any", + "metric": "DPS", + "variant": "Default", + "is_solo": true, + "is_space": true, + "internal_name": "Hive Space", + "internal_difficulty": null + } } ] diff --git a/ladder/models/ladder.py b/ladder/models/ladder.py index 8ef9468..950a361 100644 --- a/ladder/models/ladder.py +++ b/ladder/models/ladder.py @@ -52,4 +52,4 @@ def create_variants(self): self.create_variant(variant) def __str__(self): - return f"{'[Solo]' if self.is_solo else ''} ({self.variant.name}) {self.name} {self.difficulty} {self.metric}" + return f"{'[Solo]' if self.is_solo else ''} ({self.variant.name}) {self.name} ({self.difficulty} Difficulty) {self.metric}" diff --git a/requirements.txt b/requirements.txt index c41ba61..3bf16a6 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,7 @@ Django==4.2.9 djangorestframework==3.14.0 drf_yasg==1.21.7 django_filter==23.5 -STO-OSCR>=2024.7b90 +STO-OSCR>=2024.9b20 whitenoise==6.6.0 psycopg[binary,pool]==3.1.18 requests>=2.31.0 diff --git a/shell.nix b/shell.nix index 04cd045..a9961aa 100644 --- a/shell.nix +++ b/shell.nix @@ -6,6 +6,8 @@ in pkgs.mkShell rec { name = "impurePythonEnv"; venvDir = "./.venv"; buildInputs = [ + openapi-generator-cli + # A Python interpreter including the 'venv' module is required to bootstrap # the environment. pythonPackages.python @@ -18,6 +20,8 @@ in pkgs.mkShell rec { # add them to PYTHONPATH and thus make them accessible from within the venv. # pythonPackages.pyside6 pythonPackages.numpy + pythonPackages.twine + pythonPackages.build # In this particular example, in order to compile any binary extensions they may # require, the Python modules listed in the hypothetical requirements.txt need