Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge application and project communication tabs #4349

Open
wants to merge 6 commits into
base: main
Choose a base branch
from

Conversation

wes-otf
Copy link
Contributor

@wes-otf wes-otf commented Jan 17, 2025

Fixes #4249. Migrates all project comments to the associated application, and loads the applications comments via htmx (merging in project activities). This was the best solution I could come up with until we potentially take a step a coupling the two closer (ie. sharing an ID (#3944), a URL space, and/or a detail view).

I had tried another version of this that did the same migration but loaded the project view via htmx from the application detail view, which made everything feel more consistent, but I had concerns about the user needed to see the status/stages bar when making a comment which is where the UI got hairy.

Would love any suggestions/critiques!

Test Steps

Ensure...

  • ...the comments section on an application is identical to that of it's associated project
  • ...leaving a comment works as expected
  • ...applications without a project have unchanged communications tabs

@wes-otf wes-otf added Type: Enhancement This is an improvement of an existing thing (not a new thing, which would be a feature). Type: Minor Minor change, used in release drafter labels Jan 17, 2025
Copy link
Member

@frjo frjo left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In my initial testing it seems to work well. Some minor comments.

{% comment %} Loaded using the htmx via alpine's custom event "open-tab-3"{% endcomment %}
<p>{% trans "Loading…" %}</p>
</div>
</div>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can delete this part from applicationsubmission_detail.html as well. I think we might even clear out all partial-activities urls and partial_submission_activities() unless they are still used somewhere else.

@@ -208,31 +208,19 @@ <h5>{% trans "Project form approvals" %}</h5>
<div class="feed">
{% if not object.is_archive %}
<h4 class="m-0 sr-only">{% trans "Add communication" %}</h4>
{% include "activity/include/comment_form.html" %}
{% include "activity/include/comment_form.html" with action=object.submission.get_absolute_url %}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works but feels a bit, un neat. You go to projects/p:n/#communications, submit the form and end up on submissions/s:n/#communications. Not that normal users look at the address bar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a nice catch, I didn't even notice that was the case with the redirect. Do you think it should be handled in htmx so the redirect doesn't happen?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or does it make sense now to break out communications to its own view and url?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good idea, I can explore that a bit

Copy link
Contributor Author

@wes-otf wes-otf Jan 21, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another (more temporary) solution is to change up the get_success_url to resolve to the referrer URL:

So swapping

def get_success_url(self):
return self.object.source.get_absolute_url() + "#communications"

with

    def get_success_url(self):
        if (ref := self.request.META.get("HTTP_REFERER")) and (path := urlparse(ref).path):
            try:
                url_resolved = resolve(path)
                if url_resolved.url_name == "detail":
                    return f"{path}#communications"
            except Resolver404:
                # If it couldn't be resolved, use the default `get_absolute_url()`
                pass
        return f"{self.object.source.get_absolute_url()}#communications"

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The redirect is not a big issue so I do not tink we need hack around it.

If creating a stand alone view/url is a bit big we can do it in a separate PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Might be better to break the PRs up, I can take a stab at breaking it out into it's own view

Copy link
Member

@theskumar theskumar left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this works, there is another approach that you can try.

hypha/apply/activity/views.py Outdated Show resolved Hide resolved
@frjo frjo added Status: Needs testing Tickets that need testing/qa Status: Needs dev testing 🧑‍💻 Tasks that should be tested by the dev team and removed Status: Needs testing Tickets that need testing/qa Status: Needs dev testing 🧑‍💻 Tasks that should be tested by the dev team labels Jan 31, 2025
@frjo
Copy link
Member

frjo commented Jan 31, 2025

