From 08d532e21e7ef4d4178144925603ac7e7f1e84db Mon Sep 17 00:00:00 2001 From: Alexander Pace <alexander.pace@ligo.org> Date: Tue, 14 Jan 2025 13:46:36 -0600 Subject: [PATCH 1/5] add some permission management helper functions only been a decade, might as well stop copying and pasting --- gracedb/ligoauth/utils.py | 95 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) diff --git a/gracedb/ligoauth/utils.py b/gracedb/ligoauth/utils.py index f3626ddcd..4b5e8bcb3 100644 --- a/gracedb/ligoauth/utils.py +++ b/gracedb/ligoauth/utils.py @@ -1,7 +1,12 @@ from django.conf import settings +from django.contrib.auth.models import User, Permission +from django.contrib.contenttypes.models import ContentType from django.http import HttpResponseForbidden from django.utils.functional import wraps +from guardian.models import UserObjectPermission +from events.models import Pipeline + def groups_allowed(group_names): """ @@ -28,3 +33,93 @@ def groups_allowed(group_names): def is_internal(user): return user.groups.filter(name=settings.LVC_GROUP).exists() + + +def add_user_to_pipeline_uploaders(user, pipeline): + """ + Function that creates a UserObjectPermission object to upload + to a pipeline. + + Inputs: + user: a django.contrib.auth.model.User object + pipeline: a event.models.Pipeline object + """ + + # Check to see that the inputs are valid: + if not isinstance(user, User): + raise ValueError(f'{user} is not a valid User object') + if not isinstance(pipeline, Pipeline): + raise ValueError(f'{pipeline} is not a valid Pipeline object') + + # Retrieve the objects we will need + p = Permission.objects.get(codename='populate_pipeline') + ctype = ContentType.objects.get(app_label='events', model='pipeline') + + perm, created = UserObjectPermission.objects.get_or_create( + user=user, permission=p, content_type=ctype, + object_pk=pipeline.pk) + + if created: + print(f'Added populate_pipeline permission for {user.username} and', + f'{pipeline.name})') + else: + print(f'{user.username} already has permission to populate {pipeline.name}') + + +def remove_user_from_pipeline_uploaders(user, pipeline): + """ + Function that removes UserObjectPermission object to upload + to a pipeline. + + Inputs: + user: a django.contrib.auth.model.User object + pipeline: a event.models.Pipeline object + """ + + # Check to see that the inputs are valid: + if not isinstance(user, User): + raise ValueError(f'{user} is not a valid User object') + if not isinstance(pipeline, Pipeline): + raise ValueError(f'{pipeline} is not a valid Pipeline object') + + # Retrieve the objects we will need + p = Permission.objects.get(codename='populate_pipeline') + ctype = ContentType.objects.get(app_label='events', model='pipeline') + + try: + perm = UserObjectPermission.objects.get( + user=user, permission=p, content_type=ctype, + object_pk=pipeline.pk) + except UserObjectPermission.DoesNotExist: + print(f'{user.username} does not have permission to populate', + f'{pipeline.name}') + return + + perm.delete() + print(f'{user.username} removed from {pipeline.name} uploaders') + + +def get_pipeline_uploaders(pipeline): + """ + Function that returns a queryset of user objects that currently have + permission to populate a given pipeline + + Inputs: + pipeline: a event.models.Pipeline object + """ + + if not isinstance(pipeline, Pipeline): + raise ValueError(f'{pipeline} is not a valid Pipeline object') + + # Retrieve the objects we will need + p = Permission.objects.get(codename='populate_pipeline') + ctype = ContentType.objects.get(app_label='events', model='pipeline') + + uploaders = UserObjectPermission.objects.filter( + permission=p, content_type=ctype, object_pk=pipeline.pk) + + if uploaders.exists(): + return User.objects.filter(id__in=uploaders.filter(user_id__isnull=False).\ + values_list('user_id', flat=True)) + else: + return User.objects.none() -- GitLab From d15f6117bc73eed92d1a9c17986774420f9ebf59 Mon Sep 17 00:00:00 2001 From: Alexander Pace <alexander.pace@ligo.org> Date: Tue, 14 Jan 2025 14:22:22 -0600 Subject: [PATCH 2/5] add SGNL pipeline and associated coinc machinery --- config/settings/base.py | 1 + docs/user_docs/source/models.rst | 2 +- .../migrations/0102_add_sgnl_pipeline.py | 34 +++++++++++++++++++ gracedb/events/translator.py | 2 +- gracedb/events/view_logic.py | 5 ++- 5 files changed, 39 insertions(+), 5 deletions(-) create mode 100644 gracedb/events/migrations/0102_add_sgnl_pipeline.py diff --git a/config/settings/base.py b/config/settings/base.py index 2818bb2e8..28fa0c49f 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -255,6 +255,7 @@ COINC_PIPELINES = [ 'pycbc', 'MBTA', 'PyGRB', + 'SGNL', ] GRB_PIPELINES = [ 'Fermi', diff --git a/docs/user_docs/source/models.rst b/docs/user_docs/source/models.rst index d7de02d37..e3741b660 100644 --- a/docs/user_docs/source/models.rst +++ b/docs/user_docs/source/models.rst @@ -12,7 +12,7 @@ The different types of events in GraceDB are distinguished by the following para - ``Group``: the working group responsible for finding the candidate - values: ``CBC``, ``Burst``, ``Detchar``, ``External``, ``Test`` - ``Pipeline``: the data analysis software tool used make the detection - - values: ``CWB2G``, ``spiir``, ``HardwareInjection``, ``X``, ``Q``, ``Omega``, ``Ringdown``, ``Fermi``, ``Swift``, ``CWB``, ``SNEWS``, ``oLIB``, ``pycbc``, ``INTEGRAL``, ``AGILE``, ``gstlal``, ``MLy``, ``MBTAOnline``, ``MBTA``, ``CHIME``, ``PyGRB``, ``aframe``, ``SVOM``, ``IceCube``, ``GWAK`` + - values: ``CWB2G``, ``spiir``, ``HardwareInjection``, ``X``, ``Q``, ``Omega``, ``Ringdown``, ``Fermi``, ``Swift``, ``CWB``, ``SNEWS``, ``oLIB``, ``pycbc``, ``INTEGRAL``, ``AGILE``, ``gstlal``, ``MLy``, ``MBTAOnline``, ``MBTA``, ``CHIME``, ``PyGRB``, ``aframe``, ``SVOM``, ``IceCube``, ``GWAK``, ``SGNL`` - ``Search``: the search activity which led to the detection - values: ``AllSky``, ``LowMass``, ``HighMass``, ``GRB``, ``Supernova``, ``MDC``, ``LowMassSim``, ``AllSkyLong``, ``O2VirgoTest``, ``BBH``, ``IMBH``, ``SubGRB``, ``EarlyWarning``, ``SubGRBTargeted``, ``SSM``, ``FRB``, ``LensingSubthreshold``, ``VTInjection``, ``HEN`` diff --git a/gracedb/events/migrations/0102_add_sgnl_pipeline.py b/gracedb/events/migrations/0102_add_sgnl_pipeline.py new file mode 100644 index 000000000..a532e68da --- /dev/null +++ b/gracedb/events/migrations/0102_add_sgnl_pipeline.py @@ -0,0 +1,34 @@ +from django.db import migrations +from events.models import Pipeline + +# Creates initial search pipeline instances + +# List of search pipeline names +NEW_PIPELINES = [ + ('SGNL', Pipeline.PIPELINE_TYPE_SEARCH_PRODUCTION) +] + +def add_pipelines(apps, schema_editor): + Pipeline = apps.get_model('events', 'Pipeline') + + # Create pipelines + for pipeline_name in NEW_PIPELINES: + pipeline, created = Pipeline.objects.get_or_create(name=pipeline_name[0]) + pipeline.pipeline_type = pipeline_name[1] + pipeline.save() + +def remove_pipelines(apps, schema_editor): + Pipeline = apps.get_model('events', 'Pipeline') + + # Delete pipelines + Pipeline.objects.filter(name__in=NEW_PIPELINES).delete() + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0101_remove_mlyburstevent_channel_mlyburstevent_channels'), + ] + + operations = [ + migrations.RunPython(add_pipelines, remove_pipelines), + ] diff --git a/gracedb/events/translator.py b/gracedb/events/translator.py index def85d646..2feba133d 100644 --- a/gracedb/events/translator.py +++ b/gracedb/events/translator.py @@ -117,7 +117,7 @@ def handle_uploaded_data(event, datafilename, pipeline = event.pipeline.name - if pipeline in [ 'gstlal', 'spiir', 'pycbc', 'PyGRB'] or (pipeline in ['MBTA', 'MBTAOnline'] and '.xml' in datafilename): + if pipeline in [ 'gstlal', 'spiir', 'pycbc', 'PyGRB', 'SGNL'] or (pipeline in ['MBTA', 'MBTAOnline'] and '.xml' in datafilename): log_comment = "Log File Created" # Wildly speculative wrt HM diff --git a/gracedb/events/view_logic.py b/gracedb/events/view_logic.py index 2d7e632df..b630291cb 100644 --- a/gracedb/events/view_logic.py +++ b/gracedb/events/view_logic.py @@ -55,10 +55,9 @@ def _createEventFromForm(request, form): else: search = None # Create Event - if pipeline.name in ['gstlal', 'spiir', 'MBTAOnline', 'MBTA', 'pycbc', 'PyGRB']: + if pipeline.name in settings.COINC_PIPELINES: event = CoincInspiralEvent() - elif pipeline.name in ['Fermi', 'Swift', 'SNEWS','INTEGRAL','AGILE', 'CHIME', - 'SVOM']: + elif pipeline.name in settings.GRB_PIPELINES: event = GrbEvent() elif pipeline.name in ['CWB', 'CWB2G']: event = MultiBurstEvent() -- GitLab From f374b4cab193b5637afb9b84d9d0f48e69060de2 Mon Sep 17 00:00:00 2001 From: Alexander Pace <alexander.pace@ligo.org> Date: Wed, 15 Jan 2025 12:51:32 -0600 Subject: [PATCH 3/5] add SGNL to pipelines not approved for production --- config/settings/container/production.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/settings/container/production.py b/config/settings/container/production.py index b6c857625..84340199d 100644 --- a/config/settings/container/production.py +++ b/config/settings/container/production.py @@ -87,4 +87,4 @@ if (DEBUG == True): raise RuntimeError("Turn off debug mode for production") # Hardcode pipelines not approved for production: -UNAPPROVED_PIPELINES += ['aframe', 'GWAK'] +UNAPPROVED_PIPELINES += ['aframe', 'GWAK', 'SGNL'] -- GitLab From 1a1dbea4140ebcc35c2b02e3c57d98028f9c3be4 Mon Sep 17 00:00:00 2001 From: Alexander Pace <alexander.pace@ligo.org> Date: Thu, 16 Jan 2025 12:04:06 -0600 Subject: [PATCH 4/5] add uploaders for sgnl --- .../guardian/0026_update_gstlal_robots.py | 67 ++++++++++++++++ .../guardian/0027_populate_sgnl_uploaders.py | 78 +++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 gracedb/migrations/guardian/0026_update_gstlal_robots.py create mode 100644 gracedb/migrations/guardian/0027_populate_sgnl_uploaders.py diff --git a/gracedb/migrations/guardian/0026_update_gstlal_robots.py b/gracedb/migrations/guardian/0026_update_gstlal_robots.py new file mode 100644 index 000000000..a024e69ad --- /dev/null +++ b/gracedb/migrations/guardian/0026_update_gstlal_robots.py @@ -0,0 +1,67 @@ +from django.db import migrations + +# Creates UserObjectPermission objects which allow specific users +# to add events for pycbc. Also removes users that aren't in the +# collaboration anymore. They couldn't log in and up + +PIPELINE_NAME = 'gstlal' + +USERS_TO_ADD = [ + 'cort.posnansky@ligo.org', + 'gstlalcbc_offline', + ] + +def add_permissions(apps, schema_editor): + User = apps.get_model('auth', 'User') + Permission = apps.get_model('auth', 'Permission') + UserObjectPermission = apps.get_model('guardian', 'UserObjectPermission') + Pipeline = apps.get_model('events', 'Pipeline') + ContentType = apps.get_model('contenttypes', 'ContentType') + + perm = Permission.objects.get(codename='populate_pipeline') + ctype = ContentType.objects.get_for_model(Pipeline) + + pipeline, created = Pipeline.objects.get_or_create(name=PIPELINE_NAME) + + # Now add the new people: + for u in USERS_TO_ADD: + # get the user object: + user, _ = User.objects.get_or_create(username=u) + + # now get the corresponding userobjectpermission: + uop, _ = UserObjectPermission.objects.get_or_create( + user=user, permission=perm, content_type=ctype, + object_pk=pipeline.id) + +def remove_permissions(apps, schema_editor): + User = apps.get_model('auth', 'User') + Permission = apps.get_model('auth', 'Permission') + UserObjectPermission = apps.get_model('guardian', 'UserObjectPermission') + Pipeline = apps.get_model('events', 'Pipeline') + ContentType = apps.get_model('contenttypes', 'ContentType') + + perm = Permission.objects.get(codename='populate_pipeline') + ctype = ContentType.objects.get_for_model(Pipeline) + + pipeline, created = Pipeline.objects.get_or_create(name=PIPELINE_NAME) + # first remove the new people: + for u in USERS_TO_ADD: + # get the user object: + user, _ = User.objects.get_or_create(username=u) + + # now get the corresponding userobjectpermission: + uop, _ = UserObjectPermission.objects.get_or_create( + user=user, permission=perm, content_type=ctype, + object_pk=pipeline.id) + + uop.delete() + +class Migration(migrations.Migration): + + dependencies = [ + ('guardian', '0025_populate_gwak_uploaders'), + ] + + operations = [ + migrations.RunPython(add_permissions, remove_permissions), + ] diff --git a/gracedb/migrations/guardian/0027_populate_sgnl_uploaders.py b/gracedb/migrations/guardian/0027_populate_sgnl_uploaders.py new file mode 100644 index 000000000..f5d5ac2c9 --- /dev/null +++ b/gracedb/migrations/guardian/0027_populate_sgnl_uploaders.py @@ -0,0 +1,78 @@ +from django.db import migrations + +# List of pipeline names and lists of usernames who should +# be allowed to add events for them +PP_LIST = [ + { + 'pipeline': 'SGNL', + 'usernames': [ + 'sgnl-scitoken', + 'yun-jing.huang@ligo.org', + 'chad.hanna@ligo.org', + 'emfollow', + ] + }, +] + + +def add_permissions(apps, schema_editor): + User = apps.get_model('auth', 'User') + Permission = apps.get_model('auth', 'Permission') + UserObjectPermission = apps.get_model('guardian', 'UserObjectPermission') + Pipeline = apps.get_model('events', 'Pipeline') + ContentType = apps.get_model('contenttypes', 'ContentType') + + perm = Permission.objects.get(codename='populate_pipeline') + ctype = ContentType.objects.get_for_model(Pipeline) + + for pp_dict in PP_LIST: + pipeline, created = Pipeline.objects.get_or_create(name=pp_dict['pipeline']) + + # Loop over users + for username in pp_dict['usernames']: + + # get the user object + user, _ = User.objects.get_or_create(username=username) + + # Create UserObjectPermission + uop, uop_created = UserObjectPermission.objects.get_or_create( + user=user, permission=perm, content_type=ctype, + object_pk=pipeline.id) + + +def remove_permissions(apps, schema_editor): + User = apps.get_model('auth', 'User') + Permission = apps.get_model('auth', 'Permission') + UserObjectPermission = apps.get_model('guardian', 'UserObjectPermission') + Pipeline = apps.get_model('events', 'Pipeline') + ContentType = apps.get_model('contenttypes', 'ContentType') + + perm = Permission.objects.get(codename='populate_pipeline') + ctype = ContentType.objects.get_for_model(Pipeline) + + for pp_dict in PP_LIST: + pipeline, created = Pipeline.objects.get_or_create(name=pp_dict['pipeline']) + + # Loop over users + for username in pp_dict['usernames']: + + # get the user object + user, _ = User.objects.get_or_create(username=username) + + # Remove UserObjectPermission + uop, _ = UserObjectPermission.objects.get_or_create( + user=user, permission=perm, content_type=ctype, + object_pk=pipeline.id) + + uop.delete() + +class Migration(migrations.Migration): + + dependencies = [ + ('guardian', '0026_update_gstlal_robots'), + ('events', '0102_add_sgnl_pipeline'), + ] + + operations = [ + migrations.RunPython(add_permissions, remove_permissions), + ] -- GitLab From cce9d50d1a55808b64cef2c5f1a634b68917fb50 Mon Sep 17 00:00:00 2001 From: Alexander Pace <alexander.pace@ligo.org> Date: Mon, 3 Feb 2025 14:06:15 -0600 Subject: [PATCH 5/5] add raven_report and cgmi blessed tags --- config/settings/base.py | 2 + .../migrations/0103_create_blessed_tags.py | 37 +++++++++++++++++++ gracedb/events/templatetags/mediaviews.py | 4 +- 3 files changed, 42 insertions(+), 1 deletion(-) create mode 100644 gracedb/events/migrations/0103_create_blessed_tags.py diff --git a/config/settings/base.py b/config/settings/base.py index 28fa0c49f..88eabc96d 100644 --- a/config/settings/base.py +++ b/config/settings/base.py @@ -245,6 +245,8 @@ BLESSED_TAGS = [ 'pe', 'sig_info', 'audio', + 'raven_report', + 'cgmi', ] # Lists of pipelines used for selecting templates to serve diff --git a/gracedb/events/migrations/0103_create_blessed_tags.py b/gracedb/events/migrations/0103_create_blessed_tags.py new file mode 100644 index 000000000..d2cb895fb --- /dev/null +++ b/gracedb/events/migrations/0103_create_blessed_tags.py @@ -0,0 +1,37 @@ +from django.db import migrations + +# Create a blessed tag for RAVEN: +# https://git.ligo.org/computing/gracedb/server/-/issues/368 +# and for cgmi + +# List of tag names and display names +BLESSED_TAGS = [ + {'name': 'raven_report', 'displayName': 'RAVEN Report'}, + {'name': 'cgmi', 'displayName': 'Coarse-Grained Mass Information'}, +] + +def add_tags(apps, schema_editor): + Tag = apps.get_model('events', 'Tag') + + # Create tags + for tag_dict in BLESSED_TAGS: + tag, created = Tag.objects.get_or_create(name=tag_dict['name']) + if created: + tag.displayName = tag_dict['displayName'] + tag.save() + +def remove_tags(apps, schema_editor): + Tag = apps.get_model('events', 'Tag') + + # Delete tags + Tag.objects.filter(name__in=[s['name'] for s in BLESSED_TAGS]).delete() + +class Migration(migrations.Migration): + + dependencies = [ + ('events', '0102_add_sgnl_pipeline'), + ] + + operations = [ + migrations.RunPython(add_tags, remove_tags), + ] diff --git a/gracedb/events/templatetags/mediaviews.py b/gracedb/events/templatetags/mediaviews.py index 9b9382cbc..3414685e5 100644 --- a/gracedb/events/templatetags/mediaviews.py +++ b/gracedb/events/templatetags/mediaviews.py @@ -29,7 +29,9 @@ blessed_tag_priority_order = [ 'sig_info', 'audio', 'em_follow', - 'pe' + 'pe', + 'raven_report', + 'cgmi', ] -- GitLab