diff --git a/beat/web/attestations/admin.py b/beat/web/attestations/admin.py index fcbe8d0f38814706e54298e39a36731fa111e082..97c86bdd974a7d78f00b79e092f6dea7c1361ce6 100644 --- a/beat/web/attestations/admin.py +++ b/beat/web/attestations/admin.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python # vim: set fileencoding=utf-8 : +# encoding: utf-8 ############################################################################### # # @@ -25,23 +25,32 @@ # # ############################################################################### -from .models import Attestation as AttestationModel from django.contrib import admin +from .models import Attestation as AttestationModel class Attestation(admin.ModelAdmin): - list_display = ('id', 'number', 'experiment', 'locked', 'creation_date', 'expiration_date', 'publication_date',) - search_fields = [ - 'number', - 'experiment__name', - 'experiment__author__username', - 'experiment__toolchain__author__username', - 'experiment__toolchain__name', - 'experiment__toolchain__version', + list_display = ( + "id", + "number", + "experiment", + "locked", + "creation_date", + "expiration_date", + "publication_date", + ) + search_fields = [ + "number", + "experiment__name", + "experiment__author__username", + "experiment__toolchain__author__username", + "experiment__toolchain__name", + "experiment__toolchain__version", ] - list_filter = ('locked', ) - list_display_links = ('id', 'number') + list_filter = ("locked",) + list_display_links = ("id", "number") + admin.site.register(AttestationModel, Attestation) diff --git a/beat/web/attestations/api.py b/beat/web/attestations/api.py index 79845ea4fda891e03585b4a74e6c1f41b08c4973..11daf8145f9f6a2239b2081b2ed6b27e5caef2a7 100644 --- a/beat/web/attestations/api.py +++ b/beat/web/attestations/api.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python # vim: set fileencoding=utf-8 : +# encoding: utf-8 ############################################################################### # # @@ -27,83 +27,73 @@ from django.conf import settings from django.shortcuts import get_object_or_404 - - -from rest_framework.response import Response -from rest_framework import permissions from rest_framework import generics +from rest_framework import permissions from rest_framework import status +from rest_framework.response import Response from rest_framework.views import APIView -from datetime import datetime - -from .models import Attestation -from .serializers import AttestationSerializer -from .exceptions import AlreadyUnlockedError - -from ..experiments.models import Experiment - -from ..common.models import Shareable -from ..common.exceptions import ShareError -from ..common.responses import BadRequestResponse, ForbiddenResponse - import beat.core.experiment import beat.core.toolchain -import simplejson as json - +from ..common.exceptions import ShareError +from ..common.responses import BadRequestResponse +from ..common.responses import ForbiddenResponse +from ..experiments.models import Experiment +from .exceptions import AlreadyUnlockedError +from .models import Attestation +from .serializers import AttestationSerializer -#---------------------------------------------------------- +# ---------------------------------------------------------- class CreateAttestationView(APIView): """ Create a new attestation """ + permission_classes = [permissions.IsAuthenticated] def post(self, request): data = request.data - if 'experiment' not in data: - return BadRequestResponse('Missing field: experiment') + if "experiment" not in data: + return BadRequestResponse("Missing field: experiment") - if not(isinstance(data['experiment'], str)): - return BadRequestResponse('Invalid field type: "experiment" must be a string') + if not (isinstance(data["experiment"], str)): + return BadRequestResponse( + 'Invalid field type: "experiment" must be a string' + ) - declaration_id = beat.core.experiment.Storage(settings.PREFIX, - data['experiment']) + declaration_id = beat.core.experiment.Storage( + settings.PREFIX, data["experiment"] + ) # Check that the user is the author of the experiment if declaration_id.username != request.user.username: return ForbiddenResponse() - # Retrieve the experiment - try: - toolchain_id = beat.core.toolchain.Storage(settings.PREFIX, - declaration_id.toolchain) - experiment = Experiment.objects.get( - author__username=declaration_id.username, - toolchain__author__username=toolchain_id.username, - toolchain__name=toolchain_id.name, - toolchain__version=toolchain_id.version, - name=declaration_id.name - ) - except: - return Response(status=404) - + toolchain_id = beat.core.toolchain.Storage( + settings.PREFIX, declaration_id.toolchain + ) + experiment = get_object_or_404( + Experiment, + author__username=declaration_id.username, + toolchain__author__username=toolchain_id.username, + toolchain__name=toolchain_id.name, + toolchain__version=toolchain_id.version, + name=declaration_id.name, + ) # Check that an attestation doesn't already exists for that experiment if experiment.has_attestation(): - return BadRequestResponse('This experiment already has an attestation') - + return BadRequestResponse("This experiment already has an attestation") # Check that the experiment is done if experiment.end_date is None: return BadRequestResponse("This experiment isn't done yet") - # Retrieve and process the list of algorithms referenced by the declaration, and # their referenced data formats needed_formats = [] @@ -115,10 +105,10 @@ class CreateAttestationView(APIView): needed_formats = list(set(needed_formats)) needed_algorithms = list(set(needed_algorithms)) - # Process the list of referenced dataformats - other_needed_formats = filter(lambda x: x.author != experiment.author, needed_formats) - + other_needed_formats = filter( + lambda x: x.author != experiment.author, needed_formats + ) # Ensure that all needed dataformats from other users have the necessary sharing # preferences @@ -129,11 +119,10 @@ class CreateAttestationView(APIView): if len(errors) > 0: return Response(errors, status=400) - # Process the list of referenced algorithms - own_needed_algorithms = filter(lambda x: x.author == experiment.author, needed_algorithms) - other_needed_algorithms = filter(lambda x: x.author != experiment.author, needed_algorithms) - + other_needed_algorithms = filter( + lambda x: x.author != experiment.author, needed_algorithms + ) # Ensure that all needed algorithms from other users have the necessary sharing # preferences @@ -157,37 +146,37 @@ class CreateAttestationView(APIView): # Send the result result = { - 'number': attestation.number, + "number": attestation.number, } response = Response(result, status=201) return response -#---------------------------------------------------------- +# ---------------------------------------------------------- class UnlockAttestationView(APIView): """ Unlock a "locked" attestation """ + permission_classes = [permissions.IsAuthenticated] def post(self, request, number): data = request.data - if 'visible_algorithms' in data: - if not(isinstance(data['visible_algorithms'], list)): - return BadRequestResponse('Invalid visible_algorithms data') + if "visible_algorithms" in data: + if not (isinstance(data["visible_algorithms"], list)): + return BadRequestResponse("Invalid visible_algorithms data") - if len(data['visible_algorithms']) > 0: - visible_algorithms_names = data['visible_algorithms'] + if len(data["visible_algorithms"]) > 0: + visible_algorithms_names = data["visible_algorithms"] else: visible_algorithms_names = [] else: visible_algorithms_names = [] - # Retrieve the attestation attestation = get_object_or_404(Attestation, number=number) @@ -199,26 +188,31 @@ class UnlockAttestationView(APIView): try: attestation.unlock(visible_algorithms_names=visible_algorithms_names) except AlreadyUnlockedError: - return BadRequestResponse('This attestation is already unlocked') + return BadRequestResponse("This attestation is already unlocked") except ShareError as e: return Response(e.errors, status=400) return Response(status=204) -#---------------------------------------------------------- +# ---------------------------------------------------------- class ListUserAttestationView(generics.ListAPIView): """ List all attestations from a given user """ + permission_classes = [permissions.AllowAny] serializer_class = AttestationSerializer def get(self, request, username): # Retrieve all the attestations of the specified user - attestations = Attestation.objects.select_related().filter(experiment__author__username=username).order_by('-creation_date') + attestations = ( + Attestation.objects.select_related() + .filter(experiment__author__username=username) + .order_by("-creation_date") + ) if username != request.user.username: attestations = attestations.exclude(locked=True) @@ -227,13 +221,14 @@ class ListUserAttestationView(generics.ListAPIView): return Response(serializer.data) -#---------------------------------------------------------- +# ---------------------------------------------------------- class DeleteAttestationView(APIView): """ Delete given attestation if locked """ + permission_classes = [permissions.IsAuthenticated] def delete(self, request, number): diff --git a/beat/web/attestations/api_urls.py b/beat/web/attestations/api_urls.py index 2655025d274f7f3d86a19a15f6238ebbeec1fba4..0435bcef8a114a3c95af5b76e7bd88fc70d38e3c 100644 --- a/beat/web/attestations/api_urls.py +++ b/beat/web/attestations/api_urls.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python # vim: set fileencoding=utf-8 : +# encoding: utf-8 ############################################################################### # # diff --git a/beat/web/attestations/apps.py b/beat/web/attestations/apps.py index fd30640f9db8ca6069e12ddf38eda40f82bf05f5..459b40ac57ed164654b25b54a13e3fde70e64ce2 100644 --- a/beat/web/attestations/apps.py +++ b/beat/web/attestations/apps.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python # vim: set fileencoding=utf-8 : +# encoding: utf-8 ############################################################################### # # @@ -25,15 +25,19 @@ # # ############################################################################### -from ..common.apps import CommonAppConfig from django.utils.translation import ugettext_lazy as _ +from ..common.apps import CommonAppConfig + + class AttestationsConfig(CommonAppConfig): - name = 'beat.web.attestations' - verbose_name = _('Attestations') + name = "beat.web.attestations" + verbose_name = _("Attestations") def ready(self): super(AttestationsConfig, self).ready() - from .signals.handlers import on_unlocked from actstream import registry - registry.register(self.get_model('Attestation')) + + from .signals.handlers import on_unlocked # noqa: F401 + + registry.register(self.get_model("Attestation")) diff --git a/beat/web/attestations/exceptions.py b/beat/web/attestations/exceptions.py index fe2c945f7040efcb05b15659a9b52827ad7af7c4..567a595a8132f2cc0829f235ab3280646f17e2e4 100644 --- a/beat/web/attestations/exceptions.py +++ b/beat/web/attestations/exceptions.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python # vim: set fileencoding=utf-8 : +# encoding: utf-8 ############################################################################### # # @@ -28,8 +28,8 @@ """ Attestation specific exceptions """ - from ..common.exceptions import BeatWebError + class AlreadyUnlockedError(BeatWebError): pass diff --git a/beat/web/attestations/models.py b/beat/web/attestations/models.py index 1bb9d58f974660fa3657c6956c07166c82fdb2cf..a288f291bc49da98b69d0b93eeb1654ecc593752 100644 --- a/beat/web/attestations/models.py +++ b/beat/web/attestations/models.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python # vim: set fileencoding=utf-8 : +# encoding: utf-8 ############################################################################### # # @@ -25,44 +25,46 @@ # # ############################################################################### -from django.db import models +import random +from datetime import datetime +from datetime import timedelta + from django.conf import settings +from django.db import models from django.urls import reverse -from ..experiments.models import Experiment -from ..toolchains.models import Toolchain from ..algorithms.models import Algorithm from ..dataformats.models import DataFormat - -from .signals import unlocked +from ..experiments.models import Experiment +from ..toolchains.models import Toolchain from .exceptions import AlreadyUnlockedError +from .signals import unlocked -import random -from datetime import datetime, timedelta class AttestationManager(models.Manager): - def get_by_natural_key(self, number): return self.get(number=number) def create_attestation(self, experiment): # Generate a unique attestation number - used_numbers = [attestation.number for attestation in self.model.objects.all()] + used_numbers = self.model.objects.values_list("number", flat=True) number = 0 while (number == 0) or number in used_numbers: - number = random.randint(100000, 2**31) + number = random.randint(100000, 2 ** 31) # nosec creation_date = datetime.now() expiration_date = creation_date + timedelta(days=settings.EXPIRATION_DELTA) - attestation = self.model(number=number, - experiment=experiment, - toolchain=experiment.toolchain, - locked=True, - creation_date=creation_date, - expiration_date=expiration_date, - publication_date=None) + attestation = self.model( + number=number, + experiment=experiment, + toolchain=experiment.toolchain, + locked=True, + creation_date=creation_date, + expiration_date=expiration_date, + publication_date=None, + ) attestation.save() return attestation @@ -76,67 +78,78 @@ class AttestationManager(models.Manager): class Attestation(models.Model): - number = models.IntegerField() - experiment = models.OneToOneField(Experiment, related_name='attestation', on_delete=models.CASCADE) - locked = models.BooleanField(default=True) - creation_date = models.DateTimeField() + number = models.IntegerField() + experiment = models.OneToOneField( + Experiment, related_name="attestation", on_delete=models.CASCADE + ) + locked = models.BooleanField(default=True) + creation_date = models.DateTimeField() publication_date = models.DateTimeField(null=True, blank=True) expiration_date = models.DateTimeField(null=True, blank=True) - toolchain = models.ForeignKey(Toolchain, related_name='attestations', - null=True, blank=True, on_delete=models.CASCADE) - dataformats = models.ManyToManyField(DataFormat, related_name='attestations', - blank=True) - algorithms = models.ManyToManyField(Algorithm, related_name='attestations', - blank=True) - + toolchain = models.ForeignKey( + Toolchain, + related_name="attestations", + null=True, + blank=True, + on_delete=models.CASCADE, + ) + dataformats = models.ManyToManyField( + DataFormat, related_name="attestations", blank=True + ) + algorithms = models.ManyToManyField( + Algorithm, related_name="attestations", blank=True + ) objects = AttestationManager() def __str__(self): - return "Attestation #%d, for experiment %s" % (self.number, self.experiment.fullname()) + return "Attestation #%d, for experiment %s" % ( + self.number, + self.experiment.fullname(), + ) def natural_key(self): return (self.number,) def get_absolute_url(self): - return reverse( - 'attestations:view', - args=( - self.number, - ), - ) + return reverse("attestations:view", args=(self.number,),) def modifiable(self): - return not(self.locked) and super(Attestation, self).modifiable() + return not (self.locked) and super(Attestation, self).modifiable() def deletable(self): - return not(self.locked) and super(Attestation, self).deletable() + return not (self.locked) and super(Attestation, self).deletable() def list_of_referenced_formats(self): - s = '[' + s = "[" for dataformat in self.dataformats.all(): s += "'%s'," % dataformat.fullname() - if s[-1] == ',': + if s[-1] == ",": s = s[:-1] - return s + ']' + return s + "]" def share(self, visible_algorithms_names): # Build algorithms sharing information - own_needed_algorithms = self.experiment.referenced_algorithms.filter(author=self.experiment.author) + own_needed_algorithms = self.experiment.referenced_algorithms.filter( + author=self.experiment.author + ) algorithms_infos = {} - visible_algorithms = filter(lambda x: x.fullname() in visible_algorithms_names, - own_needed_algorithms) + visible_algorithms = filter( + lambda x: x.fullname() in visible_algorithms_names, own_needed_algorithms + ) for visible_algorithm in visible_algorithms: - algorithms_infos[visible_algorithm.fullname()] = {'opensource': False} + algorithms_infos[visible_algorithm.fullname()] = {"opensource": False} - public_algorithms = filter(lambda x: x.fullname() not in visible_algorithms_names, - own_needed_algorithms) + public_algorithms = filter( + lambda x: x.fullname() not in visible_algorithms_names, + own_needed_algorithms, + ) for public_algorithm in public_algorithms: - algorithms_infos[public_algorithm.fullname()] = {'opensource': True} + algorithms_infos[public_algorithm.fullname()] = {"opensource": True} # Share the experiment self.experiment.share(algorithms_infos=algorithms_infos) @@ -146,7 +159,7 @@ class Attestation(models.Model): # Share the experiment self.share(visible_algorithms_names=visible_algorithms_names) # Unlock the attestation - self.locked = False + self.locked = False self.publication_date = datetime.now() self.save() unlocked.send(self) diff --git a/beat/web/attestations/serializers.py b/beat/web/attestations/serializers.py index 439845ffe7b55da1a258b10de07fbf3e128a2be9..61888ed23469b7f4fbfd47390722aad15438815b 100644 --- a/beat/web/attestations/serializers.py +++ b/beat/web/attestations/serializers.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python # vim: set fileencoding=utf-8 : +# encoding: utf-8 ############################################################################### # # @@ -38,7 +38,14 @@ class AttestationSerializer(serializers.ModelSerializer): class Meta: model = Attestation - fields = ['number', 'experiment', 'locked', - 'creation_date', 'expiration_date', 'publication_date', - 'toolchain', - 'nb_dataformats', 'nb_algorithms'] + fields = [ + "number", + "experiment", + "locked", + "creation_date", + "expiration_date", + "publication_date", + "toolchain", + "nb_dataformats", + "nb_algorithms", + ] diff --git a/beat/web/attestations/tests.py b/beat/web/attestations/tests.py index e4c29f5ad0af84199c0eb7db574b1fea8b6b517c..efa96ec689ddde708fe957eefc4cdee39adbd945 100755 --- a/beat/web/attestations/tests.py +++ b/beat/web/attestations/tests.py @@ -1,5 +1,5 @@ -#!/usr/bin/env python # vim: set fileencoding=utf-8 : +# encoding: utf-8 ############################################################################### # # @@ -27,27 +27,26 @@ import io import os -import simplejson as json import shutil -from datetime import datetime, timedelta +from datetime import datetime +from datetime import timedelta -from django.contrib.auth.models import User +import simplejson as json from django.conf import settings -from django.urls import reverse - +from django.contrib.auth.models import User from django.core.management import call_command +from django.urls import reverse -from ..experiments.models import Experiment from ..algorithms.models import Algorithm +from ..backend.models import Environment +from ..backend.models import Queue from ..common.models import Shareable -from ..backend.models import Environment, Queue -from ..dataformats.models import DataFormat -from ..toolchains.models import Toolchain -from ..databases.models import Database - from ..common.testutils import BaseTestCase from ..common.testutils import tearDownModule # noqa test runner will call it - +from ..databases.models import Database +from ..dataformats.models import DataFormat +from ..experiments.models import Experiment +from ..toolchains.models import Toolchain from .models import Attestation TEST_PWD = "1234" diff --git a/beat/web/attestations/views.py b/beat/web/attestations/views.py index 62d10e536c612962eaad659349b70a8f64fcb619..540a47527dd3cd8e4e4d574a55062e9fd7e573ed 100644 --- a/beat/web/attestations/views.py +++ b/beat/web/attestations/views.py @@ -1,6 +1,4 @@ -#!/usr/bin/env python # vim: set fileencoding=utf-8 : -#!/usr/bin/env python # encoding: utf-8 ############################################################################### @@ -30,10 +28,10 @@ # Mon 14 Sep 13:15:34 CEST 2015 -from django.shortcuts import get_object_or_404 -from django.shortcuts import render from django.contrib.auth.models import User from django.db.models import Q +from django.shortcuts import get_object_or_404 +from django.shortcuts import render from .models import Attestation @@ -45,54 +43,53 @@ def view(request, number): (has_access, _) = attestation.experiment.accessibility_for(request.user) # Render the page - return render(request, - 'attestations/view.html', - dict( - attestation=attestation, - has_access=has_access, - owner=(attestation.experiment.author == request.user) - )) + return render( + request, + "attestations/view.html", + dict( + attestation=attestation, + has_access=has_access, + owner=(attestation.experiment.author == request.user), + ), + ) def ls(request, author_name): - '''List all accessible attestations to the request user''' + """List all accessible attestations to the request user""" - if not author_name: return public_ls(request) + if not author_name: + return public_ls(request) # check that the user exists on the system author = get_object_or_404(User, username=author_name) if request.user == author: # list all of the users attestations - objects = Attestation.objects.filter(Q(experiment__author=author) | \ - Q(locked=False)) + objects = Attestation.objects.filter( + Q(experiment__author=author) | Q(locked=False) + ) else: # list all unlocked (public) attestations from a given user - objects = Attestation.objects.filter(experiment__author=author, - locked=False) + objects = Attestation.objects.filter(experiment__author=author, locked=False) - objects = objects.order_by('-locked', 'expiration_date') + objects = objects.order_by("-locked", "expiration_date") - return render(request, - 'attestations/list.html', - dict( - objects=objects, - author=author, - owner=(request.user == author) - )) + return render( + request, + "attestations/list.html", + dict(objects=objects, author=author, owner=(request.user == author)), + ) def public_ls(request): - '''List all accessible attestations to any user''' + """List all accessible attestations to any user""" # orders so that recent objects are displayed first - objects = Attestation.objects.filter(locked=False).order_by('-publication_date') - - return render(request, - 'attestations/list.html', - dict( - objects=objects, - author=request.user, # anonymous - owner=False, - )) + objects = Attestation.objects.filter(locked=False).order_by("-publication_date") + + return render( + request, + "attestations/list.html", + dict(objects=objects, author=request.user, owner=False,), # anonymous + )