@wes-otf Trying to put this on test I get this error:

  Applying application_projects.0097_move_project_comments_to_application...Traceback (most recent call last):
  File "/app/manage.py", line 10, in <module>
    execute_from_command_line(sys.argv)
  File "/app/.heroku/python/lib/python3.12/site-packages/django/core/management/__init__.py", line 442, in execute_from_command_line
    utility.execute()
  File "/app/.heroku/python/lib/python3.12/site-packages/django/core/management/__init__.py", line 436, in execute
    self.fetch_command(subcommand).run_from_argv(self.argv)
  File "/app/.heroku/python/lib/python3.12/site-packages/django/core/management/base.py", line 412, in run_from_argv
    self.execute(*args, **cmd_options)
  File "/app/.heroku/python/lib/python3.12/site-packages/django/core/management/base.py", line 458, in execute
    output = self.handle(*args, **options)
             ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.heroku/python/lib/python3.12/site-packages/django/core/management/base.py", line 106, in wrapper
    res = handle_func(*args, **kwargs)
          ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.heroku/python/lib/python3.12/site-packages/django/core/management/commands/migrate.py", line 356, in handle
    post_migrate_state = executor.migrate(
                         ^^^^^^^^^^^^^^^^^
  File "/app/.heroku/python/lib/python3.12/site-packages/django/db/migrations/executor.py", line 135, in migrate
    state = self._migrate_all_forwards(
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.heroku/python/lib/python3.12/site-packages/django/db/migrations/executor.py", line 167, in _migrate_all_forwards
    state = self.apply_migration(
            ^^^^^^^^^^^^^^^^^^^^^
  File "/app/.heroku/python/lib/python3.12/site-packages/django/db/migrations/executor.py", line 252, in apply_migration
    state = migration.apply(state, schema_editor)
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.heroku/python/lib/python3.12/site-packages/django/db/migrations/migration.py", line 132, in apply
    operation.database_forwards(
  File "/app/.heroku/python/lib/python3.12/site-packages/django/db/migrations/operations/special.py", line 193, in database_forwards
    self.code(from_state.apps, schema_editor)
  File "/app/hypha/apply/projects/migrations/0097_move_project_comments_to_application.py", line 17, in migrate_project_comments_to_application
    application = Project.objects.get(id=comment.source_object_id).submission
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.heroku/python/lib/python3.12/site-packages/django/db/models/manager.py", line 87, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/app/.heroku/python/lib/python3.12/site-packages/django/db/models/query.py", line 637, in get
    raise self.model.DoesNotExist(
__fake__.Project.DoesNotExist: Project matching query does not exist.

@frjo
Copy link
Member

frjo commented Jan 31, 2025

Running it locally works it seems.

Comment on lines +53 to +71
proj_content_type = ContentType.objects.get_for_model(Project)
app_content_type = ContentType.objects.get_for_model(ApplicationSubmission)

# Determine if the provided object is an ApplicationSubmission or Project, then pull
# the related it's related Project/ApplicationSubmission activites if attribute it exists
if isinstance(obj, ApplicationSubmission):
source_filter = Q(source_object_id=obj.id, source_content_type=app_content_type)
if hasattr(obj, "project") and obj.project:
source_filter = source_filter | Q(
source_object_id=obj.project.id, source_content_type=proj_content_type
)
else:
source_filter = Q(
source_object_id=obj.id, source_content_type=proj_content_type
)
if hasattr(obj, "submission") and obj.submission:
source_filter = source_filter | Q(
source_object_id=obj.submission.id, source_content_type=app_content_type
)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this can further simplified like this,

logic to get the source filter.

    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)
Suggested change
proj_content_type = ContentType.objects.get_for_model(Project)
app_content_type = ContentType.objects.get_for_model(ApplicationSubmission)
# Determine if the provided object is an ApplicationSubmission or Project, then pull
# the related it's related Project/ApplicationSubmission activites if attribute it exists
if isinstance(obj, ApplicationSubmission):
source_filter = Q(source_object_id=obj.id, source_content_type=app_content_type)
if hasattr(obj, "project") and obj.project:
source_filter = source_filter | Q(
source_object_id=obj.project.id, source_content_type=proj_content_type
)
else:
source_filter = Q(
source_object_id=obj.id, source_content_type=proj_content_type
)
if hasattr(obj, "submission") and obj.submission:
source_filter = source_filter | Q(
source_object_id=obj.submission.id, source_content_type=app_content_type
)
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)

@theskumar
Copy link
Member

We'll also need to update the visibility_options template tag, to account for an activity that could be on either a submission object or project instance.

--- a/hypha/apply/activity/templatetags/activity_tags.py
+++ b/hypha/apply/activity/templatetags/activity_tags.py
@@ -84,7 +84,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)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Enhancement This is an improvement of an existing thing (not a new thing, which would be a feature). Type: Minor Minor change, used in release drafter
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Merge application and project communication tabs
3 participants