Repository: CiviWiki/OpenCiviWiki

Issue:
Add "Civis" tab to the user profile view
We recently removed some features from the user Profile view and now want to reimplement them using only Django.

This task will be to add a "Civis" tab to the user profile view. The tab should link to a sub-path of the user profile so it is clear to the viewer that they are viewing Civis related to the user.

## Task
All of these tasks should be done in the `accounts` app.

- [ ] create a Django view called `UserCivis` (in `accounts/views.py`)
- [ ] create a URL definition `profile/<str:username>/civis` that renders the `UserCivis` view ( in `accounts/urls.py`)
- [ ] ensure the `UserCivis` view context contains a list of Civis created by the relevant user (given via the `username` in the URL)
- [ ] create a template to render the user civis - for now, extending `base.html` so we have consistent branding  (in `accounts/templates/accounts/user_civis.html`)



Discussion:
Hi, I would be interested in taking this on. Do you think this could be done in two weeks or less?
I believe it could be managed in a weekend, with focused effort.
Appreciate this was assigned yesterday, but if @smhavens doesn't get it done in their needed timeframe, would be keen to have a go.
Let's check back in a couple of days.
Hi everyone, i would like to help with this issue if is necessary

Unfortunately I won't be able to continue working on this. Feel free to assign to anyone else.
Hi, I'm willing to take this on with @riajots @Rbatchu2 and @FredKhayat, and we can finish it within the next few days
To clarify, what's the difference between accounts/templates/accounts/user_civis.html and accounts/templates/accounts/partials/user_civis.html? Are we supposed to create a new accounts/templates/accounts/user_civis.html or stick with the one in partials?
The `partials/user_civis.html` contains leftover code from our Backbone.js migration. For now, use the `templates/accounts/user_civis.html` to render the User Civis.

Resolving patch:
diff --git a/project/accounts/urls.py b/project/accounts/urls.py
--- a/project/accounts/urls.py
+++ b/project/accounts/urls.py
@@ -10,6 +10,7 @@
     SettingsView,
     ProfileFollowing,
     UserProfileView,
+    UserCivis,
     expunge_user,
 )
 from django.contrib.auth import views as auth_views
@@ -43,6 +44,11 @@
         ProfileFollowing.as_view(),
         name="profile-following",
     ),
+    path(
+        "profile/<str:username>/civis/",
+        UserCivis.as_view(),
+        name="user-civis",
+    ),
     path(
         "accounts/password_reset/",
         PasswordResetView.as_view(),
diff --git a/project/accounts/views.py b/project/accounts/views.py
--- a/project/accounts/views.py
+++ b/project/accounts/views.py
@@ -206,6 +206,24 @@ def get(self, request, username=None):
         )
 
 
+class UserCivis(LoginRequiredMixin, View):
+    """
+    A view that shows list of civis
+    that profile with given username created
+    """
+
+    def get(self, request, username=None):
+        profile = get_object_or_404(Profile, user__username=username)
+        user = profile.user
+        civis = user.civis.all()
+
+        return TemplateResponse(
+            request,
+            "user_civis.html",
+            {"profile": profile, "civis": civis},
+        )
+
+
 @login_required
 def expunge_user(request):
     """


Test patch:
diff --git a/project/accounts/tests/test_views.py b/project/accounts/tests/test_views.py
--- a/project/accounts/tests/test_views.py
+++ b/project/accounts/tests/test_views.py
@@ -4,6 +4,7 @@
 from django.contrib.auth import views as auth_views
 from django.test import TestCase
 from django.urls import resolve, reverse
+from threads.models import Civi, Thread
 
 
 class BaseTestCase(TestCase):
@@ -215,3 +216,39 @@ def test_get_user_profile(self):
         self.assertEqual(response.status_code, 200)
         self.assertContains(response, self.user.username)
         self.assertTemplateUsed(response, "account.html")
+
+
+class UserProfileCivis(BaseTestCase):
+    """A class to test user profiles following view"""
+
+    def setUp(self) -> None:
+        super(UserProfileCivis, self).setUp()
+
+        self.user2 = get_user_model().objects.create_user(
+            username="newuser2", email="test2@test2.com", password="password123"
+        )
+
+        thread = Thread(
+            author=self.user2, title="test thread", summary="this is a test thread"
+        )
+        thread.save()
+        civi = Civi(author=self.user, thread=thread, title="test civi title")
+        civi.save()
+
+    def test_get_my_civis(self):
+        """Whether user_profile function works as expected"""
+
+        self.client.login(username="newuser", password="password123")
+        response = self.client.get(reverse("user-civis", args=["newuser"]))
+        self.assertEqual(response.status_code, 200)
+        self.assertContains(response, "test civi title")
+        self.assertTemplateUsed(response, "user_civis.html")
+
+    def test_get_other_civis(self):
+        """Whether user_profile function works as expected"""
+
+        self.client.login(username="newuser2", password="password123")
+        response = self.client.get(reverse("user-civis", args=["newuser"]))
+        self.assertEqual(response.status_code, 200)
+        self.assertContains(response, "test civi title")
+        self.assertTemplateUsed(response, "user_civis.html")