Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add tag in analysis_framework #1381

Merged
merged 2 commits into from
Nov 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions apps/analysis_framework/admin.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

from .models import (
AnalysisFramework,
AnalysisFrameworkTag,
AnalysisFrameworkRole,
AnalysisFrameworkMembership,
Section,
Expand Down Expand Up @@ -113,3 +114,8 @@ class AnalysisFrameworkRoleAdmin(admin.ModelAdmin):

def has_add_permission(self, request, obj=None):
return False


@admin.register(AnalysisFrameworkTag)
class AnalysisFrameworkTagAdmin(admin.ModelAdmin):
list_display = ('id', 'title',)
16 changes: 16 additions & 0 deletions apps/analysis_framework/dataloaders.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
Filter,
Exportable,
AnalysisFrameworkMembership,
AnalysisFramework,
)


Expand Down Expand Up @@ -90,6 +91,17 @@ def batch_load_fn(self, keys):
return Promise.resolve([_map[key] for key in keys])


class AnalysisFrameworkTagsLoader(DataLoaderWithContext):
def batch_load_fn(self, keys):
qs = AnalysisFramework.tags.through.objects.filter(
analysisframework__in=keys,
).select_related('analysisframeworktag')
_map = defaultdict(list)
for row in qs:
_map[row.analysisframework_id].append(row.analysisframeworktag)
return Promise.resolve([_map[key] for key in keys])


class DataLoaders(WithContextMixin):
@cached_property
def secondary_widgets(self):
Expand All @@ -114,3 +126,7 @@ def exportables(self):
@cached_property
def members(self):
return MembershipLoader(context=self.context)

@cached_property
def af_tags(self):
return AnalysisFrameworkTagsLoader(context=self.context)
25 changes: 25 additions & 0 deletions apps/analysis_framework/factories.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,47 @@
import factory
from factory import fuzzy
from factory.django import DjangoModelFactory
from django.core.files.base import ContentFile

from .models import (
AnalysisFramework,
AnalysisFrameworkTag,
Section,
Widget,
Filter,
)


class AnalysisFrameworkTagFactory(DjangoModelFactory):
title = factory.Sequence(lambda n: f'AF-Tag-{n}')
description = factory.Faker('sentence', nb_words=20)
icon = factory.LazyAttribute(
lambda n: ContentFile(
factory.django.ImageField()._make_data(
{'width': 100, 'height': 100}
), f'example_{n.title}.png'
)
)

class Meta:
model = AnalysisFrameworkTag


class AnalysisFrameworkFactory(DjangoModelFactory):
title = factory.Sequence(lambda n: f'AF-{n}')
description = factory.Faker('sentence', nb_words=20)

class Meta:
model = AnalysisFramework

@factory.post_generation
def tags(self, create, extracted, **_):
if not create:
return
if extracted:
for tag in extracted:
self.tags.add(tag)


class SectionFactory(DjangoModelFactory):
title = factory.Sequence(lambda n: f'Section-{n}')
Expand Down
19 changes: 19 additions & 0 deletions apps/analysis_framework/filter_set.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,11 @@
UserResourceFilterSet,
UserResourceGqlFilterSet,
)
from utils.graphene.filters import IDListFilter

from .models import (
AnalysisFramework,
AnalysisFrameworkTag,
)
from entry.models import Entry
from django.utils import timezone
Expand All @@ -28,7 +31,22 @@ class Meta:
},
}


# ----------------------------- Graphql Filters ---------------------------------------
class AnalysisFrameworkTagGqFilterSet(django_filters.FilterSet):
search = django_filters.CharFilter(method='search_filter')

class Meta:
model = AnalysisFrameworkTag
fields = ['id']

def search_filter(self, qs, _, value):
if value:
return qs.filter(
models.Q(title__icontains=value) |
models.Q(description__icontains=value)
)
return qs


class AnalysisFrameworkGqFilterSet(UserResourceGqlFilterSet):
Expand All @@ -39,6 +57,7 @@ class AnalysisFrameworkGqFilterSet(UserResourceGqlFilterSet):
method='filter_recently_used',
label='Recently Used',
)
tags = IDListFilter(distinct=True)

class Meta:
model = AnalysisFramework
Expand Down
27 changes: 27 additions & 0 deletions apps/analysis_framework/migrations/0040_auto_20231109_1208.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Generated by Django 3.2.17 on 2023-11-09 12:08

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('analysis_framework', '0039_analysisframework_cloned_from'),
]

