diff --git a/frog_api/models.py b/frog_api/models.py index c7cad5a..45fa967 100644 --- a/frog_api/models.py +++ b/frog_api/models.py @@ -20,7 +20,9 @@ class Version(models.Model): created_on = models.DateTimeField(auto_now_add=True) last_updated = models.DateTimeField(auto_now=True) - project = models.ForeignKey(Project, on_delete=models.CASCADE) + project = models.ForeignKey( + Project, on_delete=models.CASCADE, related_name="versions" + ) slug = models.SlugField(max_length=255) name = models.CharField(max_length=255) diff --git a/frog_api/serializers.py b/frog_api/serializers.py index 3839d73..6144f96 100644 --- a/frog_api/serializers.py +++ b/frog_api/serializers.py @@ -3,16 +3,18 @@ from rest_framework import serializers from frog_api.models import Entry, Project, Version -class ProjectSerializer(serializers.HyperlinkedModelSerializer): - class Meta: - model = Project - fields = ["slug", "name"] - - class VersionSerializer(serializers.HyperlinkedModelSerializer): class Meta: model = Version - fields = ["project", "slug", "name"] + fields = ["slug", "name"] + + +class ProjectSerializer(serializers.HyperlinkedModelSerializer): + versions = VersionSerializer(many=True) + + class Meta: + model = Project + fields = ["slug", "name", "versions"] class EntrySerializer(serializers.HyperlinkedModelSerializer): @@ -28,3 +30,13 @@ class EntrySerializer(serializers.HyperlinkedModelSerializer): "decompiled_bytes", "matching_bytes", ] + + +class TerseEntrySerializer(serializers.HyperlinkedModelSerializer): + class Meta: + model = Entry + fields = [ + "timestamp", + "total_bytes", + "matching_bytes", + ] diff --git a/frog_api/urls.py b/frog_api/urls.py index 2523cf8..890696e 100644 --- a/frog_api/urls.py +++ b/frog_api/urls.py @@ -1,6 +1,22 @@ -from django.urls import path +from django.urls import path, re_path from frog_api import views urlpatterns = [ path("projects/", views.ProjectView.as_view()), + path( + "data/", + views.RootDigestView.as_view(), + ), + re_path( + "data/(?P.+)/$", + views.ProjectDigestView.as_view(), + ), + # re_path( + # "data/(?P.+)/(?P.+)/$", + # views.EntryView.as_view(), + # ), + # re_path( + # "data/(?P.+)/(?P.+)/(?P.+)/$", + # views.EntryView.as_view(), + # ), ] diff --git a/frog_api/views.py b/frog_api/views.py index ed2e151..56853ef 100644 --- a/frog_api/views.py +++ b/frog_api/views.py @@ -1,17 +1,19 @@ -from typing import Any +from typing import Optional +from rest_framework import status from rest_framework.response import Response from rest_framework.views import APIView -from rest_framework import mixins -from rest_framework.request import Request -from rest_framework.viewsets import GenericViewSet -from frog_api.models import Entry, Project -from frog_api.serializers import EntrySerializer, ProjectSerializer +from frog_api.models import Entry, Project, Version +from frog_api.serializers import ( + EntrySerializer, + ProjectSerializer, + TerseEntrySerializer, +) class ProjectView(APIView): """ - API endpoint that allows projects to be viewed or edited. + API endpoint that allows projects to be viewed. """ def get(self, request, format=None): @@ -21,3 +23,65 @@ class ProjectView(APIView): projects = Project.objects.all() serializer = ProjectSerializer(projects, many=True) return Response(serializer.data) + + +def get_latest_entry(project, version, category) -> Optional[Entry]: + return Entry.objects.filter( + category__slug=category, + category__version__slug=version, + category__version__project__slug=project, + ).first() + + +def get_versions_digest_for_project(project) -> dict: + versions = {} + for version in Version.objects.filter(project__slug=project): + entry = get_latest_entry(project, version.slug, "total") + if entry is not None: + versions[version.slug] = TerseEntrySerializer(entry).data + return versions + + +class RootDigestView(APIView): + """ + API endpoint that returns the most recent entry for each version of each project. + """ + + def get(self, request): + """ + Return the most recent entry for ovreall progress for each version of each project. + """ + + projects = {} + for project in Project.objects.all(): + versions = get_versions_digest_for_project(project.slug) + if len(versions) > 0: + projects[project.slug] = versions + + entry = get_latest_entry(project=None, version=None, category="total") + + return Response({"progress": projects}) + + +class ProjectDigestView(APIView): + """ + API endpoint that returns the most recent entry for each version of a project. + """ + + def get(self, request, project): + """ + Return the most recent entry for ovreall progress for each version of a project. + """ + + if Project.objects.get(slug=project) is None: + return Response(status=status.HTTP_404_NOT_FOUND) + + projects = {} + + versions = get_versions_digest_for_project(project) + if len(versions) > 0: + projects[project] = versions + + entry = get_latest_entry(project=None, version=None, category="total") + + return Response({"progress": projects})