Skip to content

Commit

Permalink
add feature flag INSTITUTIONAL_DASHBOARD_2024
Browse files Browse the repository at this point in the history
- add skeletons for new institution-user metrics view and serializer
- add `view_toggled_by_feature_flag` util to ease feature-flagged views
- use that util to toggle new/old views (with new/old serializers)
  • Loading branch information
aaxelb committed Sep 6, 2024
1 parent 34cbc9a commit 190259d
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 7 deletions.
15 changes: 15 additions & 0 deletions api/base/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
from urllib.parse import urlunsplit, urlsplit, parse_qs, urlencode
from packaging.version import Version
from hashids import Hashids
import waffle

from django.apps import apps
from django.core.exceptions import ObjectDoesNotExist
Expand Down Expand Up @@ -275,3 +276,17 @@ def __len__(self):
def add_dict_as_item(self, dict):
item = type('item', (object,), dict)
self.append(item)


def view_toggled_by_feature_flag(flag_name, view_name, old_view, new_view):
'''return a view function that, depending on a feature flag, uses "old" or "new" view behavior
(when removing that feature flag: delete this view, delete the "old" view,
and rename the "new" view to replace this one (and not say "new" anymore))
'''
def _waffled_view(request, *args, **kwargs):
if waffle.flag_is_active(request, flag_name):
return new_view(request, *args, **kwargs)
return old_view(request, *args, **kwargs)
_waffled_view.view_name = view_name
return _waffled_view
10 changes: 9 additions & 1 deletion api/institutions/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,7 +270,7 @@ def get_absolute_url(self, obj):
)


class InstitutionUserMetricsSerializer(JSONAPISerializer):
class OldInstitutionUserMetricsSerializer(JSONAPISerializer):

class Meta:
type_ = 'institution-users'
Expand Down Expand Up @@ -306,3 +306,11 @@ def get_absolute_url(self, obj):
'version': 'v2',
},
)


class NewInstitutionUserMetricsSerializer(JSONAPISerializer):

class Meta:
type_ = 'institution-users'

... # TODO: serializer fields
2 changes: 1 addition & 1 deletion api/institutions/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,5 +15,5 @@
re_path(r'^(?P<institution_id>\w+)/users/$', views.InstitutionUserList.as_view(), name=views.InstitutionUserList.view_name),
re_path(r'^(?P<institution_id>\w+)/metrics/summary/$', views.InstitutionSummaryMetrics.as_view(), name=views.InstitutionSummaryMetrics.view_name),
re_path(r'^(?P<institution_id>\w+)/metrics/departments/$', views.InstitutionDepartmentList.as_view(), name=views.InstitutionDepartmentList.view_name),
re_path(r'^(?P<institution_id>\w+)/metrics/users/$', views.InstitutionUserMetricsList.as_view(), name=views.InstitutionUserMetricsList.view_name),
re_path(r'^(?P<institution_id>\w+)/metrics/users/$', views.institution_user_metrics_view, name=views.institution_user_metrics_view.view_name),
]
41 changes: 36 additions & 5 deletions api/institutions/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@

from framework.auth.oauth_scopes import CoreScopes

import osf.features
from osf.metrics import InstitutionProjectCounts
from osf.models import OSFUser, Node, Institution, Registration
from osf.metrics import UserInstitutionProjectCounts
from osf.utils import permissions as osf_permissions

from api.base import permissions as base_permissions
from api.base.filters import ListFilterMixin
from api.base.filters import ListFilterMixin, FilterMixin
from api.base.views import JSONAPIBaseView
from api.base.serializers import JSONAPISerializer
from api.base.utils import get_object_or_error, get_user_auth
Expand All @@ -25,7 +26,10 @@
)
from api.base.settings import MAX_SIZE_OF_ES_QUERY
from api.base.exceptions import RelationshipPostMakesNoChanges
from api.base.utils import MockQueryset
from api.base.utils import (
MockQueryset,
view_toggled_by_feature_flag,
)
from api.base.settings import DEFAULT_ES_NULL_VALUE
from api.metrics.permissions import IsInstitutionalMetricsUser
from api.nodes.serializers import NodeSerializer
Expand All @@ -40,7 +44,8 @@
InstitutionRegistrationsRelationshipSerializer,
InstitutionSummaryMetricSerializer,
InstitutionDepartmentMetricsSerializer,
InstitutionUserMetricsSerializer,
NewInstitutionUserMetricsSerializer,
OldInstitutionUserMetricsSerializer,
)
from api.institutions.permissions import UserIsAffiliated
from api.institutions.renderers import InstitutionDepartmentMetricsCSVRenderer, InstitutionUserMetricsCSVRenderer, MetricsCSVRenderer
Expand Down Expand Up @@ -493,10 +498,10 @@ def get_default_queryset(self):
return self._make_elasticsearch_results_filterable(search, id=institution._id)


class InstitutionUserMetricsList(InstitutionImpactList):
class _OldInstitutionUserMetricsList(InstitutionImpactList):
view_name = 'institution-user-metrics'

serializer_class = InstitutionUserMetricsSerializer
serializer_class = OldInstitutionUserMetricsSerializer
renderer_classes = tuple(api_settings.DEFAULT_RENDERER_CLASSES) + (InstitutionUserMetricsCSVRenderer,)

ordering_fields = ('user_name', 'department')
Expand All @@ -521,3 +526,29 @@ def get_default_queryset(self):
institution = self.get_institution()
search = UserInstitutionProjectCounts.get_current_user_metrics(institution)
return self._make_elasticsearch_results_filterable(search, id=institution._id, department=DEFAULT_ES_NULL_VALUE)


class _NewInstitutionUserMetricsList(JSONAPIBaseView, FilterMixin, generics.ListAPIView):
permission_classes = (
drf_permissions.IsAuthenticatedOrReadOnly,
base_permissions.TokenHasScope,
IsInstitutionalMetricsUser,
)

required_read_scopes = [CoreScopes.INSTITUTION_METRICS_READ]
required_write_scopes = [CoreScopes.NULL]

view_category = 'institutions'
view_name = 'institution-user-metrics'

serializer_class = NewInstitutionUserMetricsSerializer

... # TODO: view logic


institution_user_metrics_view = view_toggled_by_feature_flag(
flag_name=osf.features.INSTITUTIONAL_DASHBOARD_2024,
view_name='institution-user-metrics',
old_view=_OldInstitutionUserMetricsList.as_view(),
new_view=_NewInstitutionUserMetricsList.as_view(),
)
4 changes: 4 additions & 0 deletions osf/features.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ flags:
note: This is not used
everyone: true

- flag_name: INSTITUTIONAL_DASHBOARD_2024
name: institutional_dashboard_2024
note: whether to surface older or updated (in 2024) institutional metrics

switches:
- flag_name: DISABLE_ENGAGEMENT_EMAILS
name: disable_engagement_emails
Expand Down

0 comments on commit 190259d

Please sign in to comment.