operations = [
migrations.CreateModel(
name='AnalysisFrameworkTag',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('description', models.TextField(blank=True)),
('icon', models.FileField(max_length=255, upload_to='af-tag-icon/')),
],
),
migrations.AddField(
model_name='analysisframework',
name='tags',
field=models.ManyToManyField(blank=True, related_name='_analysis_framework_analysisframework_tags_+', to='analysis_framework.AnalysisFrameworkTag'),
),
]
10 changes: 10 additions & 0 deletions apps/analysis_framework/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,15 @@
from .widgets import store as widgets_store


class AnalysisFrameworkTag(models.Model):
title = models.CharField(max_length=255)
description = models.TextField(blank=True)
icon = models.FileField(upload_to='af-tag-icon/', max_length=255)

def __str__(self):
return self.title


class AnalysisFramework(UserResource):
"""
Analysis framework defining framework to do analysis
Expand All @@ -19,6 +28,7 @@ class AnalysisFramework(UserResource):
"""
title = models.CharField(max_length=255)
description = models.TextField(blank=True, null=True)
tags = models.ManyToManyField(AnalysisFrameworkTag, related_name='+', blank=True)

is_private = models.BooleanField(default=False)
assisted_tagging_enabled = models.BooleanField(default=False)
Expand Down
42 changes: 39 additions & 3 deletions apps/analysis_framework/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
from assisted_tagging.models import PredictionTagAnalysisFrameworkWidgetMapping
from .models import (
AnalysisFramework,
AnalysisFrameworkTag,
Section,
Widget,
Filter,
Expand All @@ -29,7 +30,7 @@
AnalysisFrameworkRoleTypeEnum,
)
from .serializers import AnalysisFrameworkPropertiesGqlSerializer
from .filter_set import AnalysisFrameworkGqFilterSet
from .filter_set import AnalysisFrameworkGqFilterSet, AnalysisFrameworkTagGqFilterSet
from .public_schema import PublicAnalysisFrameworkListType


Expand Down Expand Up @@ -84,6 +85,18 @@ def resolve_widgets(root, info):
return info.context.dl.analysis_framework.sections_widgets.load(root.id)


class AnalysisFrameworkTagType(DjangoObjectType):
class Meta:
model = AnalysisFrameworkTag
only_fields = (
'id',
'title',
'description',
)

icon = graphene.Field(FileFieldType, required=False)


# NOTE: We have AnalysisFrameworkDetailType for detailed AF Type.
class AnalysisFrameworkType(DjangoObjectType):
class Meta:
Expand All @@ -96,12 +109,19 @@ class Meta:
current_user_role = graphene.Field(AnalysisFrameworkRoleTypeEnum)
preview_image = graphene.Field(FileFieldType)
export = graphene.Field(FileFieldType)
cloned_from = graphene.ID(source='cloned_from_id')
allowed_permissions = graphene.List(
graphene.NonNull(
graphene.Enum.from_enum(AfP.Permission),
), required=True
),
required=True,
)
tags = graphene.List(
graphene.NonNull(
AnalysisFrameworkTagType,
),
required=True,
)
cloned_from = graphene.ID(source='cloned_from_id')

@staticmethod
def get_custom_node(_, info, id):
Expand All @@ -123,6 +143,10 @@ def resolve_allowed_permissions(root, info):
is_public=not root.is_private,
)

@staticmethod
def resolve_tags(root, info):
return info.context.dl.analysis_framework.af_tags.load(root.id)


class AnalysisFrameworkRoleType(DjangoObjectType):
class Meta:
Expand Down Expand Up @@ -251,6 +275,12 @@ class Meta:
filterset_class = AnalysisFrameworkGqFilterSet


class AnalysisFrameworkTagListType(CustomDjangoListObjectType):
class Meta:
model = AnalysisFrameworkTag
filterset_class = AnalysisFrameworkTagGqFilterSet


class Query:
analysis_framework = DjangoObjectField(AnalysisFrameworkDetailType)
analysis_frameworks = DjangoPaginatedListObjectField(
Expand All @@ -265,6 +295,12 @@ class Query:
page_size_query_param='pageSize'
)
)
analysis_framework_tags = DjangoPaginatedListObjectField(
AnalysisFrameworkTagListType,
pagination=PageGraphqlPagination(
page_size_query_param='pageSize'
)
)

@staticmethod
def resolve_analysis_frameworks(root, info, **kwargs) -> QuerySet:
Expand Down
Loading
Loading