[Patchew-devel] [PATCH 08/10] complete switch to database-based Results

Paolo Bonzini pbonzini at redhat.com
Wed Jun 13 10:37:42 UTC 2018


All results are stored in the database now and there is no need for the
ResultTuple infrastructure.  Remove all the code invoking the
rest_results_hook and switch ResultsViewSet and ResultSerializer to
be model-based.

Signed-off-by: Paolo Bonzini <pbonzini at redhat.com>
---
 api/models.py   | 46 ---------------------------------------
 api/rest.py     | 58 ++++++++++++++++---------------------------------
 mods/git.py     |  5 +----
 mods/testing.py |  5 +----
 www/views.py    | 15 +++++--------
 5 files changed, 26 insertions(+), 103 deletions(-)

diff --git a/api/models.py b/api/models.py
index 6288d8d..3ca87a6 100644
--- a/api/models.py
+++ b/api/models.py
@@ -16,7 +16,6 @@ import re
 
 from django.core import validators
 from django.db import models
-from django.db.models import Q
 from django.contrib.auth.models import User
 from django.urls import reverse
 import jsonfield
@@ -125,14 +124,6 @@ class Result(models.Model):
             log_url = request.build_absolute_uri(log_url)
         return log_url
 
-    @staticmethod
-    def get_result_tuples(obj, module, results):
-        name_filter = Q(name=module) | Q(name__startswith=module + '.')
-        renderer = mod.get_module(module)
-        for r in obj.results.filter(name_filter):
-            results.append(ResultTuple(name=r.name, obj=obj, status=r.status,
-                                       log=r.log, data=r.data, renderer=renderer))
-
     def __str__(self):
         return '%s (%s)' % (self.name, self.status)
 
@@ -756,40 +747,3 @@ class Module(models.Model):
 
     def __str__(self):
         return self.name
-
-class ResultTuple(namedtuple("ResultTuple", "name status log obj data renderer")):
-    __slots__ = ()
-
-    def __new__(cls, name, status, obj, log=None, data=None, renderer=None):
-        if status not in Result.VALID_STATUSES:
-            raise ValueError("invalid value '%s' for status field" % status)
-        return super(cls, ResultTuple).__new__(cls, status=status, log=log,
-                                          obj=obj, data=data, name=name, renderer=renderer)
-
-    def is_success(self):
-        return self.status == Result.SUCCESS
-
-    def is_failure(self):
-        return self.status == Result.FAILURE
-
-    def is_completed(self):
-        return self.is_success() or self.is_failure()
-
-    def is_pending(self):
-        return self.status == Result.PENDING
-
-    def is_running(self):
-        return self.status == Result.RUNNING
-
-    def render(self):
-        if self.renderer is None:
-            return None
-        return self.renderer.render_result(self)
-
-    def get_log_url(self, request=None):
-        if not self.is_completed() or self.renderer is None:
-            return None
-        log_url = self.renderer.get_result_log_url(self)
-        if log_url is not None and request is not None:
-            log_url = request.build_absolute_uri(log_url)
-        return log_url
diff --git a/api/rest.py b/api/rest.py
index f853964..9ec0ae8 100644
--- a/api/rest.py
+++ b/api/rest.py
@@ -14,7 +14,7 @@ from django.http import Http404
 from django.template import loader
 
 from mod import dispatch_module_hook
-from .models import Project, Message
+from .models import Project, ProjectResult, Message, MessageResult, Result
 from .search import SearchEngine
 from rest_framework import (permissions, serializers, viewsets, filters,
     mixins, generics, renderers, status)
@@ -446,10 +446,12 @@ class HyperlinkedResultField(HyperlinkedIdentityField):
             kwargs['projects_pk'] = obj.id
         return self.reverse(view_name, kwargs=kwargs, request=request, format=format)
 
-class ResultSerializer(serializers.Serializer):
+class ResultSerializer(serializers.ModelSerializer):
+    class Meta:
+        model = Result
+        fields = ('resource_uri', 'name', 'status', 'last_update', 'data', 'log_url')
+
     resource_uri = HyperlinkedResultField(view_name='results-detail')
-    name = CharField()
-    status = CharField() # one of 'failure', 'success', 'pending', 'running'
     log_url = SerializerMethodField(required=False)
     data = JSONField(required=False)
 
@@ -458,53 +460,31 @@ class ResultSerializer(serializers.Serializer):
         return obj.get_log_url(request)
 
 class ResultSerializerFull(ResultSerializer):
+    class Meta:
+        model = Result
+        fields = ResultSerializer.Meta.fields + ('log',)
+
+    # The database field is log_xz, so this is needed here
     log = CharField(required=False)
 
-class ResultsViewSet(viewsets.ViewSet, generics.GenericAPIView):
+class ResultsViewSet(mixins.ListModelMixin, mixins.RetrieveModelMixin,
+                         viewsets.GenericViewSet):
     lookup_field = 'name'
     lookup_value_regex = '[^/]+'
