From c3b5ec78fccf3005e56c7ffd78f6682b9a1bece3 Mon Sep 17 00:00:00 2001 From: Gokuldevx Date: Mon, 25 Aug 2025 13:12:36 +0530 Subject: [PATCH] minor update --- .../core/__pycache__/models.cpython-313.pyc | Bin 7171 -> 7376 bytes .../core/__pycache__/views.cpython-313.pyc | Bin 12538 -> 13811 bytes ...emove_department_users_department_admin.py | 24 +++++++++ .../core/migrations/0007_department_users.py | 19 +++++++ ...ent_users_department_admin.cpython-313.pyc | Bin 0 -> 1235 bytes .../0007_department_users.cpython-313.pyc | Bin 0 -> 922 bytes civicfix/core/models.py | 18 +++++-- .../department/department_detail.html | 36 ++++++++++++- civicfix/core/views.py | 51 ++++++++++++------ 9 files changed, 127 insertions(+), 21 deletions(-) create mode 100644 civicfix/core/migrations/0006_remove_department_users_department_admin.py create mode 100644 civicfix/core/migrations/0007_department_users.py create mode 100644 civicfix/core/migrations/__pycache__/0006_remove_department_users_department_admin.cpython-313.pyc create mode 100644 civicfix/core/migrations/__pycache__/0007_department_users.cpython-313.pyc diff --git a/civicfix/core/__pycache__/models.cpython-313.pyc b/civicfix/core/__pycache__/models.cpython-313.pyc index df7f7df79d0a388e0c5202077c17ee92386e227a..10f13c8df0c1a9292e4286a8f8bae9faa19cb7fb 100644 GIT binary patch delta 2605 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 delta 3097 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 literal 0 HcmV?d00001 diff --git a/civicfix/core/migrations/__pycache__/0007_department_users.cpython-313.pyc b/civicfix/core/migrations/__pycache__/0007_department_users.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aae0a5d4e098158a8ee5deb67ca079fdc83a83b0 GIT binary patch literal 922 zcmZuvO>fgM7`F4(B}L+Xgij!92JNeEk;ijX>`dh*OaAyswuhHT$M#ohieE z!e*HBg!GxG2{%jnG~`K6n6R=u#wm-+no>?dCtTV`AOIBlfNDOZy06`X)CkN@^=d(> ztF~lQl}`P?G1IpKr!)6&%&)BLzP+wXh7417)e**7LWFynBFrMED`qZz%+rOrmxknxs8XDU9LI=+85SDP zWymeaDLqEvH+uM< z)&_@EO`#A1Y-+0PvKbC=B)QyV;4BrNA|*ry>NPS>GazCX=8rWSUG8|eCFR) ze{_RUH$9eLBl}%39Cxf+4eLXj-k##_c$OT^en|THT9lI>E$;1Hc;dZ+tb(Gl7j?U` vdQob^eq4^=30;yVl$$B&u?`_Tw*b8J6RiBPs?hr6j_S+D_2qLQjRN2=K8*9} literal 0 HcmV?d00001 diff --git a/civicfix/core/models.py b/civicfix/core/models.py index ecaa146..de722af 100644 --- a/civicfix/core/models.py +++ b/civicfix/core/models.py @@ -1,5 +1,6 @@ from django.contrib.auth.models import AbstractUser from django.core.validators import FileExtensionValidator +from django.conf import settings from django.db import models class User(AbstractUser): @@ -98,15 +99,24 @@ class Department(models.Model): description = models.TextField(blank=True, null=True) created_at = models.DateTimeField(auto_now_add=True) + # Each department can have many users users = models.ManyToManyField( - User, + settings.AUTH_USER_MODEL, related_name="departments", - blank=True, - limit_choices_to={'is_staff': True} + blank=True + ) + + # One admin per department + admin = models.OneToOneField( + settings.AUTH_USER_MODEL, + on_delete=models.SET_NULL, + related_name="admin_of_department", + null=True, + blank=True ) class Meta: ordering = ["name"] def __str__(self): - return self.name.name + return self.name \ No newline at end of file diff --git a/civicfix/core/templates/department/department_detail.html b/civicfix/core/templates/department/department_detail.html index be81bfd..a6f5211 100644 --- a/civicfix/core/templates/department/department_detail.html +++ b/civicfix/core/templates/department/department_detail.html @@ -8,12 +8,30 @@
+
Department Users
{% if users %}
    {% for user in users %} -
  • - {{ user.username }} — {{ user.email }} +
  • + + {{ user.username }} — {{ user.email }} + {% if department.admin and department.admin.id == user.id %} + Admin + {% endif %} + + + + {% if not department.admin or department.admin.id != user.id %} +
    + {% csrf_token %} + + + +
    + {% endif %}
  • {% endfor %}
@@ -21,10 +39,24 @@

No users registered in this department yet.

{% endif %} + + {% if department.admin %} +
+ {% csrf_token %} + + +
+ {% endif %} +
+ +
Register New User for {{ department.name }}
{% csrf_token %} +
diff --git a/civicfix/core/views.py b/civicfix/core/views.py index 86c0ad0..6a8ecac 100644 --- a/civicfix/core/views.py +++ b/civicfix/core/views.py @@ -3,6 +3,7 @@ from django.contrib.auth import authenticate, login from django.contrib.auth.decorators import login_required, user_passes_test from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.hashers import make_password +from django.db import IntegrityError from django.db.models import Exists, OuterRef from django.http import JsonResponse from django.shortcuts import render, redirect, get_object_or_404 @@ -196,15 +197,13 @@ def add_comment(request, pk, parent_id=None): return redirect("issue_detail", pk=pk) def superadmin_check(user): - return user.is_superuser - + return user.is_superuser @login_required @user_passes_test(superadmin_check) def superadmin_dashboard(request): return render(request, "dashboard/superadmin_dashboard.html") - @login_required @user_passes_test(superadmin_check) def manage_departments(request): @@ -228,19 +227,41 @@ def department_detail(request, pk): users = department.users.all() if request.method == "POST": - username = request.POST.get("username") - email = request.POST.get("email") - password = request.POST.get("password") + # ---- Create user ---- + if "create_user" in request.POST: + username = request.POST.get("username", "").strip() + email = request.POST.get("email", "").strip() + password = request.POST.get("password", "").strip() + if username and password: + try: + user = User.objects.create_user( + username=username, + email=email, + password=password + ) + user.is_staff = True + user.save() + department.users.add(user) + messages.success(request, f"User '{username}' created and added to department.") + except IntegrityError: + messages.error(request, "Username already exists.") - if username and password: - user = User.objects.create( - username=username, - email=email, - password=make_password(password), # hash the password - is_staff=True # mark as staff - ) - department.users.add(user) - return redirect("department_detail", pk=department.id) + # ---- Assign admin ---- + elif "assign_admin" in request.POST: + user_id = request.POST.get("admin_user_id") + if user_id: + user = get_object_or_404(User, id=user_id) + department.admin = user + department.save() + messages.success(request, f"{user.username} is now the admin of {department.name}.") + + # ---- Remove admin ---- + elif "remove_admin" in request.POST: + department.admin = None + department.save() + messages.info(request, "Department admin removed.") + + return redirect("department_detail", pk=department.id) return render(request, "department/department_detail.html", { "department": department,