diff --git a/hypha/apply/activity/services.py b/hypha/apply/activity/services.py index 30244596c7..17a22cb1d3 100644 --- a/hypha/apply/activity/services.py +++ b/hypha/apply/activity/services.py @@ -1,5 +1,5 @@ from django.contrib.contenttypes.models import ContentType -from django.db.models import OuterRef, Subquery +from django.db.models import OuterRef, Q, Subquery from django.db.models.functions import JSONObject from django.utils import timezone @@ -37,44 +37,26 @@ def edit_comment(activity: Activity, message: str) -> Activity: return activity -def get_related_actions_for_user(obj, user): - """Return Activity objects related to an object, esp. useful with - ApplicationSubmission and Project. - - Args: - obj: instance of a model class - user: user who these actions are visible to. - - Returns: - [`Activity`][hypha.apply.activity.models.Activity] queryset - """ - related_query = type(obj).activities.rel.related_query_name - - return ( - Activity.actions.filter(**{related_query: obj}) - .select_related("user") - .prefetch_related( - "related_object", - ) - .visible_to(user) - ) - - -def get_related_comments_for_user(obj, user): +def get_related_activities_for_user(obj, user): """Return comments/communications related to an object, esp. useful with ApplicationSubmission and Project. Args: - obj: instance of a model class + obj: instance of either an [`ApplicationSubmission`][hypha.apply.funds.models.submissions.ApplicationSubmission] or [`Project`][hypha.apply.projects.models.project.Project]. user: user who these actions are visible to. Returns: [`Activity`][hypha.apply.activity.models.Activity] queryset """ - related_query = type(obj).activities.rel.related_query_name + if hasattr(obj, "project") and obj.project: + source_filter = Q(submission=obj) | Q(project=obj.project) + if hasattr(obj, "submission") and obj.submission: + source_filter = Q(submission=obj.submission) | Q(project=obj) + else: + source_filter = Q(submission=obj) queryset = ( - Activity.objects.filter(**{related_query: obj}) + Activity.objects.filter(source_filter) .exclude(current=False) .select_related("user") .prefetch_related( diff --git a/hypha/apply/activity/templates/activity/include/comment_list.html b/hypha/apply/activity/templates/activity/include/activity_list.html similarity index 93% rename from hypha/apply/activity/templates/activity/include/comment_list.html rename to hypha/apply/activity/templates/activity/include/activity_list.html index be8cd1f7c6..53026d7324 100644 --- a/hypha/apply/activity/templates/activity/include/comment_list.html +++ b/hypha/apply/activity/templates/activity/include/activity_list.html @@ -4,7 +4,7 @@
- {% for activity in comments %} + {% for activity in activities %} {% if activity.type == "comment" %} {% include "activity/ui/activity-comment-item.html" with activity=activity %} {% else %} @@ -25,7 +25,7 @@ {% endif %}
- {% if not comments %} + {% if not activities %} {% trans "No comments available" %} {% endif %}
diff --git a/hypha/apply/activity/templatetags/activity_tags.py b/hypha/apply/activity/templatetags/activity_tags.py index 23733ab684..383e16beba 100644 --- a/hypha/apply/activity/templatetags/activity_tags.py +++ b/hypha/apply/activity/templatetags/activity_tags.py @@ -98,7 +98,11 @@ def visibility_options(activity, user) -> str: Returns: A JSON string of visibility options """ - submission_partner_list = activity.source.partners.all() + if hasattr(activity.source, "partners"): + submission_partner_list = activity.source.partners.all() + else: + submission_partner_list = activity.source.submission.partners.all() + choices = activity.visibility_choices_for(user, submission_partner_list) return json.dumps(choices) diff --git a/hypha/apply/activity/views.py b/hypha/apply/activity/views.py index d87338de5c..dcb8eca8f2 100644 --- a/hypha/apply/activity/views.py +++ b/hypha/apply/activity/views.py @@ -31,7 +31,7 @@ def partial_comments(request, content_type: str, pk: int): This view handles comments for both 'submission' and 'project' content types. It checks the user's permissions and fetches the related comments for the user. - The comments are paginated and rendered in the 'comment_list' template. + The comments are paginated and rendered in the 'activity_list' template. Args: request (HttpRequest): The HTTP request object. @@ -39,7 +39,7 @@ def partial_comments(request, content_type: str, pk: int): pk (int): The primary key of the content object. Returns: - HttpResponse: The rendered 'comment_list' template with the context data. + HttpResponse: The rendered 'activity_list' template with the context data. """ if content_type == "submission": obj = get_object_or_404(ApplicationSubmission, pk=pk) @@ -54,17 +54,17 @@ def partial_comments(request, content_type: str, pk: int): ) editable = False if obj.status == "complete" else True else: - return render(request, "activity/include/comment_list.html", {}) + return render(request, "activity/include/activity_list.html", {}) - qs = services.get_related_comments_for_user(obj, request.user) + qs = services.get_related_activities_for_user(obj, request.user) page = Paginator(qs, per_page=10, orphans=5).page(request.GET.get("page", 1)) ctx = { "page": page, - "comments": page.object_list, + "activities": page.object_list, "editable": editable, } - return render(request, "activity/include/comment_list.html", ctx) + return render(request, "activity/include/activity_list.html", ctx) @login_required @@ -98,16 +98,22 @@ class ActivityContextMixin: """Mixin to add related 'comments' of the current view's 'self.object'""" def get_context_data(self, **kwargs): - extra = { - # Do not prefetch on the related_object__author as the models - # are not homogeneous and this will fail - "comments": services.get_related_comments_for_user( - self.object, self.request.user - ), - "comments_count": services.get_comment_count( - self.object, self.request.user - ), - } + # Do not prefetch on the related_object__author as the models + # are not homogeneous and this will fail + user = self.request.user + activities = services.get_related_activities_for_user( + self.object, self.request.user + ) + + # Comments for both projects and applications exist under the original application + if isinstance(self.object, ApplicationSubmission): + application_obj = self.object + else: + application_obj = self.object.submission + + comments_count = services.get_comment_count(application_obj, user) + + extra = {"activities": activities, "comments_count": comments_count} return super().get_context_data(**extra, **kwargs) diff --git a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html index fb131d7730..eaaff92c36 100644 --- a/hypha/apply/funds/templates/funds/applicationsubmission_detail.html +++ b/hypha/apply/funds/templates/funds/applicationsubmission_detail.html @@ -247,17 +247,6 @@

{% trans "Add communication" %}

- - {# Tab 3 #} -
-
-

{% trans "Loading…" %}

-
-
{% endblock %} diff --git a/hypha/apply/funds/urls.py b/hypha/apply/funds/urls.py index 7b3bc9344b..15404748b3 100644 --- a/hypha/apply/funds/urls.py +++ b/hypha/apply/funds/urls.py @@ -22,7 +22,6 @@ partial_reviews_card, partial_reviews_decisions, partial_screening_card, - partial_submission_activities, partial_submission_answers, partial_submission_lead, sub_menu_bulk_update_lead, @@ -172,11 +171,6 @@ partial_submission_lead, name="partial-get-lead", ), - path( - "partial/activities/", - partial_submission_activities, - name="partial-activities", - ), path("lead/update/", UpdateLeadView.as_view(), name="lead_update"), path("archive/", htmx_archive_unarchive_submission, name="archive"), path( diff --git a/hypha/apply/funds/views/partials.py b/hypha/apply/funds/views/partials.py index 77db81fcfc..b6dd2dfa5e 100644 --- a/hypha/apply/funds/views/partials.py +++ b/hypha/apply/funds/views/partials.py @@ -15,9 +15,6 @@ from wagtail.models import Page from hypha.apply.activity.messaging import MESSAGES, messenger -from hypha.apply.activity.services import ( - get_related_actions_for_user, -) from hypha.apply.categories.models import MetaTerm, Option from hypha.apply.funds.forms import BatchUpdateReviewersForm from hypha.apply.funds.models.reviewer_role import ReviewerRole @@ -225,17 +222,6 @@ def sub_menu_category_options(request): return render(request, "submissions/submenu/category.html", ctx) -@login_required -@require_http_methods(["GET"]) -def partial_submission_activities(request, pk): - submission = get_object_or_404(ApplicationSubmission, pk=pk) - has_permission( - "submission_view", request.user, object=submission, raise_exception=True - ) - ctx = {"actions": get_related_actions_for_user(submission, request.user)} - return render(request, "activity/include/action_list.html", ctx) - - @login_required @require_http_methods(["GET"]) def partial_reviews_card(request: HttpRequest, pk: str) -> HttpResponse: diff --git a/hypha/apply/projects/migrations/0097_move_project_comments_to_application.py b/hypha/apply/projects/migrations/0097_move_project_comments_to_application.py new file mode 100644 index 0000000000..50e654226a --- /dev/null +++ b/hypha/apply/projects/migrations/0097_move_project_comments_to_application.py @@ -0,0 +1,28 @@ +# Generated by Django 4.2.17 on 2025-01-07 17:57 + +from django.db import migrations +from hypha.apply.activity.models import COMMENT + + +def migrate_project_comments_to_application(apps, schema_editor): + Activity = apps.get_model("activity", "Activity") + ContentType = apps.get_model("contenttypes", "ContentType") + Project = apps.get_model("application_projects", "Project") + ApplicationSubmission = apps.get_model("funds", "ApplicationSubmission") + project_type = ContentType.objects.get_for_model(Project) + application_type = ContentType.objects.get_for_model(ApplicationSubmission) + for comment in Activity.objects.filter( + type=COMMENT, source_content_type=project_type + ): + application = Project.objects.get(id=comment.source_object_id).submission + comment.source_content_type = application_type + comment.source_object_id = application.id + comment.save() + + +class Migration(migrations.Migration): + dependencies = [ + ("application_projects", "0096_remove_invoicedeliverable_deliverable_and_more"), + ] + + operations = [migrations.RunPython(migrate_project_comments_to_application)] diff --git a/hypha/apply/projects/templates/application_projects/project_detail.html b/hypha/apply/projects/templates/application_projects/project_detail.html index 3a32da707f..5332b81491 100644 --- a/hypha/apply/projects/templates/application_projects/project_detail.html +++ b/hypha/apply/projects/templates/application_projects/project_detail.html @@ -1,6 +1,6 @@ {% extends "base-apply.html" %} -{% load i18n contract_tools static wagtailcore_tags approval_tools invoice_tools project_tags %} +{% load i18n static wagtailcore_tags approval_tools invoice_tools project_tags %} {% load heroicons %} {% block title %}{{ object.title }}{% endblock %} @@ -208,31 +208,19 @@
{% trans "Project form approvals" %}
{% if not object.is_archive %}

{% trans "Add communication" %}

- {% include "activity/include/comment_form.html" %} + {% include "activity/include/comment_form.html" with action=object.submission.get_absolute_url %} {% endif %}

{% trans "Loading…" %}

- - {# Tab 3 #} -
-
- {% comment %} Loaded using the htmx via alpine's custom event "open-tab-3"{% endcomment %} -

{% trans "Loading…" %}

-
-
{% endblock content %} diff --git a/hypha/apply/projects/urls.py b/hypha/apply/projects/urls.py index d4dcba57d9..31b78122ca 100644 --- a/hypha/apply/projects/urls.py +++ b/hypha/apply/projects/urls.py @@ -51,7 +51,6 @@ partial_get_invoice_detail_actions, partial_get_invoice_status, partial_get_invoice_status_table, - partial_project_activities, partial_project_lead, partial_project_title, partial_supporting_documents, @@ -77,11 +76,6 @@ include( [ path("", ProjectDetailView.as_view(), name="detail"), - path( - "partial/activities/", - partial_project_activities, - name="partial-activities", - ), path("partial/lead/", partial_project_lead, name="project_lead"), path("partial/title/", partial_project_title, name="project_title"), path("lead/update/", UpdateLeadView.as_view(), name="lead_update"), diff --git a/hypha/apply/projects/views/__init__.py b/hypha/apply/projects/views/__init__.py index 08425c8656..732e191a0b 100644 --- a/hypha/apply/projects/views/__init__.py +++ b/hypha/apply/projects/views/__init__.py @@ -46,7 +46,6 @@ partial_get_invoice_detail_actions, partial_get_invoice_status, partial_get_invoice_status_table, - partial_project_activities, partial_project_lead, partial_project_title, partial_supporting_documents, @@ -62,7 +61,6 @@ ) __all__ = [ - "partial_project_activities", "partial_project_lead", "partial_project_title", "partial_supporting_documents", diff --git a/hypha/apply/projects/views/project_partials.py b/hypha/apply/projects/views/project_partials.py index 046783f818..2df63e4c44 100644 --- a/hypha/apply/projects/views/project_partials.py +++ b/hypha/apply/projects/views/project_partials.py @@ -10,15 +10,11 @@ from django.views.decorators.http import require_GET from hypha.apply.activity.models import Activity -from hypha.apply.activity.services import ( - get_related_actions_for_user, -) from hypha.apply.funds.utils import get_statuses_as_params from ..constants import statuses_and_table_statuses_mapping from ..models.payment import Invoice from ..models.project import ContractDocumentCategory, DocumentCategory, Project -from ..permissions import has_permission from ..utils import get_project_status_choices @@ -38,15 +34,6 @@ def partial_project_title(request, pk): ) -@login_required -@require_GET -def partial_project_activities(request, pk): - project = get_object_or_404(Project, pk=pk) - has_permission("project_access", request.user, object=project, raise_exception=True) - ctx = {"actions": get_related_actions_for_user(project, request.user)} - return render(request, "activity/include/action_list.html", ctx) - - @login_required @require_GET def partial_supporting_documents(request, pk):