From 5fba59176ddb8b40afc8b24fb2374376a3a3641d Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Mon, 27 May 2024 20:17:47 -0400 Subject: [PATCH 01/12] updated queries and views to allow cohosts to both see/respond to participation requests --- physionet-django/events/models.py | 6 +++++ .../events/templates/events/event_home.html | 2 +- physionet-django/events/views.py | 22 ++++++++++++++++--- 3 files changed, 26 insertions(+), 4 deletions(-) diff --git a/physionet-django/events/models.py b/physionet-django/events/models.py index 0842c97d36..10a98f02ba 100644 --- a/physionet-django/events/models.py +++ b/physionet-django/events/models.py @@ -59,6 +59,12 @@ def get_cohosts(self): """ return self.participants.filter(is_cohost=True) + def get_cohost_ids(self): + """ + Returns a list of cohost ids for the event. + """ + return self.participants.filter(is_cohost=True).values_list('user', flat=True) + class EventParticipant(models.Model): """ diff --git a/physionet-django/events/templates/events/event_home.html b/physionet-django/events/templates/events/event_home.html index 34488c14ba..cef23ff269 100644 --- a/physionet-django/events/templates/events/event_home.html +++ b/physionet-django/events/templates/events/event_home.html @@ -82,7 +82,7 @@

{{ event.title }}

Registration status: On waiting list
{% endif %} - {% if event.host == user %} + {% if user == event.host or user.id in event.get_cohost_ids %} Share the class code: {{ url_prefix }}{% url 'event_detail' event.slug %}