-    permission_classes = (PatchewPermission,)
+    filter_backends = (filters.OrderingFilter,)
+    ordering_fields = ('name',)
+    ordering = ('name',)
 
     def get_serializer_class(self, *args, **kwargs):
         if self.lookup_field in self.kwargs:
             return ResultSerializerFull
         return ResultSerializer
 
-    def get_results(self, detailed):
-        queryset = self.get_queryset()
-        try:
-            obj = queryset[0]
-        except IndexError:
-            raise Http404
-        results = []
-        dispatch_module_hook("rest_results_hook", obj=obj, results=results,
-                             detailed=detailed)
-        return {x.name: x for x in results}
-
-    def list(self, request, *args, **kwargs):
-        results = self.get_results(detailed=False).values()
-        serializer = self.get_serializer(results, many=True)
-        # Fake paginator response for forwards-compatibility, in case
-        # this ViewSet becomes model-based
-        return Response(OrderedDict([
-            ('count', len(results)),
-            ('results', serializer.data)
-        ]))
-
-    def retrieve(self, request, name, *args, **kwargs):
-        results = self.get_results(detailed=True)
-        try:
-            result = results[name]
-        except KeyError:
-            raise Http404
-        serializer = self.get_serializer(result)
-        return Response(serializer.data)
-
 class ProjectResultsViewSet(ResultsViewSet):
     def get_queryset(self):
-        return Project.objects.filter(id=self.kwargs['projects_pk'])
+        return ProjectResult.objects.filter(project=self.kwargs['projects_pk'])
 
 class SeriesResultsViewSet(ResultsViewSet):
     def get_queryset(self):
-        return Message.objects.filter(project=self.kwargs['projects_pk'],
-                                      message_id=self.kwargs['series_message_id'])
+        return MessageResult.objects.filter(message__project=self.kwargs['projects_pk'],
+                                            message__message_id=self.kwargs['series_message_id'])
diff --git a/mods/git.py b/mods/git.py
index b8c2ff0..2b24b24 100644
--- a/mods/git.py
+++ b/mods/git.py
@@ -18,7 +18,7 @@ from django.core.exceptions import PermissionDenied
 from django.utils.html import format_html
 from mod import PatchewModule
 from event import declare_event, register_handler, emit_event
-from api.models import Message, MessageProperty, Project, Result, ResultTuple
+from api.models import Message, MessageProperty, Project, Result
 from api.rest import PluginMethodField
 from api.views import APILoginRequiredView, prepare_series
 from patchew.logviewer import LogView
@@ -134,9 +134,6 @@ class GitModule(PatchewModule):
     def rest_series_fields_hook(self, request, fields, detailed):
         fields['based_on'] = PluginMethodField(obj=self, required=False)
 
-    def rest_results_hook(self, obj, results, detailed=False):
-        Result.get_result_tuples(obj, "git", results)
-
     def prepare_message_hook(self, request, message, detailed):
         if not message.is_series_head:
             return
diff --git a/mods/testing.py b/mods/testing.py
index 9c053a7..7dfc5a2 100644
--- a/mods/testing.py
+++ b/mods/testing.py
@@ -19,7 +19,7 @@ import time
 import math
 from api.views import APILoginRequiredView
 from api.models import (Message, MessageProperty, MessageResult,
-        Project, ProjectResult, Result, ResultTuple)
+        Project, ProjectResult, Result)
 from api.search import SearchEngine
 from event import emit_event, declare_event, register_handler
 from patchew.logviewer import LogView
@@ -272,9 +272,6 @@ class TestingModule(PatchewModule):
                         })
         return ret
 
-    def rest_results_hook(self, obj, results, detailed=False):
-        Result.get_result_tuples(obj, "testing", results)
-
     def prepare_message_hook(self, request, message, detailed):
         if not message.is_series_head:
             return
diff --git a/www/views.py b/www/views.py
index c2010c5..d7c60dd 100644
--- a/www/views.py
+++ b/www/views.py
@@ -92,19 +92,14 @@ def prepare_series(request, s, skip_patches=False):
     return r
 
 def prepare_results(request, obj):
-    results = []
-    dispatch_module_hook("rest_results_hook", obj=obj,
-                         results=results, detailed=False)
-
-    results_dicts = []
-    for result in results:
+    rendered_results = []
+    for result in obj.results.all():
         html = result.render()
         if html is None:
             continue
-        d = result._asdict()
-        d['html'] = html
-        results_dicts.append(d)
-    return results_dicts
+        result.html = html
+        rendered_results.append(result)
+    return rendered_results
 
 def prepare_series_list(request, sl):
     return [prepare_message(request, s.project, s, False) for s in sl]
-- 
2.17.0





More information about the Patchew-devel mailing list