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 %}
-
- {# 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):
{% trans "Loading…" %}