diff --git a/physionet-django/events/views.py b/physionet-django/events/views.py index 6cb962511e..5c493bfaa2 100644 --- a/physionet-django/events/views.py +++ b/physionet-django/events/views.py @@ -109,6 +109,9 @@ def event_home(request): form_error = False + cohost_ids = EventParticipant.objects.filter( + user=user, is_cohost=True).values_list("event__id", flat=True) + # handle notifications to join an event if request.method == "POST" and "participation_response" in request.POST.keys(): formset = EventApplicationResponseFormSet(request.POST) @@ -123,7 +126,13 @@ def event_home(request): event_application = form.save(commit=False) event = event_application.event - if event.host != user: + # if user is not a host or a participant with cohort status, they don't have permission to accept/reject + if not ( + event.host == user + or EventParticipant.objects.filter( + event=event, user=user, is_cohost=True + ).exists() + ): messages.error( request, "You don't have permission to accept/reject this application", @@ -228,14 +237,21 @@ def event_home(request): # get all participation requests for Active events where the current user is the host and the participants are # waiting for a response + + # making the query to get all the participation requests for the events + # where the user is the host or an event participant with cohort status participation_requests = EventApplication.objects.filter( - status=EventApplication.EventApplicationStatus.WAITLISTED - ).filter(event__host=user, event__end_date__gte=datetime.now()) + Q(event__host=user) | Q(event__id__in=cohost_ids), + status=EventApplication.EventApplicationStatus.WAITLISTED, + event__end_date__gte=datetime.now(), + ) + participation_response_formset = EventApplicationResponseFormSet( queryset=participation_requests ) invitation_response_formset = InvitationResponseFormSet( queryset=CohostInvitation.get_user_invitations(user)) + return render( request, "events/event_home.html", From 8398fdf16472d20bf0d9cc57960b2b2fa87d0ac3 Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Mon, 27 May 2024 21:10:48 -0400 Subject: [PATCH 02/12] removed redundant comments --- physionet-django/events/views.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/physionet-django/events/views.py b/physionet-django/events/views.py index 5c493bfaa2..8030a449c5 100644 --- a/physionet-django/events/views.py +++ b/physionet-django/events/views.py @@ -235,9 +235,6 @@ def event_home(request): }, ] - # get all participation requests for Active events where the current user is the host and the participants are - # waiting for a response - # making the query to get all the participation requests for the events # where the user is the host or an event participant with cohort status participation_requests = EventApplication.objects.filter( From c3d5bd1e9fed7b80049c523e430e59993f5d904c Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Wed, 29 May 2024 11:55:26 -0400 Subject: [PATCH 03/12] Addressing the comments (improved UI for clarity & better security to ensure proper dataacess only during the event --- .../console/event_management_manage_dataset.html | 3 +++ physionet-django/console/views.py | 1 + physionet-django/events/models.py | 6 ++++-- .../events/templates/events/event_home.html | 4 +++- .../templates/events/event_notifications.html | 16 ++++++++++++++++ physionet-django/events/views.py | 7 +++++++ 6 files changed, 34 insertions(+), 3 deletions(-) diff --git a/physionet-django/console/templates/console/event_management_manage_dataset.html b/physionet-django/console/templates/console/event_management_manage_dataset.html index 6db04a7d52..91d6c7e647 100644 --- a/physionet-django/console/templates/console/event_management_manage_dataset.html +++ b/physionet-django/console/templates/console/event_management_manage_dataset.html @@ -4,6 +4,9 @@
  • Add a dataset to the event
    +

    By adding a dataset, all participants (approved by {{ event.host.username }} or their cohosts) will be allowed to access this + dataset for the duration of the event (from {{ event.start_date }} to {{ event.end_date }}). There are currently {{ event.participants.count }} approved + participants in this event. Note that once a dataset is added to the event, the dates of the event cannot be changed.

    {% csrf_token %} {% include 'form_snippet.html' with form=event_dataset_form %} diff --git a/physionet-django/console/views.py b/physionet-django/console/views.py index f7cd7f4868..d12a51b35a 100644 --- a/physionet-django/console/views.py +++ b/physionet-django/console/views.py @@ -3134,6 +3134,7 @@ def event_archive(request): }) +@console_permission_required('user.add_event_dataset') @console_permission_required('user.view_all_events') def event_management(request, event_slug): """ diff --git a/physionet-django/events/models.py b/physionet-django/events/models.py index 10a98f02ba..66ca2619ee 100644 --- a/physionet-django/events/models.py +++ b/physionet-django/events/models.py @@ -29,7 +29,8 @@ class Event(models.Model): class Meta: unique_together = ('title', 'host') permissions = [('view_all_events', 'Can view all events in the console'), - ('view_event_menu', 'Can view event menu in the navbar')] + ('view_event_menu', 'Can view event menu in the navbar'), + ('add_event_dataset', 'Can add a dataset to an event'),] def save(self, *args, **kwargs): if not self.slug: @@ -224,7 +225,8 @@ def is_accessible(self): if not self.is_active: return False - if timezone.now().date() > self.event.end_date: + if ((timezone.now().date() > self.event.end_date) + or (timezone.now().date() < self.event.start_date)): return False return True diff --git a/physionet-django/events/templates/events/event_home.html b/physionet-django/events/templates/events/event_home.html index cef23ff269..24383f6991 100644 --- a/physionet-django/events/templates/events/event_home.html +++ b/physionet-django/events/templates/events/event_home.html @@ -88,7 +88,9 @@

    {{ event.title }}

    - + + {% endif %} + {% if user == event.host %} Edit Event {% endif %} diff --git a/physionet-django/events/templates/events/event_notifications.html b/physionet-django/events/templates/events/event_notifications.html index d9305f7c01..e676830628 100644 --- a/physionet-django/events/templates/events/event_notifications.html +++ b/physionet-django/events/templates/events/event_notifications.html @@ -53,6 +53,22 @@ diff --git a/physionet-django/events/views.py b/physionet-django/events/views.py index 8030a449c5..1f549d50db 100644 --- a/physionet-django/events/views.py +++ b/physionet-django/events/views.py @@ -22,9 +22,16 @@ @login_required def update_event(request, event_slug, **kwargs): + user = request.user can_change_event = user.has_perm('events.add_event') event = Event.objects.get(slug=event_slug) + + # if the event has dataset added to it, it cannot be edited + if event.datasets.exists(): + messages.error(request, "Event with datasets cannot be edited") + return redirect(reverse('event_detail', args=[event_slug])) + if request.method == 'POST': event_form = AddEventForm(user=user, data=request.POST, instance=event) if event_form.is_valid(): From f649eeb9e4629ddcff40872bb9f7be50304e0d25 Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Wed, 29 May 2024 12:06:06 -0400 Subject: [PATCH 04/12] adding the required migrations to alter the event permissions --- .../migrations/0010_alter_event_options.py | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 physionet-django/events/migrations/0010_alter_event_options.py diff --git a/physionet-django/events/migrations/0010_alter_event_options.py b/physionet-django/events/migrations/0010_alter_event_options.py new file mode 100644 index 0000000000..619353245b --- /dev/null +++ b/physionet-django/events/migrations/0010_alter_event_options.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.11 on 2024-05-29 16:05 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("events", "0009_cohostinvitation"), + ] + + operations = [ + migrations.AlterModelOptions( + name="event", + options={ + "permissions": [ + ("view_all_events", "Can view all events in the console"), + ("view_event_menu", "Can view event menu in the navbar"), + ("add_event_dataset", "Can add a dataset to an event"), + ] + }, + ), + ] From b52836bbfe3835fa225495948605159cec173686 Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Wed, 29 May 2024 12:10:45 -0400 Subject: [PATCH 05/12] Fixing styling issues --- physionet-django/events/models.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physionet-django/events/models.py b/physionet-django/events/models.py index 66ca2619ee..d9575c6757 100644 --- a/physionet-django/events/models.py +++ b/physionet-django/events/models.py @@ -30,7 +30,7 @@ class Meta: unique_together = ('title', 'host') permissions = [('view_all_events', 'Can view all events in the console'), ('view_event_menu', 'Can view event menu in the navbar'), - ('add_event_dataset', 'Can add a dataset to an event'),] + ('add_event_dataset', 'Can add a dataset to an event')] def save(self, *args, **kwargs): if not self.slug: From 5458606ee5b1963ef34db5c4c5b3919d485108da Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Thu, 30 May 2024 17:15:17 -0400 Subject: [PATCH 06/12] Updating the accessible projects(to be tested) --- physionet-django/console/views.py | 2 +- physionet-django/events/forms.py | 14 +++++++++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/physionet-django/console/views.py b/physionet-django/console/views.py index d12a51b35a..e95f9deaa4 100644 --- a/physionet-django/console/views.py +++ b/physionet-django/console/views.py @@ -3173,7 +3173,7 @@ def event_management(request, event_slug): return redirect("event_management", event_slug=event_slug) else: - event_dataset_form = EventDatasetForm() + event_dataset_form = EventDatasetForm(user=request.user) participants = selected_event.participants.all() pending_applications = selected_event.applications.filter( diff --git a/physionet-django/events/forms.py b/physionet-django/events/forms.py index 6c304abfa7..79692ff1e0 100644 --- a/physionet-django/events/forms.py +++ b/physionet-django/events/forms.py @@ -3,6 +3,7 @@ from events.widgets import DatePickerInput from events.models import Event, EventApplication, EventAgreement, EventDataset, CohostInvitation from project.models import PublishedProject +from user.models import CredentialApplication INVITATION_CHOICES = ( (1, 'Accept'), @@ -72,7 +73,18 @@ class EventDatasetForm(forms.ModelForm): """ A form for adding datasets to an event. """ - dataset = forms.ModelChoiceField(queryset=PublishedProject.objects.all(), + + def __init__(self, *args, **kwargs): + user = kwargs.pop('user') + super(EventDatasetForm, self).__init__(*args, **kwargs) + + # Get the projects that the user is credentialed to access + projects = PublishedProject.objects.accessible_by(user) + + # Update the queryset of the 'dataset' field + self.fields['dataset'].queryset = projects + + dataset = forms.ModelChoiceField(queryset=PublishedProject.objects.none(), widget=forms.Select(attrs={'class': 'form-control'})) class Meta: From cf8c087e35d4eb10e52fef646387be6916eb6213 Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Thu, 30 May 2024 23:07:36 -0400 Subject: [PATCH 07/12] updated the manager queryset to not include event datasets for the query --- physionet-django/console/views.py | 4 ++-- physionet-django/events/forms.py | 2 +- .../project/managers/publishedproject.py | 21 ++++++++++--------- 3 files changed, 14 insertions(+), 13 deletions(-) diff --git a/physionet-django/console/views.py b/physionet-django/console/views.py index e95f9deaa4..5ab6b1350c 100644 --- a/physionet-django/console/views.py +++ b/physionet-django/console/views.py @@ -3134,8 +3134,8 @@ def event_archive(request): }) -@console_permission_required('user.add_event_dataset') -@console_permission_required('user.view_all_events') +@console_permission_required('event.add_event_dataset') +@console_permission_required('event.view_all_events') def event_management(request, event_slug): """ Admin page for managing an individual Event. diff --git a/physionet-django/events/forms.py b/physionet-django/events/forms.py index 79692ff1e0..77082776e9 100644 --- a/physionet-django/events/forms.py +++ b/physionet-django/events/forms.py @@ -79,7 +79,7 @@ def __init__(self, *args, **kwargs): super(EventDatasetForm, self).__init__(*args, **kwargs) # Get the projects that the user is credentialed to access - projects = PublishedProject.objects.accessible_by(user) + projects = PublishedProject.objects.accessible_by(user, include_event_datatsets=False) # Update the queryset of the 'dataset' field self.fields['dataset'].queryset = projects diff --git a/physionet-django/project/managers/publishedproject.py b/physionet-django/project/managers/publishedproject.py index 15c90794d7..36cf7ad885 100644 --- a/physionet-django/project/managers/publishedproject.py +++ b/physionet-django/project/managers/publishedproject.py @@ -8,7 +8,7 @@ class PublishedProjectManager(Manager): - def accessible_by(self, user): + def accessible_by(self, user, include_event_datatsets=True): """ Return all published projects accessible by a specified user Part of the `hdn-research-environment` app contract @@ -52,14 +52,15 @@ def accessible_by(self, user): contributor_review_with_access | credentialed_with_dua_signed ) - # add projects that are accessible through events - events_all = Event.objects.filter(Q(host=user) | Q(participants__user=user)) - active_events = set(events_all.filter(end_date__gte=datetime.now())) - accessible_datasets = EventDataset.objects.filter(event__in=active_events, is_active=True) - accessible_projects_ids = [] - for event_dataset in accessible_datasets: - if has_access_to_event_dataset(user, event_dataset): - accessible_projects_ids.append(event_dataset.dataset.id) - query |= Q(id__in=accessible_projects_ids) + if include_event_datatsets: + # add projects that are accessible through events + events_all = Event.objects.filter(Q(host=user) | Q(participants__user=user)) + active_events = set(events_all.filter(end_date__gte=datetime.now())) + accessible_datasets = EventDataset.objects.filter(event__in=active_events, is_active=True) + accessible_projects_ids = [] + for event_dataset in accessible_datasets: + if has_access_to_event_dataset(user, event_dataset): + accessible_projects_ids.append(event_dataset.dataset.id) + query |= Q(id__in=accessible_projects_ids) return self.filter(query).distinct() From 51361be05633f3eb046afaf0b8e5c92fb8ebf6d5 Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Thu, 30 May 2024 23:13:42 -0400 Subject: [PATCH 08/12] wrapped the added statements in dataset exists condition --- .../events/templates/events/event_notifications.html | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/physionet-django/events/templates/events/event_notifications.html b/physionet-django/events/templates/events/event_notifications.html index e676830628..5bbe690b2b 100644 --- a/physionet-django/events/templates/events/event_notifications.html +++ b/physionet-django/events/templates/events/event_notifications.html @@ -54,6 +54,8 @@

    {{ participation_response_form.instance.user.get_full_name }} is requesting to join Event {{ participation_response_form.instance.event.title }}.

    + {% if participation_response_form.instance.event.datasets.exists %} +

    If a participant is approved to join this event, the participant will be allowed to access the following datasets for the duration of the event ({{ participation_response_form.instance.event.start_date }} @@ -67,6 +69,8 @@

+ {% endif %} +
{{ participation_response_form }} From 204ee9499174d3f8c7213ec4bebe11fdc8917064 Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Thu, 30 May 2024 23:31:39 -0400 Subject: [PATCH 09/12] removed the redundant cohost_ids method and implemented templatetags --- physionet-django/events/templates/events/event_home.html | 2 +- .../events/templatetags/participation_status.py | 8 ++++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/physionet-django/events/templates/events/event_home.html b/physionet-django/events/templates/events/event_home.html index 24383f6991..2fda6ac019 100644 --- a/physionet-django/events/templates/events/event_home.html +++ b/physionet-django/events/templates/events/event_home.html @@ -82,7 +82,7 @@

{{ event.title }}

Registration status: On waiting list
{% endif %} - {% if user == event.host or user.id in event.get_cohost_ids %} + {% if user|can_view_participants:event %} Share the class code: {{ url_prefix }}{% url 'event_detail' event.slug %}

diff --git a/physionet-django/events/templatetags/participation_status.py b/physionet-django/events/templatetags/participation_status.py index d67bbfd797..f23a468f67 100644 --- a/physionet-django/events/templatetags/participation_status.py +++ b/physionet-django/events/templatetags/participation_status.py @@ -30,3 +30,11 @@ def has_access_to_event_dataset(user, dataset): @register.filter(name="get_applicant_info") def get_applicant_info(event_details, event_id): return event_details[event_id] + + +@register.filter(name="can_view_participants") +def can_view_participants(user, event): + # check if the user is a host or an event participant with is_cohost flag set to True + return event.host == user or event.participants.filter( + user=user, is_cohost=True + ).exists() From 074f95a2bac1f174a5c6c5df5d8112ecc1ccceff Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Thu, 30 May 2024 23:52:17 -0400 Subject: [PATCH 10/12] removed redundant model method in events --- physionet-django/events/models.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/physionet-django/events/models.py b/physionet-django/events/models.py index d9575c6757..c4ae99a6a8 100644 --- a/physionet-django/events/models.py +++ b/physionet-django/events/models.py @@ -60,12 +60,6 @@ def get_cohosts(self): """ return self.participants.filter(is_cohost=True) - def get_cohost_ids(self): - """ - Returns a list of cohost ids for the event. - """ - return self.participants.filter(is_cohost=True).values_list('user', flat=True) - class EventParticipant(models.Model): """ From eeb586bb6882a6015f8b7e89be4afc345b4777a9 Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Thu, 27 Jun 2024 15:01:17 -0400 Subject: [PATCH 11/12] Addressed the typo of datatsets --- physionet-django/project/managers/publishedproject.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/physionet-django/project/managers/publishedproject.py b/physionet-django/project/managers/publishedproject.py index 36cf7ad885..55d2aadc08 100644 --- a/physionet-django/project/managers/publishedproject.py +++ b/physionet-django/project/managers/publishedproject.py @@ -8,7 +8,7 @@ class PublishedProjectManager(Manager): - def accessible_by(self, user, include_event_datatsets=True): + def accessible_by(self, user, include_event_datasets=True): """ Return all published projects accessible by a specified user Part of the `hdn-research-environment` app contract @@ -52,7 +52,7 @@ def accessible_by(self, user, include_event_datatsets=True): contributor_review_with_access | credentialed_with_dua_signed ) - if include_event_datatsets: + if include_event_datasets: # add projects that are accessible through events events_all = Event.objects.filter(Q(host=user) | Q(participants__user=user)) active_events = set(events_all.filter(end_date__gte=datetime.now())) From 107f502fc52d90ca627dfd45d00f0d46314f6fa1 Mon Sep 17 00:00:00 2001 From: rutvikrj26 Date: Thu, 27 Jun 2024 18:19:19 -0400 Subject: [PATCH 12/12] updated datatsets in the forms.py --- physionet-django/events/forms.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/physionet-django/events/forms.py b/physionet-django/events/forms.py index 77082776e9..dde1bc1611 100644 --- a/physionet-django/events/forms.py +++ b/physionet-django/events/forms.py @@ -79,7 +79,7 @@ def __init__(self, *args, **kwargs): super(EventDatasetForm, self).__init__(*args, **kwargs) # Get the projects that the user is credentialed to access - projects = PublishedProject.objects.accessible_by(user, include_event_datatsets=False) + projects = PublishedProject.objects.accessible_by(user, include_event_datasets=False) # Update the queryset of the 'dataset' field self.fields['dataset'].queryset = projects