From f02be24470a8e01549b8250beb02330563f01e74 Mon Sep 17 00:00:00 2001 From: Samuel Gaist <samuel.gaist@idiap.ch> Date: Fri, 11 Sep 2020 10:50:51 +0200 Subject: [PATCH] [search][all] Pre-commit cleanup --- beat/web/search/admin.py | 170 ++--- beat/web/search/api.py | 48 +- beat/web/search/apps.py | 11 +- beat/web/search/fields.py | 1 + beat/web/search/models.py | 206 +++--- beat/web/search/serializers.py | 90 +-- beat/web/search/signals.py | 30 +- beat/web/search/utils.py | 1093 ++++++++++++++++++-------------- beat/web/search/views.py | 309 +++++---- 9 files changed, 1082 insertions(+), 876 deletions(-) diff --git a/beat/web/search/admin.py b/beat/web/search/admin.py index 2442dc424..eb8773529 100644 --- a/beat/web/search/admin.py +++ b/beat/web/search/admin.py @@ -25,18 +25,18 @@ # # ############################################################################### -from django.contrib import admin from django import forms +from django.contrib import admin from ..common.texts import Messages - -from .models import Search, Leaderboard, Rank -from ..ui.forms import CodeMirrorRSTCharField from ..ui.forms import CodeMirrorJSONCharField +from ..ui.forms import CodeMirrorRSTCharField from ..ui.forms import NameField +from .models import Leaderboard +from .models import Rank +from .models import Search - -#---------------------------------------------------------- +# ---------------------------------------------------------- def rehash_search(modeladmin, request, queryset): @@ -44,64 +44,57 @@ def rehash_search(modeladmin, request, queryset): for q in queryset: q.save() -rehash_search.short_description = 'Rehash selected search' +rehash_search.short_description = "Rehash selected search" -#---------------------------------------------------------- + +# ---------------------------------------------------------- class SearchModelForm(forms.ModelForm): name = NameField( - widget=forms.TextInput(attrs=dict(size=80)), - help_text=Messages['algo_name'], + widget=forms.TextInput(attrs=dict(size=80)), help_text=Messages["algo_name"], ) description = CodeMirrorRSTCharField( - required=False, - help_text=Messages['description'], + required=False, help_text=Messages["description"], ) filters = CodeMirrorJSONCharField( - readonly=False, - required=False, - help_text=Messages['json'], + readonly=False, required=False, help_text=Messages["json"], ) settings = CodeMirrorJSONCharField( - readonly=False, - required=False, - help_text=Messages['json'], + readonly=False, required=False, help_text=Messages["json"], ) class Meta: model = Search exclude = [] widgets = { - 'short_description': forms.TextInput( - attrs=dict(size=100), - ), + "short_description": forms.TextInput(attrs=dict(size=100),), } -#---------------------------------------------------------- +# ---------------------------------------------------------- class SearchAdmin(admin.ModelAdmin): - list_display = ('id', 'author', 'name') - search_fields = [ - 'author__username', - 'name', - 'short_description', - 'description', - 'filters', - 'settings', + list_display = ("id", "author", "name") + search_fields = [ + "author__username", + "name", + "short_description", + "description", + "filters", + "settings", ] - list_display_links = ('id', 'name') + list_display_links = ("id", "name") - list_filter = ('author', 'name', 'version') - readonly_fields = ('hash',) + list_filter = ("author", "name", "version") + readonly_fields = ("hash",) actions = [ rehash_search, @@ -109,87 +102,98 @@ class SearchAdmin(admin.ModelAdmin): form = SearchModelForm - filter_horizontal = [ - 'shared_with', - 'shared_with_team' - ] + filter_horizontal = ["shared_with", "shared_with_team"] fieldsets = ( - (None, - dict( - fields=('name', 'author',), - ), - ), - ('Documentation', - dict( - classes=('collapse',), - fields=('short_description', 'description',), - ), - ), - ('Versioning', - dict( - classes=('collapse',), - fields=('version', 'previous_version', 'fork_of'), - ), - ), - ('Sharing', - dict( - classes=('collapse',), - fields=('sharing', 'shared_with', 'shared_with_team'), - ), - ), - ('Definition', - dict( - fields=('hash', 'filters', 'settings'), - ), - ), + (None, dict(fields=("name", "author",),),), + ( + "Documentation", + dict(classes=("collapse",), fields=("short_description", "description",),), + ), + ( + "Versioning", + dict( + classes=("collapse",), + fields=("version", "previous_version", "fork_of"), + ), + ), + ( + "Sharing", + dict( + classes=("collapse",), + fields=("sharing", "shared_with", "shared_with_team"), + ), + ), + ("Definition", dict(fields=("hash", "filters", "settings"),),), ) + admin.site.register(Search, SearchAdmin) class RankInline(admin.TabularInline): - model = Rank - can_delete = False - extra = 0 - max_num = 0 - readonly_fields = ('id', 'algorithm', 'result', 'order', 'experiment') - ordering = ('algorithm', 'order',) + model = Rank + can_delete = False + extra = 0 + max_num = 0 + readonly_fields = ("id", "algorithm", "result", "order", "experiment") + ordering = ( + "algorithm", + "order", + ) def number_of_experiments(obj): return obj.experiments.count() -number_of_experiments.short_description = 'Experiments' + + +number_of_experiments.short_description = "Experiments" def users_to_notify(obj): return obj.notify.count() -users_to_notify.short_description = 'Subscribed' + + +users_to_notify.short_description = "Subscribed" def search_sharing(obj): return obj.search.get_sharing_display() -search_sharing.short_description = 'Sharing' + + +search_sharing.short_description = "Sharing" class LeaderboardAdmin(admin.ModelAdmin): - list_display = ('id', 'search', 'created', 'updated', users_to_notify, number_of_experiments, search_sharing) + list_display = ( + "id", + "search", + "created", + "updated", + users_to_notify, + number_of_experiments, + search_sharing, + ) search_fiels = [ - 'search__author__username', - 'search__name', - 'search_short_description', - 'search_description', - 'filters', - 'settings', + "search__author__username", + "search__name", + "search_short_description", + "search_description", + "filters", + "settings", ] - list_display_links = ('id', 'search',) + list_display_links = ( + "id", + "search", + ) inlines = [ RankInline, ] filter_horizontal = [ - 'notify', + "notify", ] + admin.site.register(Leaderboard, LeaderboardAdmin) diff --git a/beat/web/search/api.py b/beat/web/search/api.py index a3863c3e1..5d0d4b5ff 100644 --- a/beat/web/search/api.py +++ b/beat/web/search/api.py @@ -26,46 +26,40 @@ ############################################################################### -import simplejson as json - from functools import reduce +import simplejson as json from django.conf import settings from django.contrib.auth.models import User from django.db.models import Q from django.shortcuts import get_object_or_404 - - -from rest_framework.response import Response -from rest_framework.views import APIView -from rest_framework import permissions as drf_permissions from rest_framework import generics +from rest_framework import permissions as drf_permissions from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView from ..algorithms.models import Algorithm +from ..common import permissions as beat_permissions +from ..common.api import ShareView +from ..common.mixins import CommonContextMixin +from ..common.mixins import SerializerFieldsMixin +from ..common.models import Shareable +from ..common.responses import BadRequestResponse +from ..common.utils import ensure_html +from ..common.utils import py3_cmp from ..databases.models import Database from ..dataformats.models import DataFormat from ..experiments.models import Experiment from ..toolchains.models import Toolchain - -from ..common.models import Shareable -from ..common.api import ShareView -from ..common.utils import ensure_html -from ..common.responses import BadRequestResponse -from ..common.mixins import CommonContextMixin, SerializerFieldsMixin -from ..common.utils import py3_cmp -from ..common import permissions as beat_permissions - from ..ui.templatetags.gravatar import gravatar_hash - -from .utils import apply_filter -from .utils import FilterGenerator -from .utils import OR - from .models import Search - -from .serializers import SearchResultSerializer, SearchSerializer, SearchWriteSerializer - +from .serializers import SearchResultSerializer +from .serializers import SearchSerializer +from .serializers import SearchWriteSerializer +from .utils import OR +from .utils import FilterGenerator +from .utils import apply_filter # ------------------------------------------------ @@ -111,9 +105,9 @@ class SearchView(APIView): filters = None display_settings = None - if 'query' in data: - if not(isinstance(data['query'], str)) or (len(data['query']) == 0): - return BadRequestResponse('Invalid query data') + if "query" in data: + if not (isinstance(data["query"], str)) or (len(data["query"]) == 0): + return BadRequestResponse("Invalid query data") query = data["query"] else: diff --git a/beat/web/search/apps.py b/beat/web/search/apps.py index da7e5f4f5..01745b55e 100644 --- a/beat/web/search/apps.py +++ b/beat/web/search/apps.py @@ -25,14 +25,17 @@ # # ############################################################################### -from ..common.apps import CommonAppConfig from django.utils.translation import ugettext_lazy as _ +from ..common.apps import CommonAppConfig + + class SearchConfig(CommonAppConfig): - name = 'beat.web.search' - verbose_name = _('Search') + name = "beat.web.search" + verbose_name = _("Search") def ready(self): super(SearchConfig, self).ready() from actstream import registry - registry.register(self.get_model('Search')) + + registry.register(self.get_model("Search")) diff --git a/beat/web/search/fields.py b/beat/web/search/fields.py index 6bf806d4e..fbf2aa17f 100644 --- a/beat/web/search/fields.py +++ b/beat/web/search/fields.py @@ -27,5 +27,6 @@ from rest_framework import serializers + class DictListField(serializers.ListField): child = serializers.DictField() diff --git a/beat/web/search/models.py b/beat/web/search/models.py index 74c21542c..57611a75f 100644 --- a/beat/web/search/models.py +++ b/beat/web/search/models.py @@ -25,41 +25,40 @@ # # ############################################################################### +import copy +import datetime +import operator + +import simplejson as json +from django.contrib.auth.models import User from django.db import models from django.urls import reverse -from django.contrib.auth.models import User from beat.core.hash import hash -from ..common.texts import Messages -from ..common.models import Contribution -from ..experiments.models import Experiment, Result from ..algorithms.models import Algorithm - -import copy -import operator -import datetime -import simplejson as json +from ..common.models import Contribution +from ..common.texts import Messages +from ..experiments.models import Experiment +from ..experiments.models import Result # date/time for the 1st january 1970, UTC EPOCH = datetime.datetime.utcfromtimestamp(0) class Search(Contribution): - class Meta: - verbose_name_plural = 'searches' - - - filters = models.TextField(default='', blank=True) - settings = models.TextField(default='', blank=True) - description = models.TextField(default='', blank=True, help_text=Messages['description']) + verbose_name_plural = "searches" + filters = models.TextField(default="", blank=True) + settings = models.TextField(default="", blank=True) + description = models.TextField( + default="", blank=True, help_text=Messages["description"] + ) def fullname(self): - return '{}/{}'.format(self.author.username, self.name) - + return "{}/{}".format(self.author.username, self.name) def save(self, *args, **kwargs): # Compute the hash @@ -69,73 +68,61 @@ class Search(Contribution): super(Search, self).save(*args, **kwargs) # If there is a leaderboard, make sure to update it as well - if hasattr(self, 'leaderboard'): - self.leaderboard.save() #force update - + if hasattr(self, "leaderboard"): + self.leaderboard.save() # force update def get_absolute_url(self): - return reverse( - 'search:view', - args=( - self.author.username, - self.name, - ), - ) - + return reverse("search:view", args=(self.author.username, self.name,),) def get_notify_url(self): - return reverse( - 'search:notify', - args=( - self.author.username, - self.name, - ), - ) - + return reverse("search:notify", args=(self.author.username, self.name,),) def get_api_update_url(self): - '''Returns the endpoint to update this object''' - - return reverse( - 'api_search:save', - args=(self.author.username, self.name,), - ) + """Returns the endpoint to update this object""" + return reverse("api_search:save", args=(self.author.username, self.name,),) def get_api_share_url(self): - '''Returns the endpoint to share this object''' - - return reverse( - 'api_search:share', - args=(self.author.username, self.name,), - ) + """Returns the endpoint to share this object""" + return reverse("api_search:share", args=(self.author.username, self.name,),) def has_leaderboard(self): - return hasattr(self, 'leaderboard') + return hasattr(self, "leaderboard") class LeaderboardManager(models.Manager): - def get_by_natural_key(self, username, name, version): - return self.get(search__author__username=username, search__name=name, search__version=version) + return self.get( + search__author__username=username, + search__name=name, + search__version=version, + ) class Leaderboard(models.Model): - '''Keeps track of experiments''' + """Keeps track of experiments""" - search = models.OneToOneField(Search, related_name='leaderboard', on_delete=models.CASCADE) + search = models.OneToOneField( + Search, related_name="leaderboard", on_delete=models.CASCADE + ) created = models.DateTimeField(auto_now_add=True) updated = models.DateTimeField(auto_now=True) changed = models.DateTimeField(default=datetime.datetime.now) - notify = models.ManyToManyField(User, limit_choices_to={'is_active': True}, blank=True, help_text='If set, and the leader board changes, an e-mail notification will be sent to people on this list, every time it changes.') + notify = models.ManyToManyField( + User, + limit_choices_to={"is_active": True}, + blank=True, + help_text="If set, and the leader board changes, an e-mail notification will be sent to people on this list, every time it changes.", + ) - experiments = models.ManyToManyField(Experiment, - through='Rank', - related_name='leaderboards', - blank=True, - help_text='Experiments currently set on the leaderboard', + experiments = models.ManyToManyField( + Experiment, + through="Rank", + related_name="leaderboards", + blank=True, + help_text="Experiments currently set on the leaderboard", ) objects = LeaderboardManager() @@ -145,25 +132,21 @@ class Leaderboard(models.Model): def natural_key(self): return (self.search.author.username, self.search.name, self.search.version) - natural_key.dependencies = ['search.search'] + + natural_key.dependencies = ["search.search"] def get_absolute_url(self): return reverse( - 'search:view', - args=( - self.search.author.username, - self.search.name, - ), + "search:view", args=(self.search.author.username, self.search.name,), ) - def current_experiments(self, time_delta=None): - '''Returns a list of experiments, sorted by criteria in settings + """Returns a list of experiments, sorted by criteria in settings If you specify a ``time_delta``, then we won't consider experiments which are newer than ``now - time_delta``. ``time_delta`` should be set as a ``datetime.timedelta`` object. - ''' + """ # reset experiments from .views import search_experiments @@ -174,14 +157,16 @@ class Leaderboard(models.Model): # creates multiple tables (per common analyzer), with experiments # and existing table-able results (simple numbers) sorted_experiments = [] - for analyzer, blocks in results['common_analyzers']: + for analyzer, blocks in results["common_analyzers"]: table = [] header = [] for block in blocks: - analyzer_output = Result.objects.filter(cache__in=block.outputs.all(), type__in=Result.SIMPLE_TYPE_NAMES).order_by('name') + analyzer_output = Result.objects.filter( + cache__in=block.outputs.all(), type__in=Result.SIMPLE_TYPE_NAMES + ).order_by("name") - if not header: #first row, set order + if not header: # first row, set order header = [k.name for k in analyzer_output] table.append([block.experiment] + list(analyzer_output)) @@ -192,16 +177,17 @@ class Leaderboard(models.Model): ordering = json.loads(self.search.settings) for entry in ordering: - if not entry['analyzers']: continue + if not entry["analyzers"]: + continue # if an analyzer is set, apply ordering to each relevant table - analyzer = entry['analyzers'][0] + analyzer = entry["analyzers"][0] for index, (algorithm, header, table) in enumerate(sorted_experiments): - if analyzer == algorithm.fullname(): #applies the sorting + if analyzer == algorithm.fullname(): # applies the sorting - getters = [] #getters for sorting + getters = [] # getters for sorting tmp_table = copy.deepcopy(table) for row in tmp_table: @@ -210,20 +196,22 @@ class Leaderboard(models.Model): if isinstance(element, Result): row[k] = element.value() # 2. append experiment end date (in seconds) to the end - row.append(-1 * (row[0].end_date-EPOCH).total_seconds()) + row.append(-1 * (row[0].end_date - EPOCH).total_seconds()) # replaces experiments with indexes - for i, row in enumerate(table): tmp_table[i][0] = i + for i, row in enumerate(table): + tmp_table[i][0] = i # get index of columns to use for sorting - for col in entry['columns']: - g = header.index(col['name'].rsplit('.', 1)[1]) + 1 - if not col['ascending']: - for row in tmp_table: row[g] *= -1 + for col in entry["columns"]: + g = header.index(col["name"].rsplit(".", 1)[1]) + 1 + if not col["ascending"]: + for row in tmp_table: + row[g] *= -1 getters.append(g) # if there are matching rows, sort by date (latest first) - getters.append(len(tmp_table[0])-1) + getters.append(len(tmp_table[0]) - 1) # sort table tmp_table and apply results back tmp_table.sort(key=operator.itemgetter(*getters)) @@ -232,22 +220,21 @@ class Leaderboard(models.Model): table = [table[k[0]] for k in tmp_table] # remove unwanted columns - del getters[-1] #remove date element from list - getters.insert(0, 0) #keep experiment pointers + del getters[-1] # remove date element from list + getters.insert(0, 0) # keep experiment pointers for j, row in enumerate(table): - table[j] = [v for k,v in enumerate(row) if k in getters] + table[j] = [v for k, v in enumerate(row) if k in getters] # remove unwanted headers - del getters[0] #not useful for header manipulation - header = [v for k,v in enumerate(header) if k+1 in getters] + del getters[0] # not useful for header manipulation + header = [v for k, v in enumerate(header) if k + 1 in getters] sorted_experiments[index] = (algorithm, header, table) return sorted_experiments - def table(self): - '''Returns the leader board tables for all algorithms''' + """Returns the leader board tables for all algorithms""" retval = [] @@ -255,20 +242,22 @@ class Leaderboard(models.Model): table = [] - for rank in Rank.objects.filter(leaderboard=self, algorithm=algo).distinct().order_by('order'): - results = rank.result.order_by('name') - if not table: #add header - table.append(['experiment'] + [k.name for k in results]) + for rank in ( + Rank.objects.filter(leaderboard=self, algorithm=algo) + .distinct() + .order_by("order") + ): + results = rank.result.order_by("name") + if not table: # add header + table.append(["experiment"] + [k.name for k in results]) table.append([rank.experiment] + [k.value() for k in results]) retval.append((algo, table)) - return retval - def update_experiments(self): - '''Updates internal experiment table, returns ``True`` if changed''' + """Updates internal experiment table, returns ``True`` if changed""" prev_table = self.table() @@ -277,26 +266,27 @@ class Leaderboard(models.Model): # at this point, all tables are sorted, set results for algorithm, header, table in self.current_experiments(): for k, row in enumerate(table): - r = Rank(leaderboard=self, experiment=row[0], - algorithm=algorithm, order=k) + r = Rank( + leaderboard=self, experiment=row[0], algorithm=algorithm, order=k + ) r.save() - for j in row[1:]: r.result.add(j) + for j in row[1:]: + r.result.add(j) return prev_table != self.table() - def save(self, *args, **kwargs): - '''Overload of Django's built-in''' + """Overload of Django's built-in""" super(Leaderboard, self).save(*args, **kwargs) - if self.update_experiments(): #changed + if self.update_experiments(): # changed self.changed = datetime.datetime.now() super(Leaderboard, self).save(*args, **kwargs) class Rank(models.Model): - '''Keeps experiments ordered on the experiments relationship for searches''' + """Keeps experiments ordered on the experiments relationship for searches""" leaderboard = models.ForeignKey(Leaderboard, on_delete=models.CASCADE) experiment = models.ForeignKey(Experiment, on_delete=models.CASCADE) @@ -304,8 +294,10 @@ class Rank(models.Model): order = models.PositiveIntegerField() result = models.ManyToManyField(Result) - def __str__(self): - return '%s: [%d] %s' % (self.leaderboard.search.fullname(), - self.order, self.experiment.fullname()) + return "%s: [%d] %s" % ( + self.leaderboard.search.fullname(), + self.order, + self.experiment.fullname(), + ) diff --git a/beat/web/search/serializers.py b/beat/web/search/serializers.py index 0842ca671..01601e485 100644 --- a/beat/web/search/serializers.py +++ b/beat/web/search/serializers.py @@ -25,24 +25,22 @@ # # ############################################################################### +import simplejson as json from rest_framework import serializers - from ..common.serializers import VersionableSerializer from ..ui.templatetags.markup import restructuredtext - -from .models import Search, Leaderboard from .fields import DictListField - -import simplejson as json +from .models import Leaderboard +from .models import Search class SearchResultSerializer(serializers.Serializer): name = serializers.SerializerMethodField() - description = serializers.CharField(source='short_description') + description = serializers.CharField(source="short_description") def __init__(self, *args, **kwargs): - self.name_field = kwargs.pop('name_field', 'fullname') + self.name_field = kwargs.pop("name_field", "fullname") super(SearchResultSerializer, self).__init__(*args, **kwargs) @@ -65,23 +63,31 @@ class SearchSerializer(VersionableSerializer): class Meta(VersionableSerializer.Meta): model = Search - default_fields = ['name', 'author', 'user_name', 'is_owner', 'accessibility', 'short_description', 'update_url'] + default_fields = [ + "name", + "author", + "user_name", + "is_owner", + "accessibility", + "short_description", + "update_url", + ] def get_user_name(self, obj): - return self.context['user'].username + return self.context["user"].username def get_is_owner(self, obj): - return self.context['user'] == obj.author + return self.context["user"] == obj.author def get_accessibility(self, obj): - (has_access, accessibility) = obj.accessibility_for(self.context['user']) + (has_access, accessibility) = obj.accessibility_for(self.context["user"]) return accessibility def get_html_description(self, obj): d = obj.description if len(d) > 0: return restructuredtext(d) - return '' + return "" def get_update_url(self, obj): return obj.get_api_update_url() @@ -98,53 +104,63 @@ class SearchWriteSerializer(serializers.ModelSerializer): class Meta: model = Search - fields = ['name', 'short_description', 'description', - 'filters', 'settings', 'leaderboard'] + fields = [ + "name", + "short_description", + "description", + "filters", + "settings", + "leaderboard", + ] default_fields = [] def validate_name(self, name): sanitized_name = Search.sanitize_name(name) - user = self.context['user'] + user = self.context["user"] if Search.objects.filter(author=user, name=sanitized_name).exists(): - raise serializers.ValidationError('Name already used') + raise serializers.ValidationError("Name already used") return sanitized_name def validate(self, validated_data): - if 'description' in validated_data and\ - not 'short_description' in validated_data: - raise serializers.ValidationError('Missing short description') + if ( + "description" in validated_data + and "short_description" not in validated_data + ): + raise serializers.ValidationError("Missing short description") return validated_data def create(self, validated_data): - user = self.context['user'] + user = self.context["user"] search = Search(author=user, version=1) - search.name = validated_data['name'] - search.filters = json.dumps(validated_data.get('filters')) - search.settings = json.dumps(validated_data.get('settings')) - search.short_description = validated_data.get('short_description', '') - search.description = validated_data.get('description', '') + search.name = validated_data["name"] + search.filters = json.dumps(validated_data.get("filters")) + search.settings = json.dumps(validated_data.get("settings")) + search.short_description = validated_data.get("short_description", "") + search.description = validated_data.get("description", "") search.save() - #if leaderboard activation was asked, activate the leaderboard - if validated_data.get('leaderboard'): + # if leaderboard activation was asked, activate the leaderboard + if validated_data.get("leaderboard"): leaderboard = Leaderboard(search=search) leaderboard.notify.add(user) return search def update(self, instance, validated_data): - if 'filters' in validated_data: - instance.filters = json.dumps(validated_data.get('filters')) - if 'settings' in validated_data: - instance.settings = json.dumps(validated_data.get('settings')) - instance.short_description = validated_data.get('short_description', instance.short_description) - instance.description = validated_data.get('description', instance.description) + if "filters" in validated_data: + instance.filters = json.dumps(validated_data.get("filters")) + if "settings" in validated_data: + instance.settings = json.dumps(validated_data.get("settings")) + instance.short_description = validated_data.get( + "short_description", instance.short_description + ) + instance.description = validated_data.get("description", instance.description) instance.save() - #if leaderboard activation was asked, activate the leaderboard - leaderboard = validated_data.get('leaderboard') - if leaderboard is True: #create + # if leaderboard activation was asked, activate the leaderboard + leaderboard = validated_data.get("leaderboard") + if leaderboard is True: # create # for some reason, get_or_create always raises an IntegrityError try: obj = Leaderboard.objects.get(search=instance) @@ -152,7 +168,7 @@ class SearchWriteSerializer(serializers.ModelSerializer): obj = Leaderboard(search=instance) obj.save() obj.notify.add(instance.author) - elif leaderboard is False and instance.has_leaderboard(): #delete + elif leaderboard is False and instance.has_leaderboard(): # delete instance.leaderboard.delete() return instance diff --git a/beat/web/search/signals.py b/beat/web/search/signals.py index 73e15b10f..b730aed17 100644 --- a/beat/web/search/signals.py +++ b/beat/web/search/signals.py @@ -25,32 +25,34 @@ # # ############################################################################### +from django.conf import settings +from django.contrib.sites.models import Site +from django.core.mail import EmailMessage from django.db import models from django.dispatch import receiver - -from django.core.mail import EmailMessage from django.template.loader import render_to_string -from django.conf import settings -from django.contrib.sites.models import Site +from .. import version from .models import Leaderboard + @receiver(models.signals.pre_delete, sender=Leaderboard) def notify_users(sender, instance, **kwargs): """Notify users the leaderboard was deleted""" - emails = instance.notify.exclude(instance.author).values_list('email', flat=True) + emails = instance.notify.exclude(instance.author).values_list("email", flat=True) if emails: - template_path = 'search/leaderboard_deleted.txt' - subject = "Leaderboard \"%s\" was deleted" % instance.search.fullname() + current_site = Site.objects.get_current() + template_path = "search/leaderboard_deleted.txt" + subject = 'Leaderboard "%s" was deleted' % instance.search.fullname() mesg = EmailMessage( subject.strip(), - render_to_string(template_path, - { - 'leaderboard': instance, - 'prev_date': prev_date, - 'beat_version': __version__, - 'site': current_site, - } + render_to_string( + template_path, + { + "leaderboard": instance, + "beat_version": version.__version__, + "site": current_site, + }, ), settings.DEFAULT_FROM_EMAIL, to=[settings.DEFAULT_FROM_EMAIL], diff --git a/beat/web/search/utils.py b/beat/web/search/utils.py index f26a44090..af9793ed8 100644 --- a/beat/web/search/utils.py +++ b/beat/web/search/utils.py @@ -25,8 +25,8 @@ # # ############################################################################### -import re import datetime +import re try: from functools import reduce @@ -34,30 +34,27 @@ except ImportError: pass from django.db.models import Q -from django.db.models.query import QuerySet UNLIKELY_STRING = "ZaqXsw<>?:{}|!@#$%^&*()-={}" -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- # Helper class to create an AND'ed combination of Q filters # # It is destined to create filters corresponding to contribution names, # containing optional '*' wildcards characters. -#------------------------------------------------------------------------------- +# ------------------------------------------------------------------------------- class FilterGenerator(object): - FILTER_IEXACT = 0 - FILTER_ICONTAINS = 1 + FILTER_IEXACT = 0 + FILTER_ICONTAINS = 1 FILTER_ISTARTSWITH = 2 - FILTER_IENDSWITH = 3 - + FILTER_IENDSWITH = 3 def __init__(self): self.filter = None - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Process a contribution name, optionally containing '*' wildcards # characters, and returns the corresponding filter # @@ -70,7 +67,7 @@ class FilterGenerator(object): # # The 'exact' parameters prevents '*' wildcards to be added at the # beginning and/or end of the relevant contribution name parts. - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def process_contribution_name(self, contribution_name, exact=False): self.filter = None @@ -84,8 +81,7 @@ class FilterGenerator(object): return self.filter - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Process a dataset name, optionally containing '*' wildcards characters, # and returns the corresponding filter # @@ -97,11 +93,13 @@ class FilterGenerator(object): # # The 'exact' parameters prevents '*' wildcards to be added at the # beginning and/or end of the relevant dataset name parts. - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def process_dataset_name(self, dataset_name, exact=False): self.filter = None - (database, version, protocol, dataset) = self.parse_dataset_name(dataset_name, exact) + (database, version, protocol, dataset) = self.parse_dataset_name( + dataset_name, exact + ) self.add(database, FilterGenerator.create_name_filter) @@ -113,8 +111,7 @@ class FilterGenerator(object): return self.filter - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Parse a contribution name, optionally containing '*' wildcards characters # # The contribution name can be in one of the following forms: @@ -129,25 +126,25 @@ class FilterGenerator(object): # # The 'exact' parameters prevents '*' wildcards to be added at the # beginning and/or end of the relevant contribution name parts. - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def parse_contribution_name(self, contribution_name, exact=False): - parts = contribution_name.split('/') + parts = contribution_name.split("/") - author = None - name = None + author = None + name = None version = None if len(parts) == 3: author = parts[0] - if not(exact) and (author[0] != '*'): - author = '*' + author + if not (exact) and (author[0] != "*"): + author = "*" + author name = parts[1] try: version = int(parts[2]) - except: + except ValueError: pass elif len(parts) == 2: @@ -155,31 +152,30 @@ class FilterGenerator(object): version = int(parts[1]) name = parts[0] - if not(exact) and (name[0] != '*'): - name = '*' + name - except: + if not (exact) and (name[0] != "*"): + name = "*" + name + except ValueError: author = parts[0] - if not(exact) and (author[0] != '*'): - author = '*' + author + if not (exact) and (author[0] != "*"): + author = "*" + author name = parts[1] - if not(exact) and (name[-1] != '*'): - name = name + '*' + if not (exact) and (name[-1] != "*"): + name = name + "*" else: name = parts[0] - if not(exact): - if name[0] != '*': - name = '*' + name + if not (exact): + if name[0] != "*": + name = "*" + name - if name[-1] != '*': - name = name + '*' + if name[-1] != "*": + name = name + "*" return (author, name, version) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Parse a dataset name, optionally containing '*' wildcards characters # # The dataset name can be in one of the following forms: @@ -196,63 +192,62 @@ class FilterGenerator(object): # # The 'exact' parameters prevents '*' wildcards to be added at the # beginning and/or end of the relevant dataset name parts. - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def parse_dataset_name(self, dataset_name, exact=False): - parts = re.split(r'[\.@]', dataset_name.strip(".@")) + parts = re.split(r"[\.@]", dataset_name.strip(".@")) database = None - version = None + version = None protocol = None - dataset = None + dataset = None if len(parts) >= 3: database = parts[0] - if not(exact) and (database[0] != '*'): - database = '*' + database + if not (exact) and (database[0] != "*"): + database = "*" + database protocol = parts[1] - dataset = '.'.join(parts[2:]) - if not(exact) and (dataset[-1] != '*'): - dataset = dataset + '*' + dataset = ".".join(parts[2:]) + if not (exact) and (dataset[-1] != "*"): + dataset = dataset + "*" elif len(parts) == 2: database = parts[0] - if not(exact) and (database[0] != '*'): - database = '*' + database + if not (exact) and (database[0] != "*"): + database = "*" + database protocol = parts[1] - if not(exact) and (protocol[-1] != '*'): - protocol = protocol + '*' + if not (exact) and (protocol[-1] != "*"): + protocol = protocol + "*" else: database = parts[0] - if not(exact): - if database[0] != '*': - database = '*' + database + if not (exact): + if database[0] != "*": + database = "*" + database - if database[-1] != '*': - database = database + '*' + if database[-1] != "*": + database = database + "*" if database is not None: - parts = database.split('/') + parts = database.split("/") if len(parts) == 2: - if parts[1][-1] == '*': - parts[0] += '*' + if parts[1][-1] == "*": + parts[0] += "*" parts[1] = parts[1][:-1] database = parts[0] try: version = int(parts[1]) - except: + except ValueError: pass return (database, version, protocol, dataset) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Process a (string) part of a contribution name, and invoke the provided # callback with the correct creation information # @@ -263,9 +258,8 @@ class FilterGenerator(object): # supplied type. # # Note: the value can be None, in which case the filter isn't changed. - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- def add(self, value, creation_callback): - def _add(new_filter): if self.filter is None: self.filter = new_filter @@ -275,10 +269,10 @@ class FilterGenerator(object): if value is None: return - start = (value[0] == '*') - end = (value[-1] == '*') + start = value[0] == "*" + end = value[-1] == "*" - parts = [x for x in value.split('*') if len(x) > 0] + parts = [x for x in value.split("*") if len(x) > 0] if len(parts) == 0: return @@ -305,10 +299,9 @@ class FilterGenerator(object): for part in parts[1:-1]: _add(creation_callback(FilterGenerator.FILTER_ICONTAINS, part)) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Creation callback for 'author__username__*' - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @staticmethod def create_author_filter(filter_type, value): if filter_type == FilterGenerator.FILTER_ICONTAINS: @@ -320,10 +313,9 @@ class FilterGenerator(object): else: return Q(author__username__iexact=value) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Creation callback for 'name__*' - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @staticmethod def create_name_filter(filter_type, value): if filter_type == FilterGenerator.FILTER_ICONTAINS: @@ -335,10 +327,9 @@ class FilterGenerator(object): else: return Q(name__iexact=value) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Creation callback for 'toolchain__author__username__*' - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @staticmethod def create_toolchain_author_filter(filter_type, value): if filter_type == FilterGenerator.FILTER_ICONTAINS: @@ -350,10 +341,9 @@ class FilterGenerator(object): else: return Q(toolchain__author__username__iexact=value) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Creation callback for 'toolchain__name__*' - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @staticmethod def create_toolchain_name_filter(filter_type, value): if filter_type == FilterGenerator.FILTER_ICONTAINS: @@ -365,10 +355,9 @@ class FilterGenerator(object): else: return Q(toolchain__name__iexact=value) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Creation callback for 'protocols__name__*' - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @staticmethod def create_protocol_filter(filter_type, value): if filter_type == FilterGenerator.FILTER_ICONTAINS: @@ -380,10 +369,9 @@ class FilterGenerator(object): else: return Q(protocols__name__iexact=value) - - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- # Creation callback for 'protocols__sets__name__*' - #--------------------------------------------------------------------------- + # --------------------------------------------------------------------------- @staticmethod def create_dataset_filter(filter_type, value): if filter_type == FilterGenerator.FILTER_ICONTAINS: @@ -396,18 +384,17 @@ class FilterGenerator(object): return Q(protocols__sets__name__iexact=value) -#------------------------------------------------ +# ------------------------------------------------ def OR(filters): return reduce(lambda a, b: a | b, filters) -#------------------------------------------------ +# ------------------------------------------------ def generate_database_filter(database, django_operator): - def _create_database_filter(filter_type, value): if filter_type == FilterGenerator.FILTER_ICONTAINS: return Q(referenced_datasets__protocol__database__name__icontains=value) @@ -438,15 +425,18 @@ def generate_database_filter(database, django_operator): else: return Q(referenced_datasets__name__iexact=value) - generator = FilterGenerator() - (database, version, protocol, dataset) = generator.parse_dataset_name(database, (django_operator == 'exact')) + (database, version, protocol, dataset) = generator.parse_dataset_name( + database, (django_operator == "exact") + ) generator.add(database, _create_database_filter) if version is not None: - generator.filter = generator.filter & Q(referenced_datasets__protocol__database__version=version) + generator.filter = generator.filter & Q( + referenced_datasets__protocol__database__version=version + ) generator.add(protocol, _create_protocol_filter) generator.add(dataset, _create_dataset_filter) @@ -455,116 +445,139 @@ def generate_database_filter(database, django_operator): def filter_database_name(queryset, operator, value): - if operator == 'contains': - return queryset.filter(generate_database_filter(value, 'contains')) + if operator == "contains": + return queryset.filter(generate_database_filter(value, "contains")) - elif operator == 'contains-not': - return queryset.exclude(generate_database_filter(value, 'contains')) + elif operator == "contains-not": + return queryset.exclude(generate_database_filter(value, "contains")) - elif operator == 'contains-any-of': - return queryset.filter(OR(map(lambda x: generate_database_filter(x, 'contains'), value))) + elif operator == "contains-any-of": + return queryset.filter( + OR(map(lambda x: generate_database_filter(x, "contains"), value)) + ) - elif operator == 'contains-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_database_filter(x, 'contains'), value))) + elif operator == "contains-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_database_filter(x, "contains"), value)) + ) - elif operator == 'is': - return queryset.filter(generate_database_filter(value, 'exact')) + elif operator == "is": + return queryset.filter(generate_database_filter(value, "exact")) - elif operator == 'is-not': - return queryset.exclude(generate_database_filter(value, 'exact')) + elif operator == "is-not": + return queryset.exclude(generate_database_filter(value, "exact")) - elif operator == 'is-any-of': - return queryset.filter(OR(map(lambda x: generate_database_filter(x, 'exact'), value))) + elif operator == "is-any-of": + return queryset.filter( + OR(map(lambda x: generate_database_filter(x, "exact"), value)) + ) - elif operator == 'is-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_database_filter(x, 'exact'), value))) + elif operator == "is-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_database_filter(x, "exact"), value)) + ) return queryset -#------------------------------------------------ +# ------------------------------------------------ def generate_protocol_filter(protocol, django_operator): - splitted = re.split(r'[\.@]', protocol) + splitted = re.split(r"[\.@]", protocol) if len(splitted) <= 2: - protocol = '*.' + protocol + protocol = "*." + protocol return generate_database_filter(protocol, django_operator) def filter_protocol_name(queryset, operator, value): - if operator == 'contains': - return queryset.filter(generate_protocol_filter(value, 'contains')) + if operator == "contains": + return queryset.filter(generate_protocol_filter(value, "contains")) - elif operator == 'contains-not': - return queryset.exclude(generate_protocol_filter(value, 'contains')) + elif operator == "contains-not": + return queryset.exclude(generate_protocol_filter(value, "contains")) - elif operator == 'contains-any-of': - return queryset.filter(OR(map(lambda x: generate_protocol_filter(x, 'contains'), value))) + elif operator == "contains-any-of": + return queryset.filter( + OR(map(lambda x: generate_protocol_filter(x, "contains"), value)) + ) - elif operator == 'contains-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_protocol_filter(x, 'contains'), value))) + elif operator == "contains-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_protocol_filter(x, "contains"), value)) + ) - elif operator == 'is': - return queryset.filter(generate_protocol_filter(value, 'exact')) + elif operator == "is": + return queryset.filter(generate_protocol_filter(value, "exact")) - elif operator == 'is-not': - return queryset.exclude(generate_protocol_filter(value, 'exact')) + elif operator == "is-not": + return queryset.exclude(generate_protocol_filter(value, "exact")) - elif operator == 'is-any-of': - return queryset.filter(OR(map(lambda x: generate_protocol_filter(x, 'exact'), value))) + elif operator == "is-any-of": + return queryset.filter( + OR(map(lambda x: generate_protocol_filter(x, "exact"), value)) + ) - elif operator == 'is-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_protocol_filter(x, 'exact'), value))) + elif operator == "is-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_protocol_filter(x, "exact"), value)) + ) return queryset -#------------------------------------------------ +# ------------------------------------------------ def generate_dataset_filter(dataset, django_operator): - splitted = re.split(r'[\.@]', dataset) + splitted = re.split(r"[\.@]", dataset) if len(splitted) == 1: - dataset = '*.' + dataset + dataset = "*." + dataset return generate_protocol_filter(dataset, django_operator) def filter_dataset_name(queryset, operator, value): - if operator == 'contains': - return queryset.filter(generate_dataset_filter(value, 'contains')) + if operator == "contains": + return queryset.filter(generate_dataset_filter(value, "contains")) - elif operator == 'contains-not': - return queryset.exclude(generate_dataset_filter(value, 'contains')) + elif operator == "contains-not": + return queryset.exclude(generate_dataset_filter(value, "contains")) - elif operator == 'contains-any-of': - return queryset.filter(OR(map(lambda x: generate_dataset_filter(x, 'contains'), value))) + elif operator == "contains-any-of": + return queryset.filter( + OR(map(lambda x: generate_dataset_filter(x, "contains"), value)) + ) - elif operator == 'contains-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_dataset_filter(x, 'contains'), value))) + elif operator == "contains-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_dataset_filter(x, "contains"), value)) + ) - elif operator == 'is': - return queryset.filter(generate_dataset_filter(value, 'exact')) + elif operator == "is": + return queryset.filter(generate_dataset_filter(value, "exact")) - elif operator == 'is-not': - return queryset.exclude(generate_dataset_filter(value, 'exact')) + elif operator == "is-not": + return queryset.exclude(generate_dataset_filter(value, "exact")) - elif operator == 'is-any-of': - return queryset.filter(OR(map(lambda x: generate_dataset_filter(x, 'exact'), value))) + elif operator == "is-any-of": + return queryset.filter( + OR(map(lambda x: generate_dataset_filter(x, "exact"), value)) + ) - elif operator == 'is-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_dataset_filter(x, 'exact'), value))) + elif operator == "is-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_dataset_filter(x, "exact"), value)) + ) return queryset -#------------------------------------------------ +# ------------------------------------------------ def generate_algorithm_filter(algorithm, analyzer_flag, django_operator): - def _create_algorithm_author_filter(filter_type, value): if filter_type == FilterGenerator.FILTER_ICONTAINS: return Q(blocks__algorithm__author__username__icontains=value) @@ -585,10 +598,11 @@ def generate_algorithm_filter(algorithm, analyzer_flag, django_operator): else: return Q(blocks__algorithm__name__iexact=value) - generator = FilterGenerator() - (author, name, version) = generator.parse_contribution_name(algorithm, (django_operator == 'exact')) + (author, name, version) = generator.parse_contribution_name( + algorithm, (django_operator == "exact") + ) generator.add(author, _create_algorithm_author_filter) generator.add(name, _create_algorithm_name_filter) @@ -603,71 +617,89 @@ def generate_algorithm_filter(algorithm, analyzer_flag, django_operator): def filter_algorithm_fullname(queryset, operator, value, analyzer): - if operator == 'contains': - return queryset.filter(generate_algorithm_filter(value, analyzer, 'contains')) + if operator == "contains": + return queryset.filter(generate_algorithm_filter(value, analyzer, "contains")) - elif operator == 'contains-not': - return queryset.exclude(generate_algorithm_filter(value, analyzer, 'contains')) + elif operator == "contains-not": + return queryset.exclude(generate_algorithm_filter(value, analyzer, "contains")) - elif operator == 'contains-any-of': - return queryset.filter(OR(map(lambda x: generate_algorithm_filter(x, analyzer, 'contains'), value))) + elif operator == "contains-any-of": + return queryset.filter( + OR(map(lambda x: generate_algorithm_filter(x, analyzer, "contains"), value)) + ) - elif operator == 'contains-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_algorithm_filter(x, analyzer, 'contains'), value))) + elif operator == "contains-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_algorithm_filter(x, analyzer, "contains"), value)) + ) - elif operator == 'is': - return queryset.filter(generate_algorithm_filter(value, analyzer, 'exact')) + elif operator == "is": + return queryset.filter(generate_algorithm_filter(value, analyzer, "exact")) - elif operator == 'is-not': - return queryset.exclude(generate_algorithm_filter(value, analyzer, 'exact')) + elif operator == "is-not": + return queryset.exclude(generate_algorithm_filter(value, analyzer, "exact")) - elif operator == 'is-any-of': - return queryset.filter(OR(map(lambda x: generate_algorithm_filter(x, analyzer, 'exact'), value))) + elif operator == "is-any-of": + return queryset.filter( + OR(map(lambda x: generate_algorithm_filter(x, analyzer, "exact"), value)) + ) - elif operator == 'is-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_algorithm_filter(x, analyzer, 'exact'), value))) + elif operator == "is-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_algorithm_filter(x, analyzer, "exact"), value)) + ) return queryset -#------------------------------------------------ +# ------------------------------------------------ def filter_toolchain_author(queryset, operator, value): - if operator == 'contains': + if operator == "contains": return queryset.filter(toolchain__author__username__icontains=value) - elif operator == 'contains-not': + elif operator == "contains-not": return queryset.exclude(toolchain__author__username__icontains=value) - elif operator == 'contains-any-of': - return queryset.filter(OR(map(lambda x: Q(toolchain__author__username__icontains=x), value))) + elif operator == "contains-any-of": + return queryset.filter( + OR(map(lambda x: Q(toolchain__author__username__icontains=x), value)) + ) - elif operator == 'contains-not-any-of': - return queryset.exclude(OR(map(lambda x: Q(toolchain__author__username__icontains=x), value))) + elif operator == "contains-not-any-of": + return queryset.exclude( + OR(map(lambda x: Q(toolchain__author__username__icontains=x), value)) + ) - elif operator == 'is': + elif operator == "is": return queryset.filter(toolchain__author__username__iexact=value) - elif operator == 'is-not': + elif operator == "is-not": return queryset.exclude(toolchain__author__username__iexact=value) - elif operator == 'is-any-of': - return queryset.filter(OR(map(lambda x: Q(toolchain__author__username__iexact=x), value))) + elif operator == "is-any-of": + return queryset.filter( + OR(map(lambda x: Q(toolchain__author__username__iexact=x), value)) + ) - elif operator == 'is-not-any-of': - return queryset.exclude(OR(map(lambda x: Q(toolchain__author__username__iexact=x), value))) + elif operator == "is-not-any-of": + return queryset.exclude( + OR(map(lambda x: Q(toolchain__author__username__iexact=x), value)) + ) return queryset -#------------------------------------------------ +# ------------------------------------------------ def generate_toolchain_filter(toolchain, django_operator): generator = FilterGenerator() - (author, name, version) = generator.parse_contribution_name(toolchain, (django_operator == 'exact')) + (author, name, version) = generator.parse_contribution_name( + toolchain, (django_operator == "exact") + ) generator.add(author, FilterGenerator.create_toolchain_author_filter) generator.add(name, FilterGenerator.create_toolchain_name_filter) @@ -680,164 +712,219 @@ def generate_toolchain_filter(toolchain, django_operator): def filter_toolchain_fullname(queryset, operator, value): - if operator == 'contains': - return queryset.filter(generate_toolchain_filter(value, 'contains')) + if operator == "contains": + return queryset.filter(generate_toolchain_filter(value, "contains")) - elif operator == 'contains-not': - return queryset.exclude(generate_toolchain_filter(value, 'contains')) + elif operator == "contains-not": + return queryset.exclude(generate_toolchain_filter(value, "contains")) - elif operator == 'contains-any-of': - return queryset.filter(OR(map(lambda x: generate_toolchain_filter(x, 'contains'), value))) + elif operator == "contains-any-of": + return queryset.filter( + OR(map(lambda x: generate_toolchain_filter(x, "contains"), value)) + ) - elif operator == 'contains-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_toolchain_filter(x, 'contains'), value))) + elif operator == "contains-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_toolchain_filter(x, "contains"), value)) + ) - elif operator == 'is': - return queryset.filter(generate_toolchain_filter(value, 'exact')) + elif operator == "is": + return queryset.filter(generate_toolchain_filter(value, "exact")) - elif operator == 'is-not': - return queryset.exclude(generate_toolchain_filter(value, 'exact')) + elif operator == "is-not": + return queryset.exclude(generate_toolchain_filter(value, "exact")) - elif operator == 'is-any-of': - return queryset.filter(OR(map(lambda x: generate_toolchain_filter(x, 'exact'), value))) + elif operator == "is-any-of": + return queryset.filter( + OR(map(lambda x: generate_toolchain_filter(x, "exact"), value)) + ) - elif operator == 'is-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_toolchain_filter(x, 'exact'), value))) + elif operator == "is-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_toolchain_filter(x, "exact"), value)) + ) return queryset -#------------------------------------------------ +# ------------------------------------------------ def filter_experiment_author(queryset, operator, value): - if operator == 'contains': + if operator == "contains": return queryset.filter(author__username__icontains=value) - elif operator == 'contains-not': + elif operator == "contains-not": return queryset.exclude(author__username__icontains=value) - elif operator == 'contains-any-of': - return queryset.filter(OR(map(lambda x: Q(author__username__icontains=x), value))) + elif operator == "contains-any-of": + return queryset.filter( + OR(map(lambda x: Q(author__username__icontains=x), value)) + ) - elif operator == 'contains-not-any-of': - return queryset.exclude(OR(map(lambda x: Q(author__username__icontains=x), value))) + elif operator == "contains-not-any-of": + return queryset.exclude( + OR(map(lambda x: Q(author__username__icontains=x), value)) + ) - elif operator == 'is': + elif operator == "is": return queryset.filter(author__username__iexact=value) - elif operator == 'is-not': + elif operator == "is-not": return queryset.exclude(author__username__iexact=value) - elif operator == 'is-any-of': + elif operator == "is-any-of": return queryset.filter(OR(map(lambda x: Q(author__username__iexact=x), value))) - elif operator == 'is-not-any-of': + elif operator == "is-not-any-of": return queryset.exclude(OR(map(lambda x: Q(author__username__iexact=x), value))) return queryset -#------------------------------------------------ +# ------------------------------------------------ def generate_experiment_filter(experiment, django_operator): - experiment_elems = experiment.split('/') + experiment_elems = experiment.split("/") if len(experiment_elems) == 5: - experiment_authorname, toolchain_authorname, toolchain_name, toolchain_version, experiment_name = experiment_elems + ( + experiment_authorname, + toolchain_authorname, + toolchain_name, + toolchain_version, + experiment_name, + ) = experiment_elems try: toolchain_version = int(toolchain_version) - except: + except ValueError: return Q(name__iexact=UNLIKELY_STRING) - if django_operator == 'contains': - return Q(author__username__icontains=experiment_authorname) & Q(toolchain__author__username__icontains=toolchain_authorname) & \ - Q(toolchain__name__icontains=toolchain_name) & Q(toolchain__version=toolchain_version) & \ - Q(name__icontains=experiment_name) - elif django_operator == 'exact': - return Q(author__username__iexact=experiment_authorname) & Q(toolchain__author__username__iexact=toolchain_authorname) & \ - Q(toolchain__name__iexact=toolchain_name) & Q(toolchain__version=toolchain_version) & \ - Q(name__iexact=experiment_name) + if django_operator == "contains": + return ( + Q(author__username__icontains=experiment_authorname) + & Q(toolchain__author__username__icontains=toolchain_authorname) + & Q(toolchain__name__icontains=toolchain_name) + & Q(toolchain__version=toolchain_version) + & Q(name__icontains=experiment_name) + ) + elif django_operator == "exact": + return ( + Q(author__username__iexact=experiment_authorname) + & Q(toolchain__author__username__iexact=toolchain_authorname) + & Q(toolchain__name__iexact=toolchain_name) + & Q(toolchain__version=toolchain_version) + & Q(name__iexact=experiment_name) + ) elif len(experiment_elems) == 4: - toolchain_authorname, toolchain_name, toolchain_version, experiment_name = experiment_elems + ( + toolchain_authorname, + toolchain_name, + toolchain_version, + experiment_name, + ) = experiment_elems experiment_authorname = toolchain_authorname try: toolchain_version = int(toolchain_version) - except: + except ValueError: return Q(name__iexact=UNLIKELY_STRING) - if django_operator == 'contains': - return Q(toolchain__author__username__icontains=toolchain_authorname) & \ - Q(toolchain__name__icontains=toolchain_name) & Q(toolchain__version=toolchain_version) & \ - Q(name__icontains=experiment_name) - elif django_operator == 'exact': - return Q(toolchain__author__username__iexact=toolchain_authorname) & \ - Q(toolchain__name__iexact=toolchain_name) & Q(toolchain__version=toolchain_version) & \ - Q(name__iexact=experiment_name) + if django_operator == "contains": + return ( + Q(toolchain__author__username__icontains=toolchain_authorname) + & Q(toolchain__name__icontains=toolchain_name) + & Q(toolchain__version=toolchain_version) + & Q(name__icontains=experiment_name) + ) + elif django_operator == "exact": + return ( + Q(toolchain__author__username__iexact=toolchain_authorname) + & Q(toolchain__name__iexact=toolchain_name) + & Q(toolchain__version=toolchain_version) + & Q(name__iexact=experiment_name) + ) elif len(experiment_elems) == 3: toolchain_name, toolchain_version, experiment_name = experiment_elems try: toolchain_version = int(toolchain_version) - except: + except ValueError: return Q(name__iexact=UNLIKELY_STRING) - if django_operator == 'contains': - return Q(toolchain__name__icontains=toolchain_name) & Q(toolchain__version=toolchain_version) & \ - Q(name__icontains=experiment_name) - elif django_operator == 'exact': - return Q(toolchain__name__iexact=toolchain_name) & Q(toolchain__version=toolchain_version) & \ - Q(name__iexact=experiment_name) + if django_operator == "contains": + return ( + Q(toolchain__name__icontains=toolchain_name) + & Q(toolchain__version=toolchain_version) + & Q(name__icontains=experiment_name) + ) + elif django_operator == "exact": + return ( + Q(toolchain__name__iexact=toolchain_name) + & Q(toolchain__version=toolchain_version) + & Q(name__iexact=experiment_name) + ) elif len(experiment_elems) == 2: toolchain_version, experiment_name = experiment_elems try: toolchain_version = int(toolchain_version) - except: + except ValueError: return Q(name__iexact=UNLIKELY_STRING) - if django_operator == 'contains': - return Q(toolchain__version=toolchain_version) & Q(name__icontains=experiment_name) - elif django_operator == 'exact': - return Q(toolchain__version=toolchain_version) & Q(name__iexact=experiment_name) + if django_operator == "contains": + return Q(toolchain__version=toolchain_version) & Q( + name__icontains=experiment_name + ) + elif django_operator == "exact": + return Q(toolchain__version=toolchain_version) & Q( + name__iexact=experiment_name + ) elif len(experiment_elems) == 1: experiment_name = experiment_elems[0] - if django_operator == 'contains': + if django_operator == "contains": return Q(name__icontains=experiment_name) - elif django_operator == 'exact': + elif django_operator == "exact": return Q(name__iexact=experiment_name) return Q(name__iexact=UNLIKELY_STRING) def filter_experiment(queryset, operator, value): - if operator == 'contains': - return queryset.filter(generate_experiment_filter(value, 'contains')) + if operator == "contains": + return queryset.filter(generate_experiment_filter(value, "contains")) - elif operator == 'contains-not': - return queryset.exclude(generate_experiment_filter(value, 'contains')) + elif operator == "contains-not": + return queryset.exclude(generate_experiment_filter(value, "contains")) - elif operator == 'contains-any-of': - return queryset.filter(OR(map(lambda x: generate_experiment_filter(x, 'contains'), value))) + elif operator == "contains-any-of": + return queryset.filter( + OR(map(lambda x: generate_experiment_filter(x, "contains"), value)) + ) - elif operator == 'contains-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_experiment_filter(x, 'contains'), value))) + elif operator == "contains-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_experiment_filter(x, "contains"), value)) + ) - elif operator == 'is': - return queryset.filter(generate_experiment_filter(value, 'exact')) + elif operator == "is": + return queryset.filter(generate_experiment_filter(value, "exact")) - elif operator == 'is-not': - return queryset.exclude(generate_experiment_filter(value, 'exact')) + elif operator == "is-not": + return queryset.exclude(generate_experiment_filter(value, "exact")) - elif operator == 'is-any-of': - return queryset.filter(OR(map(lambda x: generate_experiment_filter(x, 'exact'), value))) + elif operator == "is-any-of": + return queryset.filter( + OR(map(lambda x: generate_experiment_filter(x, "exact"), value)) + ) - elif operator == 'is-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_experiment_filter(x, 'exact'), value))) + elif operator == "is-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_experiment_filter(x, "exact"), value)) + ) - elif operator == 'has-attestation': + elif operator == "has-attestation": return queryset.filter(attestation__isnull=False) - elif operator == 'has-no-attestation': + elif operator == "has-no-attestation": return queryset.filter(attestation__isnull=True) return queryset -#------------------------------------------------ +# ------------------------------------------------ def _make_date(value): @@ -856,11 +943,11 @@ def _make_date(value): """ - return datetime.datetime.strptime(value, '%Y-%m-%d').date() + return datetime.datetime.strptime(value, "%Y-%m-%d").date() def _make_date_range(value): - """Creates a datetime.datetime range that spawns the hole day + """Creates a datetime.datetime range that spawns the hole day Parameters: @@ -878,237 +965,305 @@ def _make_date_range(value): """ - d = _make_date(value) - return (datetime.combine(d, datetime.time.min), - datetime.combine(d, datetime.time.max)) + d = _make_date(value) + return ( + datetime.combine(d, datetime.time.min), + datetime.combine(d, datetime.time.max), + ) def filter_experiment_date(queryset, operator, value): - if operator == 'is': + if operator == "is": return queryset.filter(end_date__range=_make_date_range(value)) - elif operator == 'is-not': + elif operator == "is-not": return queryset.exclude(end_date__range=_make_date_range(value)) - elif operator == 'is-any-of': - return queryset.filter(OR([Q(end_date__range=_make_date_range(k)) for k in value])) - - elif operator == 'is-not-any-of': - return queryset.exclude(OR([Q(end_date__range=_make_date_range(k)) for k in value])) - - elif operator == '>': - return queryset.filter(end_date__gt=datetime.datetime.combine(_make_date(value), datetime.time.max)) - - elif operator == '>=': - return queryset.filter(end_date__gte=datetime.datetime.combine(_make_date(value), datetime.time.min)) - - elif operator == '<': - return queryset.filter(end_date__lt=datetime.datetime.combine(_make_date(value), datetime.time.min)) - - elif operator == '<=': - return queryset.filter(end_date__lte=datetime.datetime.combine(_make_date(value), datetime.time.max)) + elif operator == "is-any-of": + return queryset.filter( + OR([Q(end_date__range=_make_date_range(k)) for k in value]) + ) + + elif operator == "is-not-any-of": + return queryset.exclude( + OR([Q(end_date__range=_make_date_range(k)) for k in value]) + ) + + elif operator == ">": + return queryset.filter( + end_date__gt=datetime.datetime.combine(_make_date(value), datetime.time.max) + ) + + elif operator == ">=": + return queryset.filter( + end_date__gte=datetime.datetime.combine( + _make_date(value), datetime.time.min + ) + ) + + elif operator == "<": + return queryset.filter( + end_date__lt=datetime.datetime.combine(_make_date(value), datetime.time.min) + ) + + elif operator == "<=": + return queryset.filter( + end_date__lte=datetime.datetime.combine( + _make_date(value), datetime.time.max + ) + ) return queryset -#------------------------------------------------ +# ------------------------------------------------ def filter_experiment_result(queryset, name, operator, value): - if operator == 'is': - return queryset.filter(blocks__analyzer=True, - blocks__results__name__iexact=name, - blocks__results__data_value__iexact=value - ) - - elif operator == 'is-not': - return queryset.exclude(blocks__analyzer=True, - blocks__results__name__iexact=name, - blocks__results__data_value__iexact=value - ) - - elif operator == 'is-any-of': - return queryset.filter(OR(map(lambda x: Q(blocks__results__data_value__iexact=x), value)), - blocks__analyzer=True, - blocks__results__name__iexact=name - ) - - elif operator == 'is-not-any-of': - return queryset.exclude(OR(map(lambda x: Q(blocks__results__data_value__iexact=x), value)), - blocks__analyzer=True, - blocks__results__name__iexact=name - ) - - elif operator == '>': - return queryset.filter(blocks__analyzer=True, - blocks__results__name__iexact=name, - blocks__results__data_value__gt=value - ) - - elif operator == '>=': - return queryset.filter(blocks__analyzer=True, - blocks__results__name__iexact=name, - blocks__results__data_value__gte=value - ) - - elif operator == '<': - return queryset.filter(blocks__analyzer=True, - blocks__results__name__iexact=name, - blocks__results__data_value__lt=value - ) - - elif operator == '<=': - return queryset.filter(blocks__analyzer=True, - blocks__results__name__iexact=name, - blocks__results__data_value__lte=value - ) + if operator == "is": + return queryset.filter( + blocks__analyzer=True, + blocks__results__name__iexact=name, + blocks__results__data_value__iexact=value, + ) + + elif operator == "is-not": + return queryset.exclude( + blocks__analyzer=True, + blocks__results__name__iexact=name, + blocks__results__data_value__iexact=value, + ) + + elif operator == "is-any-of": + return queryset.filter( + OR(map(lambda x: Q(blocks__results__data_value__iexact=x), value)), + blocks__analyzer=True, + blocks__results__name__iexact=name, + ) + + elif operator == "is-not-any-of": + return queryset.exclude( + OR(map(lambda x: Q(blocks__results__data_value__iexact=x), value)), + blocks__analyzer=True, + blocks__results__name__iexact=name, + ) + + elif operator == ">": + return queryset.filter( + blocks__analyzer=True, + blocks__results__name__iexact=name, + blocks__results__data_value__gt=value, + ) + + elif operator == ">=": + return queryset.filter( + blocks__analyzer=True, + blocks__results__name__iexact=name, + blocks__results__data_value__gte=value, + ) + + elif operator == "<": + return queryset.filter( + blocks__analyzer=True, + blocks__results__name__iexact=name, + blocks__results__data_value__lt=value, + ) + + elif operator == "<=": + return queryset.filter( + blocks__analyzer=True, + blocks__results__name__iexact=name, + blocks__results__data_value__lte=value, + ) return queryset -#------------------------------------------------ +# ------------------------------------------------ def filter_any_field(queryset, operator, value): - if operator == 'contains': - return queryset.filter(generate_database_filter(value, 'contains') | - Q(referenced_datasets__protocol__name__icontains=value) | - Q(referenced_datasets__name__icontains=value) | - generate_algorithm_filter(value, False, 'contains') | - generate_algorithm_filter(value, True, 'contains') | - generate_toolchain_filter(value, 'contains') | - Q(toolchain__author__username__icontains=value) | - Q(author__username__icontains=value) | - generate_experiment_filter(value, 'contains') - ) - - elif operator == 'contains-not': - return queryset.exclude(generate_database_filter(value, 'contains') | - Q(referenced_datasets__protocol__name__icontains=value) | - Q(referenced_datasets__name__icontains=value) | - generate_algorithm_filter(value, False, 'contains') | - generate_algorithm_filter(value, True, 'contains') | - generate_toolchain_filter(value, 'contains') | - Q(toolchain__author__username__icontains=value) | - Q(author__username__icontains=value) | - generate_experiment_filter(value, 'contains') - ) - - elif operator == 'contains-any-of': - return queryset.filter(OR(map(lambda x: generate_database_filter(x, 'contains'), value)) | - OR(map(lambda x: Q(referenced_datasets__protocol__name__icontains=x), value)) | - OR(map(lambda x: Q(referenced_datasets__name__icontains=x), value)) | - OR(map(lambda x: generate_algorithm_filter(x, False, 'contains'), value)) | - OR(map(lambda x: generate_algorithm_filter(x, True, 'contains'), value)) | - OR(map(lambda x: generate_toolchain_filter(x, 'contains'), value)) | - OR(map(lambda x: Q(toolchain__author__username__icontains=x), value)) | - OR(map(lambda x: Q(author__username__icontains=x), value)) | - OR(map(lambda x: generate_experiment_filter(x, 'contains'), value)) - ) - - elif operator == 'contains-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_database_filter(x, 'contains'), value)) | - OR(map(lambda x: Q(referenced_datasets__protocol__name__icontains=x), value)) | - OR(map(lambda x: Q(referenced_datasets__name__icontains=x), value)) | - OR(map(lambda x: generate_algorithm_filter(x, False, 'contains'), value)) | - OR(map(lambda x: generate_algorithm_filter(x, True, 'contains'), value)) | - OR(map(lambda x: generate_toolchain_filter(x, 'contains'), value)) | - OR(map(lambda x: Q(toolchain__author__username__icontains=x), value)) | - OR(map(lambda x: Q(author__username__icontains=x), value)) | - OR(map(lambda x: generate_experiment_filter(x, 'contains'), value)) - ) - - elif operator == 'is': - return queryset.filter(generate_database_filter(value, 'exact') | - Q(referenced_datasets__protocol__name__iexact=value) | - Q(referenced_datasets__name__iexact=value) | - generate_algorithm_filter(value, False, 'exact') | - generate_algorithm_filter(value, True, 'exact') | - generate_toolchain_filter(value, 'exact') | - Q(toolchain__author__username__iexact=value) | - Q(author__username__iexact=value) | - generate_experiment_filter(value, 'exact') - ) - - elif operator == 'is-not': - return queryset.exclude(generate_database_filter(value, 'exact') | - Q(referenced_datasets__protocol__name__iexact=value) | - Q(referenced_datasets__name__iexact=value) | - generate_algorithm_filter(value, False, 'exact') | - generate_algorithm_filter(value, True, 'exact') | - generate_toolchain_filter(value, 'exact') | - Q(toolchain__author__username__iexact=value) | - Q(author__username__iexact=value) | - generate_experiment_filter(value, 'exact') - ) - - elif operator == 'is-any-of': - return queryset.filter(OR(map(lambda x: generate_database_filter(x, 'exact'), value)) | - OR(map(lambda x: Q(referenced_datasets__protocol__name__iexact=x), value)) | - OR(map(lambda x: Q(referenced_datasets__name__iexact=x), value)) | - OR(map(lambda x: generate_algorithm_filter(x, False, 'exact'), value)) | - OR(map(lambda x: generate_algorithm_filter(x, True, 'exact'), value)) | - OR(map(lambda x: generate_toolchain_filter(x, 'exact'), value)) | - OR(map(lambda x: Q(toolchain__author__username__iexact=x), value)) | - OR(map(lambda x: Q(author__username__iexact=x), value)) | - OR(map(lambda x: generate_experiment_filter(x, 'exact'), value)) - ) - - elif operator == 'is-not-any-of': - return queryset.exclude(OR(map(lambda x: generate_database_filter(x, 'exact'), value)) | - OR(map(lambda x: Q(referenced_datasets__protocol__name__iexact=x), value)) | - OR(map(lambda x: Q(referenced_datasets__name__iexact=x), value)) | - OR(map(lambda x: generate_algorithm_filter(x, False, 'exact'), value)) | - OR(map(lambda x: generate_algorithm_filter(x, True, 'exact'), value)) | - OR(map(lambda x: generate_toolchain_filter(x, 'exact'), value)) | - OR(map(lambda x: Q(toolchain__author__username__iexact=x), value)) | - OR(map(lambda x: Q(author__username__iexact=x), value)) | - OR(map(lambda x: generate_experiment_filter(x, 'exact'), value)) - ) + if operator == "contains": + return queryset.filter( + generate_database_filter(value, "contains") + | Q(referenced_datasets__protocol__name__icontains=value) + | Q(referenced_datasets__name__icontains=value) + | generate_algorithm_filter(value, False, "contains") + | generate_algorithm_filter(value, True, "contains") + | generate_toolchain_filter(value, "contains") + | Q(toolchain__author__username__icontains=value) + | Q(author__username__icontains=value) + | generate_experiment_filter(value, "contains") + ) + + elif operator == "contains-not": + return queryset.exclude( + generate_database_filter(value, "contains") + | Q(referenced_datasets__protocol__name__icontains=value) + | Q(referenced_datasets__name__icontains=value) + | generate_algorithm_filter(value, False, "contains") + | generate_algorithm_filter(value, True, "contains") + | generate_toolchain_filter(value, "contains") + | Q(toolchain__author__username__icontains=value) + | Q(author__username__icontains=value) + | generate_experiment_filter(value, "contains") + ) + + elif operator == "contains-any-of": + return queryset.filter( + OR(map(lambda x: generate_database_filter(x, "contains"), value)) + | OR( + map( + lambda x: Q(referenced_datasets__protocol__name__icontains=x), value + ) + ) + | OR(map(lambda x: Q(referenced_datasets__name__icontains=x), value)) + | OR(map(lambda x: generate_algorithm_filter(x, False, "contains"), value)) + | OR(map(lambda x: generate_algorithm_filter(x, True, "contains"), value)) + | OR(map(lambda x: generate_toolchain_filter(x, "contains"), value)) + | OR(map(lambda x: Q(toolchain__author__username__icontains=x), value)) + | OR(map(lambda x: Q(author__username__icontains=x), value)) + | OR(map(lambda x: generate_experiment_filter(x, "contains"), value)) + ) + + elif operator == "contains-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_database_filter(x, "contains"), value)) + | OR( + map( + lambda x: Q(referenced_datasets__protocol__name__icontains=x), value + ) + ) + | OR(map(lambda x: Q(referenced_datasets__name__icontains=x), value)) + | OR(map(lambda x: generate_algorithm_filter(x, False, "contains"), value)) + | OR(map(lambda x: generate_algorithm_filter(x, True, "contains"), value)) + | OR(map(lambda x: generate_toolchain_filter(x, "contains"), value)) + | OR(map(lambda x: Q(toolchain__author__username__icontains=x), value)) + | OR(map(lambda x: Q(author__username__icontains=x), value)) + | OR(map(lambda x: generate_experiment_filter(x, "contains"), value)) + ) + + elif operator == "is": + return queryset.filter( + generate_database_filter(value, "exact") + | Q(referenced_datasets__protocol__name__iexact=value) + | Q(referenced_datasets__name__iexact=value) + | generate_algorithm_filter(value, False, "exact") + | generate_algorithm_filter(value, True, "exact") + | generate_toolchain_filter(value, "exact") + | Q(toolchain__author__username__iexact=value) + | Q(author__username__iexact=value) + | generate_experiment_filter(value, "exact") + ) + + elif operator == "is-not": + return queryset.exclude( + generate_database_filter(value, "exact") + | Q(referenced_datasets__protocol__name__iexact=value) + | Q(referenced_datasets__name__iexact=value) + | generate_algorithm_filter(value, False, "exact") + | generate_algorithm_filter(value, True, "exact") + | generate_toolchain_filter(value, "exact") + | Q(toolchain__author__username__iexact=value) + | Q(author__username__iexact=value) + | generate_experiment_filter(value, "exact") + ) + + elif operator == "is-any-of": + return queryset.filter( + OR(map(lambda x: generate_database_filter(x, "exact"), value)) + | OR(map(lambda x: Q(referenced_datasets__protocol__name__iexact=x), value)) + | OR(map(lambda x: Q(referenced_datasets__name__iexact=x), value)) + | OR(map(lambda x: generate_algorithm_filter(x, False, "exact"), value)) + | OR(map(lambda x: generate_algorithm_filter(x, True, "exact"), value)) + | OR(map(lambda x: generate_toolchain_filter(x, "exact"), value)) + | OR(map(lambda x: Q(toolchain__author__username__iexact=x), value)) + | OR(map(lambda x: Q(author__username__iexact=x), value)) + | OR(map(lambda x: generate_experiment_filter(x, "exact"), value)) + ) + + elif operator == "is-not-any-of": + return queryset.exclude( + OR(map(lambda x: generate_database_filter(x, "exact"), value)) + | OR(map(lambda x: Q(referenced_datasets__protocol__name__iexact=x), value)) + | OR(map(lambda x: Q(referenced_datasets__name__iexact=x), value)) + | OR(map(lambda x: generate_algorithm_filter(x, False, "exact"), value)) + | OR(map(lambda x: generate_algorithm_filter(x, True, "exact"), value)) + | OR(map(lambda x: generate_toolchain_filter(x, "exact"), value)) + | OR(map(lambda x: Q(toolchain__author__username__iexact=x), value)) + | OR(map(lambda x: Q(author__username__iexact=x), value)) + | OR(map(lambda x: generate_experiment_filter(x, "exact"), value)) + ) return queryset -#------------------------------------------------ +# ------------------------------------------------ def apply_filter(queryset, filter_entry): - context = filter_entry['context'] - if context == 'database-name': - return filter_database_name(queryset, filter_entry['operator'], filter_entry['value']) - - elif context == 'protocol-name': - return filter_protocol_name(queryset, filter_entry['operator'], filter_entry['value']) - - elif context == 'dataset-name': - return filter_dataset_name(queryset, filter_entry['operator'], filter_entry['value']) - - elif context == 'algorithm': - return filter_algorithm_fullname(queryset, filter_entry['operator'], filter_entry['value'], False) - - elif context == 'analyzer': - return filter_algorithm_fullname(queryset, filter_entry['operator'], filter_entry['value'], True) - - elif context == 'toolchain-author': - return filter_toolchain_author(queryset, filter_entry['operator'], filter_entry['value']) - - elif context == 'toolchain': - return filter_toolchain_fullname(queryset, filter_entry['operator'], filter_entry['value']) - - elif context == 'experiment-author': - return filter_experiment_author(queryset, filter_entry['operator'], filter_entry['value']) - - elif context == 'experiment': - return filter_experiment(queryset, filter_entry['operator'], filter_entry['value']) - - elif context == 'experiment-date': - return filter_experiment_date(queryset, filter_entry['operator'], filter_entry['value']) - - elif context == 'experiment-result': - return filter_experiment_result(queryset, filter_entry['name'], - filter_entry['operator'], filter_entry['value']) - - elif context == 'any-field': - return filter_any_field(queryset, filter_entry['operator'], filter_entry['value']) + context = filter_entry["context"] + if context == "database-name": + return filter_database_name( + queryset, filter_entry["operator"], filter_entry["value"] + ) + + elif context == "protocol-name": + return filter_protocol_name( + queryset, filter_entry["operator"], filter_entry["value"] + ) + + elif context == "dataset-name": + return filter_dataset_name( + queryset, filter_entry["operator"], filter_entry["value"] + ) + + elif context == "algorithm": + return filter_algorithm_fullname( + queryset, filter_entry["operator"], filter_entry["value"], False + ) + + elif context == "analyzer": + return filter_algorithm_fullname( + queryset, filter_entry["operator"], filter_entry["value"], True + ) + + elif context == "toolchain-author": + return filter_toolchain_author( + queryset, filter_entry["operator"], filter_entry["value"] + ) + + elif context == "toolchain": + return filter_toolchain_fullname( + queryset, filter_entry["operator"], filter_entry["value"] + ) + + elif context == "experiment-author": + return filter_experiment_author( + queryset, filter_entry["operator"], filter_entry["value"] + ) + + elif context == "experiment": + return filter_experiment( + queryset, filter_entry["operator"], filter_entry["value"] + ) + + elif context == "experiment-date": + return filter_experiment_date( + queryset, filter_entry["operator"], filter_entry["value"] + ) + + elif context == "experiment-result": + return filter_experiment_result( + queryset, + filter_entry["name"], + filter_entry["operator"], + filter_entry["value"], + ) + + elif context == "any-field": + return filter_any_field( + queryset, filter_entry["operator"], filter_entry["value"] + ) else: - raise ValueError('Unkown context %s' % context) + raise ValueError("Unkown context %s" % context) diff --git a/beat/web/search/views.py b/beat/web/search/views.py index e4639812d..f93c6d43b 100644 --- a/beat/web/search/views.py +++ b/beat/web/search/views.py @@ -25,27 +25,26 @@ # # ############################################################################### -from django.http import Http404 -from django.shortcuts import render, redirect -from django.shortcuts import get_object_or_404 +import datetime + +import simplejson as json from django.conf import settings -from django.contrib.auth.models import User -from django.db.models import Count from django.contrib import messages from django.contrib.auth.decorators import login_required +from django.contrib.auth.models import User +from django.http import Http404 +from django.shortcuts import get_object_or_404 +from django.shortcuts import redirect +from django.shortcuts import render -from .models import Search -from .utils import apply_filter - +from ..databases.models import DatabaseProtocol +from ..databases.models import DatabaseSet +from ..experiments.models import Block +from ..experiments.models import Experiment from ..team.models import Team -from ..ui.templatetags.markup import restructuredtext -from ..experiments.models import Experiment, Block -from ..algorithms.models import Algorithm -from ..databases.models import DatabaseProtocol, DatabaseSet from ..toolchains.models import Toolchain - -import datetime -import simplejson as json +from .models import Search +from .utils import apply_filter def search_experiments(user, filters, time_delta=None): @@ -75,13 +74,14 @@ def search_experiments(user, filters, time_delta=None): """ results = { - 'experiments': [], - 'common_toolchains':[], - 'common_protocols': [], - 'common_analyzers': [], + "experiments": [], + "common_toolchains": [], + "common_protocols": [], + "common_analyzers": [], } - if len(filters) == 0: return results + if len(filters) == 0: + return results # Use the experiment filters experiments = Experiment.objects.for_user(user, True).filter(status=Experiment.DONE) @@ -104,7 +104,8 @@ def search_experiments(user, filters, time_delta=None): # All used toolchains toolchains = Toolchain.objects.filter(experiments__in=experiments).distinct() - if toolchains.count() == 1: results['common_toolchains'] = toolchains + if toolchains.count() == 1: + results["common_toolchains"] = toolchains # Dictionary analyzer (algorithm) -> blocks in each experiment # The issue: for each experiment, the analyzer block name and associated @@ -152,9 +153,11 @@ def search_experiments(user, filters, time_delta=None): # This works if the toolchain is the same or very similar (in terms of # block names). Any number of matching blocks is captured. - blocks = blocks__in=Block.objects.filter(analyzer=True, experiment__in=experiments) + blocks = Block.objects.filter(analyzer=True, experiment__in=experiments) - ordered_blocks = blocks.order_by('name', 'algorithm__author__username', 'algorithm__name', 'algorithm__version') + ordered_blocks = blocks.order_by( + "name", "algorithm__author__username", "algorithm__name", "algorithm__version" + ) unused_blocks = [] blocks_by_name = {} for b in ordered_blocks: @@ -165,7 +168,7 @@ def search_experiments(user, filters, time_delta=None): # full match, algorithm is a common analyzer across all experiments # n.b.: because block names are unique within experiments the above # equality works fine. - results['common_analyzers'].append((algorithms.pop(), elements)) + results["common_analyzers"].append((algorithms.pop(), elements)) # else: blocks with the same name use different algorithms else: @@ -180,25 +183,38 @@ def search_experiments(user, filters, time_delta=None): blocks_by_algorithm.setdefault(block.algorithm, []).append(block) for algorithm, algo_blocks in blocks_by_algorithm.items(): uniq_experiments = set([k.experiment for k in algo_blocks]) - if len(algo_blocks) == len(experiments) and \ - len(uniq_experiments) == len(experiments): - results['common_analyzers'].append((algorithm, algo_blocks)) + if len(algo_blocks) == len(experiments) and len(uniq_experiments) == len( + experiments + ): + results["common_analyzers"].append((algorithm, algo_blocks)) # Protocols - protocols = DatabaseProtocol.objects.filter(sets__in=DatabaseSet.objects.filter(experiments__in=experiments)).distinct() + protocols = DatabaseProtocol.objects.filter( + sets__in=DatabaseSet.objects.filter(experiments__in=experiments) + ).distinct() if protocols.count() == 1: - results['common_protocols'] = protocols - else: #do we have the same number of protocols everywhere? + results["common_protocols"] = protocols + else: # do we have the same number of protocols everywhere? # calculates the distinct protocols per experiment - if all([DatabaseProtocol.objects.filter(sets__in=DatabaseSet.objects.filter(experiments__in=[k])).distinct().count() == protocols.count() for k in experiments]): - results['common_protocols'] = protocols - - results['experiments'] = experiments + if all( + [ + DatabaseProtocol.objects.filter( + sets__in=DatabaseSet.objects.filter(experiments__in=[k]) + ) + .distinct() + .count() + == protocols.count() + for k in experiments + ] + ): + results["common_protocols"] = protocols + + results["experiments"] = experiments return results -#------------------------------------------------ +# ------------------------------------------------ def filters_from_query(query): @@ -207,40 +223,41 @@ def filters_from_query(query): filters = [] any_fields = [] - if not query.strip(): return filters #empty + if not query.strip(): + return filters # empty def _operator_chooser(arr): - return 'contains' if len(arr) == 1 else 'contains-any-of' + return "contains" if len(arr) == 1 else "contains-any-of" def _value_chooser(arr): return arr[0] if len(arr) == 1 else arr def _make_context(name, entries): return { - 'context': name, - 'operator': _operator_chooser(entries), - 'value': _value_chooser(entries), - 'name': None, + "context": name, + "operator": _operator_chooser(entries), + "value": _value_chooser(entries), + "name": None, } keywords = [x.strip() for x in query.split() if x.strip()] for keyword in keywords: - offset = keyword.find(':') + offset = keyword.find(":") if offset != -1: command = keyword[:offset] - entries = [x.strip() for x in keyword[offset+1:].split(',')] + entries = [x.strip() for x in keyword[offset + 1 :].split(",")] entries = [x for x in entries if x] - if command in ['db', 'database']: - filters.append(_make_context('database-name', entries)) - elif command in ['tc', 'toolchain']: - filters.append(_make_context('toolchain', entries)) - elif command in ['algo', 'algorithm']: - filters.append(_make_context('algorithm', entries)) - elif command == 'analyzer': - filters.append(_make_context('analyzer', entries)) - elif command == 'type': + if command in ["db", "database"]: + filters.append(_make_context("database-name", entries)) + elif command in ["tc", "toolchain"]: + filters.append(_make_context("toolchain", entries)) + elif command in ["algo", "algorithm"]: + filters.append(_make_context("algorithm", entries)) + elif command == "analyzer": + filters.append(_make_context("analyzer", entries)) + elif command == "type": continue else: any_fields.extend(entries) @@ -248,139 +265,144 @@ def filters_from_query(query): any_fields.append(keyword) if any_fields: - filters.append({ - 'context': 'any-field', - 'operator': 'contains-any-of', - 'value': any_fields, - 'name': None, - }) + filters.append( + { + "context": "any-field", + "operator": "contains-any-of", + "value": any_fields, + "name": None, + } + ) return filters -#------------------------------------------------ +# ------------------------------------------------ def search(request): """Casual search request""" - filters = '[]' #by default, no filters, display all visible experiments - display_settings = [] #by default, no special settings + filters = "[]" # by default, no filters, display all visible experiments + display_settings = [] # by default, no special settings # A sequence of filter overrides that must be respected - if 'query' in request.GET: - query = request.GET['query'].strip() + if "query" in request.GET: + query = request.GET["query"].strip() filters = json.dumps(filters_from_query(query)) - if 'query' in request.POST: #overrules the GET request - query = request.POST['query'].strip() + if "query" in request.POST: # overrules the GET request + query = request.POST["query"].strip() filters = json.dumps(filters_from_query(query)) - if 'filters' in request.POST: #overrules the other two - filters = request.POST['filters'].strip() + if "filters" in request.POST: # overrules the other two + filters = request.POST["filters"].strip() - if 'settings' in request.POST: #overrules the other two - display_settings = request.POST['settings'].strip() + if "settings" in request.POST: # overrules the other two + display_settings = request.POST["settings"].strip() - return render(request, - 'search/view.html', - dict( - search=None, - owner=False, - filters=filters, - settings=display_settings, - results=search_experiments(request.user, json.loads(filters)), - URL_PREFIX=settings.URL_PREFIX, - )) + return render( + request, + "search/view.html", + dict( + search=None, + owner=False, + filters=filters, + settings=display_settings, + results=search_experiments(request.user, json.loads(filters)), + URL_PREFIX=settings.URL_PREFIX, + ), + ) -#------------------------------------------------ +# ------------------------------------------------ def view(request, author_name, query_name): """Stored search request""" # get the query from the DB - obj = get_object_or_404(Search, - author__username = author_name, - name = query_name) + obj = get_object_or_404(Search, author__username=author_name, name=query_name) # makes sure that the current user has access to it has_access, _ = obj.accessibility_for(request.user) - if not has_access: raise Http404() + if not has_access: + raise Http404() # user requests a filter update on a saved search, overrides defaults - if 'filters' in request.POST: - filters = request.POST['filters'].strip() - else: #use the obj filters + if "filters" in request.POST: + filters = request.POST["filters"].strip() + else: # use the obj filters filters = obj.filters - if 'settings' in request.POST: - display_settings = request.POST['settings'].strip() - else: #use the obj settings + if "settings" in request.POST: + display_settings = request.POST["settings"].strip() + else: # use the obj settings display_settings = obj.settings # Users the object can be shared with - users = User.objects.exclude(username__in=settings.ACCOUNTS_TO_EXCLUDE_FROM_TEAMS).order_by('username') + users = User.objects.exclude( + username__in=settings.ACCOUNTS_TO_EXCLUDE_FROM_TEAMS + ).order_by("username") - return render(request, - 'search/view.html', - { - 'search': obj, - 'owner': (request.user == obj.author), - 'filters': filters, - 'settings': display_settings, - 'results': search_experiments(request.user, json.loads(filters)), - 'URL_PREFIX': settings.URL_PREFIX, - 'users': users, - 'teams': Team.objects.for_user(request.user, True) - }) + return render( + request, + "search/view.html", + { + "search": obj, + "owner": (request.user == obj.author), + "filters": filters, + "settings": display_settings, + "results": search_experiments(request.user, json.loads(filters)), + "URL_PREFIX": settings.URL_PREFIX, + "users": users, + "teams": Team.objects.for_user(request.user, True), + }, + ) -#---------------------------------------------------------- +# ---------------------------------------------------------- def ls(request, author_name): - '''List all accessible searches to the request user''' + """List all accessible searches 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) # orders searchs so that the latest information is displayed first - objects = Search.objects.from_author_and_public(request.user, - author_name).order_by('-creation_date') + objects = Search.objects.from_author_and_public(request.user, author_name).order_by( + "-creation_date" + ) - return render(request, - 'search/list.html', - dict( - objects=objects, - author=author, - owner=(request.user==author), - )) + return render( + request, + "search/list.html", + dict(objects=objects, author=author, owner=(request.user == author),), + ) -#---------------------------------------------------------- +# ---------------------------------------------------------- def public_ls(request): - '''List all public searches''' + """List all public searches""" # orders searchs so that the latest information is displayed first - objects = Search.objects.public().order_by('-creation_date') + objects = Search.objects.public().order_by("-creation_date") - return render(request, - 'search/list.html', - dict( - objects=objects, - author=request.user, - owner=False, - )) + return render( + request, + "search/list.html", + dict(objects=objects, author=request.user, owner=False,), + ) -#---------------------------------------------------------- +# ---------------------------------------------------------- @login_required @@ -388,14 +410,13 @@ def notify(request, author_name, query_name): """Toggles notification for user""" # get the query from the DB - obj = get_object_or_404(Search, - author__username = author_name, - name = query_name) + obj = get_object_or_404(Search, author__username=author_name, name=query_name) # makes sure that the current user has access to it has_access, _ = obj.accessibility_for(request.user) - if not has_access: raise Http404() + if not has_access: + raise Http404() if not obj.has_leaderboard(): # user page is outdated, reload it @@ -403,12 +424,30 @@ def notify(request, author_name, query_name): if obj.leaderboard.notify.filter(id=request.user.id).exists(): obj.leaderboard.notify.remove(request.user) - messages.success(request, "Successfuly unsubscribed %s %s (%s) from leaderboard %s" % (request.user.first_name, request.user.last_name, request.user.username, obj)) + messages.success( + request, + "Successfuly unsubscribed %s %s (%s) from leaderboard %s" + % ( + request.user.first_name, + request.user.last_name, + request.user.username, + obj, + ), + ) else: obj.leaderboard.notify.add(request.user) - messages.success(request, "Successfuly subscribed %s %s (%s) to leaderboard %s" % (request.user.first_name, request.user.last_name, request.user.username, obj)) - - if 'HTTP_REFERER' in request.META: - return redirect(request.META['HTTP_REFERER']) + messages.success( + request, + "Successfuly subscribed %s %s (%s) to leaderboard %s" + % ( + request.user.first_name, + request.user.last_name, + request.user.username, + obj, + ), + ) + + if "HTTP_REFERER" in request.META: + return redirect(request.META["HTTP_REFERER"]) return redirect(obj) -- GitLab