diff --git a/apps/analysis_framework/filter_set.py b/apps/analysis_framework/filter_set.py index f119bae989..6f41bea999 100644 --- a/apps/analysis_framework/filter_set.py +++ b/apps/analysis_framework/filter_set.py @@ -8,9 +8,13 @@ from .models import ( AnalysisFramework, ) +from entry.models import Entry +from django.utils import timezone +from datetime import timedelta class AnalysisFrameworkFilterSet(UserResourceFilterSet): + class Meta: model = AnalysisFramework fields = ('id', 'title', 'description', 'created_at',) @@ -24,12 +28,17 @@ class Meta: }, } - # ----------------------------- Graphql Filters --------------------------------------- + + class AnalysisFrameworkGqFilterSet(UserResourceGqlFilterSet): search = django_filters.CharFilter(method='search_filter') is_current_user_member = django_filters.BooleanFilter( field_name='is_current_user_member', method='filter_with_membership') + recently_used = django_filters.BooleanFilter( + method='filter_recently_used', + label='Recently Used', + ) class Meta: model = AnalysisFramework @@ -50,3 +59,11 @@ def filter_with_membership(self, queryset, _, value): afs = afs.exclude(filter_query) return queryset.filter(id__in=afs) return queryset + + def filter_recently_used(self, queryset, name, value): + if value: + # Calculate the date for "recent" usage (e.g., within the last 6 months or 180 days) + recent_usage_cutoff = timezone.now() - timedelta(days=180) + entries_qs = Entry.objects.filter(modified_at__gte=recent_usage_cutoff) + return queryset.filter(id__in=entries_qs.values('analysis_framework')) + return queryset diff --git a/apps/analysis_framework/tests/test_filters.py b/apps/analysis_framework/tests/test_filters.py index 27f0344db8..313dbc649b 100644 --- a/apps/analysis_framework/tests/test_filters.py +++ b/apps/analysis_framework/tests/test_filters.py @@ -2,6 +2,11 @@ from analysis_framework.filter_set import AnalysisFrameworkGqFilterSet from analysis_framework.factories import AnalysisFrameworkFactory +from entry.factories import EntryFactory +from lead.factories import LeadFactory + +from django.utils import timezone +from datetime import timedelta class TestAnalysisFrameworkFilter(GraphQLTestCase): @@ -21,3 +26,34 @@ def test_search_filter(self): expected, obtained ) + + def test_filter_recently_used(self): + af1, af2, af3, _ = AnalysisFrameworkFactory.create_batch(4) + lead1, lead2 = LeadFactory.create_batch(2) + now = timezone.now() + # Within date range window + EntryFactory.create( + analysis_framework=af1, + lead=lead1, + modified_at=now - timedelta(days=90), + ) + EntryFactory.create( + analysis_framework=af2, + lead=lead2, + modified_at=now - timedelta(days=30), + ) + # Outside date range window + EntryFactory.create( + analysis_framework=af3, + lead=lead1, + modified_at=now - timedelta(days=190), + ) + # Make sure we only get af1, af2 + obtained = set(list( + self + .filter_class(data={'recently_used': True}) + .qs + .values_list('id', flat=True) + )) + expected = set([af1.pk, af2.pk]) + self.assertEqual(obtained, expected) diff --git a/schema.graphql b/schema.graphql index 99a5344c29..c16e8cbf19 100644 --- a/schema.graphql +++ b/schema.graphql @@ -2842,8 +2842,8 @@ type Query { user(id: ID!): UserType users(id: Float, search: String, membersExcludeProject: ID, membersExcludeFramework: ID, membersExcludeUsergroup: ID, page: Int = 1, ordering: String, pageSize: Int): UserListType analysisFramework(id: ID!): AnalysisFrameworkDetailType - analysisFrameworks(id: Float, createdAt: DateTime, createdAtGte: DateTime, createdAtLte: DateTime, modifiedAt: DateTime, modifiedAtGte: DateTime, modifiedAtLte: DateTime, createdBy: [ID!], modifiedBy: [ID!], search: String, isCurrentUserMember: Boolean, page: Int = 1, ordering: String, pageSize: Int): AnalysisFrameworkListType - publicAnalysisFrameworks(id: Float, createdAt: DateTime, createdAtGte: DateTime, createdAtLte: DateTime, modifiedAt: DateTime, modifiedAtGte: DateTime, modifiedAtLte: DateTime, createdBy: [ID!], modifiedBy: [ID!], search: String, isCurrentUserMember: Boolean, page: Int = 1, ordering: String, pageSize: Int): PublicAnalysisFrameworkListType + analysisFrameworks(id: Float, createdAt: DateTime, createdAtGte: DateTime, createdAtLte: DateTime, modifiedAt: DateTime, modifiedAtGte: DateTime, modifiedAtLte: DateTime, createdBy: [ID!], modifiedBy: [ID!], search: String, isCurrentUserMember: Boolean, recentlyUsed: Boolean, page: Int = 1, ordering: String, pageSize: Int): AnalysisFrameworkListType + publicAnalysisFrameworks(id: Float, createdAt: DateTime, createdAtGte: DateTime, createdAtLte: DateTime, modifiedAt: DateTime, modifiedAtGte: DateTime, modifiedAtLte: DateTime, createdBy: [ID!], modifiedBy: [ID!], search: String, isCurrentUserMember: Boolean, recentlyUsed: Boolean, page: Int = 1, ordering: String, pageSize: Int): PublicAnalysisFrameworkListType project(id: ID!): ProjectDetailType projects(createdAt: DateTime, createdAtGte: DateTime, createdAtLte: DateTime, modifiedAt: DateTime, modifiedAtGte: DateTime, modifiedAtLte: DateTime, createdBy: [ID!], modifiedBy: [ID!], ids: [ID!], excludeIds: [ID!], status: ProjectStatusEnum, organizations: [ID!], analysisFrameworks: [ID!], regions: [ID!], search: String, isCurrentUserMember: Boolean, hasPermissionAccess: ProjectPermission, ordering: [ProjectOrderingEnum!], isTest: Boolean, page: Int = 1, pageSize: Int): ProjectListType recentProjects: [ProjectDetailType!]