refactor(src): replace hardcoded role checks with is_admin attribute

Simplify permission logic by using the unified `is_admin` flag instead of duplicated role list checks.
This commit is contained in:
sankalp committed 2026-06-28 19:16:54 +05:45
1 parent 5db8cfc63e
commit 6a343efc0a
18 files changed
+269 -123

No files matched your search

+98 -34
View File
@@ -184,40 +184,49 @@ class Command(BaseCommand):
# ── Demo Users ───────────────────────────────────────────────────────
users = []
for i in range(1, 10):
dev_roles = [
"backend_developer",
"frontend_developer",
"ui_ux_designer",
"qa",
"tester",
"devops",
"data_analyst",
"product_owner",
"scrum_master",
]
for role_name in dev_roles:
user, _ = User.objects.get_or_create(
username=f"user{i}",
username=role_name,
defaults={
"email": f"user{i}@mail.com",
"first_name": f"Developer{i}",
"email": f"{role_name}@mail.com",
"first_name": role_name.replace("_", " ").title(),
"last_name": "User",
"role": "member",
"role": role_name,
},
)
user.set_password("user123")
if user.role != "member":
user.role = "member"
if user.role != role_name:
user.role = role_name
user.save()
users.append(user)
# Additional specific roles
roles = ["Manager", "Designer", "Tester", "DevOps"]
for r_name in roles:
u_role = "manager" if r_name == "Manager" else "member"
user, _ = User.objects.get_or_create(
username=r_name.lower(),
defaults={
"email": f"{r_name.lower()}@mail.com",
"first_name": r_name,
"last_name": "Staff",
"role": u_role,
},
)
user.set_password("user123")
if user.role != u_role:
user.role = u_role
user.save()
users.append(user)
# Manager user
manager_role = "manager"
user, _ = User.objects.get_or_create(
username="manager",
defaults={
"email": "manager@mail.com",
"first_name": "Manager",
"last_name": "Staff",
"role": manager_role,
},
)
user.set_password("user123")
if user.role != manager_role:
user.role = manager_role
user.save()
users.append(user)
all_users = [admin] + users
self.stdout.write(f" ✓ {len(all_users)} users ready")
@@ -256,6 +265,14 @@ class Command(BaseCommand):
"Shopping Cart",
"Payment Gateway",
],
"statuses": [
("Backlog", "#6B7280", False),
("Todo", "#3B82F6", False),
("In Progress", "#F59E0B", False),
("In Review", "#8B5CF6", False),
("Staging", "#06B6D4", False),
("Done", "#10B981", True),
],
},
{
"name": "Org Website",
@@ -268,6 +285,14 @@ class Command(BaseCommand):
"Services Section",
"Contact & Forms",
],
"statuses": [
("Draft", "#6B7280", False),
("Copy Review", "#3B82F6", False),
("Design Review", "#8B5CF6", False),
("Development", "#F59E0B", False),
("Client Review", "#EC4899", False),
("Published", "#10B981", True),
],
},
{
"name": "Health Homeservice Website",
@@ -280,6 +305,14 @@ class Command(BaseCommand):
"Booking System",
"Telehealth Video",
],
"statuses": [
("Registration", "#6B7280", False),
("Triage", "#3B82F6", False),
("Provider Assigned", "#F59E0B", False),
("In Service", "#8B5CF6", False),
("Follow-up", "#EC4899", False),
("Completed", "#10B981", True),
],
},
{
"name": "Consultancy Website",
@@ -292,6 +325,14 @@ class Command(BaseCommand):
"Scheduling Integration",
"Client Testimonials",
],
"statuses": [
("Lead", "#6B7280", False),
("Discovery", "#3B82F6", False),
("Proposal", "#F59E0B", False),
("Negotiation", "#EC4899", False),
("Active", "#8B5CF6", False),
("Closed", "#10B981", True),
],
},
{
"name": "School Website with Student Management",
@@ -304,6 +345,13 @@ class Command(BaseCommand):
"Grading & Attendance",
"Public Admissions Page",
],
"statuses": [
("Enrollment", "#6B7280", False),
("Records Pending", "#3B82F6", False),
("Active", "#10B981", False),
("Graduating", "#F59E0B", False),
("Alumni", "#8B5CF6", True),
],
},
{
"name": "Human Resource Management Website",
@@ -316,6 +364,14 @@ class Command(BaseCommand):
"Leave Management",
"Performance Reviews",
],
"statuses": [
("Application", "#6B7280", False),
("Screening", "#3B82F6", False),
("Interview", "#F59E0B", False),
("Onboarding", "#8B5CF6", False),
("Active", "#10B981", False),
("Offboarding", "#EF4444", True),
],
},
{
"name": "Staff Location Tracking Application",
@@ -328,6 +384,14 @@ class Command(BaseCommand):
"Mobile App Core",
"Reporting & Analytics",
],
"statuses": [
("Logged", "#6B7280", False),
("Dispatched", "#3B82F6", False),
("En Route", "#F59E0B", False),
("On Site", "#8B5CF6", False),
("Returning", "#06B6D4", False),
("Completed", "#10B981", True),
],
},
{
"name": "Ecommerce App",
@@ -340,6 +404,14 @@ class Command(BaseCommand):
"Push Notifications",
"Mobile Checkout",
],
"statuses": [
("Backlog", "#6B7280", False),
("Design", "#8B5CF6", False),
("Development", "#F59E0B", False),
("QA", "#EC4899", False),
("Beta Review", "#06B6D4", False),
("Published", "#10B981", True),
],
},
]
@@ -378,15 +450,7 @@ class Command(BaseCommand):
# ── Statuses ────────────────────────────────────────────────────
statuses = []
for i, (name, color, completed) in enumerate(
[
("Backlog", "#6B7280", False),
("Todo", "#3B82F6", False),
("In Progress", "#F59E0B", False),
("In Review", "#8B5CF6", False),
("Done", "#10B981", True),
]
):
for i, (name, color, completed) in enumerate(pd["statuses"]):
st, _ = TicketStatus.objects.get_or_create(
project=proj,
name=name,
@@ -49,3 +49,21 @@ class Command(BaseCommand):
admin.role = "superadmin"
admin.save()
self.stdout.write(" -> superuser 'admin' already exists (role verified)")
if not User.objects.filter(username="manager").exists():
mgr = User.objects.create_user(
username="manager",
email="manager@dewakoding.com",
password="admin123",
first_name="Project",
last_name="Manager",
)
mgr.role = "manager"
mgr.save()
self.stdout.write(" -> manager created: manager / admin123")
else:
mgr = User.objects.get(username="manager")
if mgr.role != "manager":
mgr.role = "manager"
mgr.save()
self.stdout.write(" -> manager 'manager' already exists (role verified)")
+34 -3
View File
@@ -1,15 +1,46 @@
from django.contrib.auth.models import AbstractUser
from django.db import models
ADMIN_ROLES = ("superadmin", "manager")
DEVELOPER_ROLES = (
"backend_developer",
"frontend_developer",
"ui_ux_designer",
"qa",
"tester",
"devops",
"data_analyst",
"product_owner",
"scrum_master",
)
class User(AbstractUser):
ROLE_CHOICES = (
("superadmin", "Superadmin"),
("manager", "Manager"),
("member", "Member"),
("guest", "Guest"),
("backend_developer", "Backend Developer"),
("frontend_developer", "Frontend Developer"),
("ui_ux_designer", "UI/UX Designer"),
("qa", "QA"),
("tester", "Tester"),
("devops", "DevOps"),
("data_analyst", "Data Analyst"),
("product_owner", "Product Owner"),
("scrum_master", "Scrum Master"),
)
role = models.CharField(max_length=20, choices=ROLE_CHOICES, default="member")
role = models.CharField(
max_length=30, choices=ROLE_CHOICES, default="backend_developer"
)
@property
def is_admin(self):
return self.role in ADMIN_ROLES
@property
def is_developer(self):
return not self.is_admin
google_id = models.CharField(max_length=255, blank=True, null=True, unique=True)
profile_image = models.ImageField(upload_to="profiles/", blank=True, null=True)
receive_deadline_emails = models.BooleanField(
+10 -3
View File
@@ -18,9 +18,6 @@ from src.tickets.models import (
User = get_user_model()
DEFAULT_STATUS_NAMES = ["Backlog", "Todo", "In Progress", "In Review", "Done"]
DEFAULT_STATUS_COLORS = ["#6B7280", "#3B82F6", "#F59E0B", "#8B5CF6", "#10B981"]
@require_http_methods(["POST"])
def board_create_ticket(request, project_id):
@@ -101,6 +98,16 @@ def move_ticket(request, project_id, ticket_id):
)
status = get_object_or_404(TicketStatus, pk=status_id, project_id=project_id)
if not request.user.is_admin and status.is_completed:
return JsonResponse(
{
"success": False,
"error": "Members cannot move tickets to completed statuses.",
},
status=403,
)
ticket.status = status
ticket.save(update_fields=["status", "updated_at"])
+9 -27
View File
@@ -16,33 +16,10 @@ from src.tickets.models import (
User = get_user_model()
DEFAULT_STATUS_NAMES = ["Backlog", "Todo", "In Progress", "In Review", "Done"]
DEFAULT_STATUS_COLORS = ["#6B7280", "#3B82F6", "#F59E0B", "#8B5CF6", "#10B981"]
def _ensure_default_statuses(project):
# Auto-migrate legacy 'Review' to 'In Review' if it exists
review_status = TicketStatus.objects.filter(project=project, name="Review").first()
if review_status:
in_review_status, created = TicketStatus.objects.get_or_create(
project=project,
name="In Review",
defaults={"color": "#8B5CF6", "sort_order": 3},
)
Ticket.objects.filter(status=review_status).update(status=in_review_status)
review_status.delete()
for i, name in enumerate(DEFAULT_STATUS_NAMES):
TicketStatus.objects.get_or_create(
project=project,
name=name,
defaults={"color": DEFAULT_STATUS_COLORS[i], "sort_order": i},
)
def project_board(request, project_id=None):
projects = Project.objects.all()
if request.user.role == "member":
if not request.user.is_admin:
projects = projects.filter(members=request.user)
projects = projects.annotate(
member_count=Count("members", distinct=True),
@@ -68,7 +45,6 @@ def project_board(request, project_id=None):
),
pk=project_id,
)
_ensure_default_statuses(selected_project)
project_members = selected_project.project_members.select_related("user").all()
statuses = TicketStatus.objects.filter(project=selected_project).order_by(
"sort_order"
@@ -120,10 +96,12 @@ def project_board(request, project_id=None):
}
)
can_move = request.user.role in ["superadmin", "manager"] or (
is_admin = request.user.is_admin
can_move = is_admin or (
selected_project
and selected_project.members.filter(id=request.user.id).exists()
)
can_move_to_completed = is_admin
priorities = TicketPriority.objects.all()
epics = Epic.objects.filter(project=selected_project) if selected_project else []
@@ -174,12 +152,14 @@ def project_board(request, project_id=None):
"selected_project_id": project_id,
"selected_project": selected_project,
"project_members": project_members,
"is_admin": is_admin,
"columns": columns,
"priorities": priorities,
"epics": epics,
"sort": sort,
"search": search,
"can_move": can_move,
"can_move_to_completed": can_move_to_completed,
"today": date.today(),
"project_users": project_users,
"project_users_json": json.dumps(project_users),
@@ -200,7 +180,6 @@ def project_board(request, project_id=None):
@require_http_methods(["GET"])
def board_data(request, project_id):
project = get_object_or_404(Project, pk=project_id)
_ensure_default_statuses(project)
statuses = TicketStatus.objects.filter(project=project).order_by("sort_order")
tickets = (
Ticket.objects.filter(project=project)
@@ -236,6 +215,8 @@ def board_data(request, project_id):
priorities = TicketPriority.objects.all()
epics = Epic.objects.filter(project=project)
user_can_move_to_completed = request.user.is_admin
return render(
request,
"projects/partials/board_columns.html",
@@ -246,5 +227,6 @@ def board_data(request, project_id):
"epics": epics,
"sort": sort,
"today": date.today(),
"user_can_move_to_completed": user_can_move_to_completed,
},
)
-3
View File
@@ -14,9 +14,6 @@ from src.tickets.models import (
User = get_user_model()
DEFAULT_STATUS_NAMES = ["Backlog", "Todo", "In Progress", "In Review", "Done"]
DEFAULT_STATUS_COLORS = ["#6B7280", "#3B82F6", "#F59E0B", "#8B5CF6", "#10B981"]
@require_http_methods(["POST"])
def export_board_tickets(request, project_id):
+1 -1
View File
@@ -30,7 +30,7 @@ def site_stats(request):
User = get_user_model()
total_users = User.objects.filter(is_active=True).count()
if request.user.is_authenticated and request.user.role == "member":
if request.user.is_authenticated and not request.user.is_admin:
total_tickets = Ticket.objects.filter(assignees=request.user).count()
else:
total_tickets = Ticket.objects.count()
+1 -1
View File
@@ -77,7 +77,7 @@ def projects_list(request):
)
projects = Project.objects.annotate()
if request.user.role == "member":
if not request.user.is_admin:
projects = projects.filter(members=request.user)
projects = projects.annotate(
member_count=Count("members", distinct=True),
+1 -1
View File
@@ -81,7 +81,7 @@ def tickets_list(request):
"status", "priority", "project", "created_by", "epic"
).prefetch_related("assignees")
if request.user.role == "member":
if not request.user.is_admin:
tickets = tickets.filter(assignees=request.user)
# Search across name, uuid, project name, assignee name, creator name, epic name
+2 -2
View File
@@ -141,7 +141,7 @@ def epic_delete(request, epic_id):
def epics_overview(request, project_id=None):
projects = Project.objects.all()
if request.user.role == "member":
if not request.user.is_admin:
projects = projects.filter(members=request.user)
projects = projects.annotate(
ticket_count=Count("tickets"),
@@ -154,7 +154,7 @@ def epics_overview(request, project_id=None):
if project_id:
selected_project = get_object_or_404(Project, pk=project_id)
if (
request.user.role == "member"
not request.user.is_admin
and not selected_project.members.filter(id=request.user.id).exists()
):
from django.contrib import messages
+3 -3
View File
@@ -283,7 +283,7 @@ def ticket_priorities(request):
@require_http_methods(["POST"])
def ticket_priority_add(request):
if request.user.role == "member":
if not request.user.is_admin:
messages.error(request, "Permission denied.")
return redirect("ticket-priorities")
name = request.POST.get("name", "").strip()
@@ -298,7 +298,7 @@ def ticket_priority_add(request):
@require_http_methods(["POST"])
def ticket_priority_edit(request, priority_id):
if request.user.role == "member":
if not request.user.is_admin:
messages.error(request, "Permission denied.")
return redirect("ticket-priorities")
priority = get_object_or_404(TicketPriority, pk=priority_id)
@@ -316,7 +316,7 @@ def ticket_priority_edit(request, priority_id):
@require_http_methods(["POST"])
def ticket_priority_delete(request, priority_id):
if request.user.role == "member":
if not request.user.is_admin:
messages.error(request, "Permission denied.")
return redirect("ticket-priorities")
priority = get_object_or_404(TicketPriority, pk=priority_id)
+4 -21
View File
@@ -16,7 +16,6 @@ from src.projects.models import Project, ProjectMember
from src.tickets.models import (
Ticket,
TicketPriority,
TicketStatus,
)
User = get_user_model()
@@ -48,7 +47,7 @@ COLOR_PALETTE = [
@require_http_methods(["GET", "POST"])
def project_create(request):
if request.user.role == "member":
if not request.user.is_admin:
from django.contrib import messages
from django.shortcuts import redirect
@@ -88,23 +87,6 @@ def project_create(request):
except User.DoesNotExist:
pass
if request.POST.get("use_default_statuses") == "1":
default_statuses = ["Backlog", "Todo", "In Progress", "In Review", "Done"]
for i, sname in enumerate(default_statuses):
TicketStatus.objects.create(
project=project,
name=sname,
sort_order=i,
is_completed=(sname == "Done"),
color={
"Backlog": "#6B7280",
"Todo": "#3B82F6",
"In Progress": "#F59E0B",
"In Review": "#8B5CF6",
"Done": "#10B981",
}.get(sname, "#6B7280"),
)
messages.success(request, f"Project '{project.name}' created successfully.")
if request.POST.get("create_another") == "1":
@@ -308,6 +290,7 @@ def project_detail(request, project_id):
"pinned_display": pinned_display,
"today": today,
"external_access": external_access,
"is_admin": request.user.is_admin,
},
)
@@ -498,7 +481,7 @@ def project_edit(request, project_id):
@require_http_methods(["POST"])
def project_delete(request, project_id):
if request.user.role == "member":
if not request.user.is_admin:
from django.contrib import messages
from django.shortcuts import redirect
@@ -529,7 +512,7 @@ def project_timeline(request):
projects_qs = Project.objects.filter(
start_date__isnull=False, end_date__isnull=False
)
if request.user.role == "member":
if not request.user.is_admin:
projects_qs = projects_qs.filter(members=request.user)
projects_qs = projects_qs.order_by("start_date")
+2 -2
View File
@@ -52,7 +52,7 @@ COLOR_PALETTE = [
def ticket_timeline(request, project_id=None):
projects = Project.objects.all()
if request.user.role == "member":
if not request.user.is_admin:
projects = projects.filter(members=request.user)
projects = projects.annotate(
ticket_count=Count("tickets"),
@@ -482,7 +482,7 @@ def tickets_export(request):
tickets = Ticket.objects.select_related(
"project", "status", "priority", "created_by", "epic"
)
if request.user.role == "member":
if not request.user.is_admin:
tickets = tickets.filter(assignees=request.user)
tickets = (
tickets.select_related("status", "priority", "project", "created_by", "epic")
+77 -13
View File
@@ -107,12 +107,31 @@ def users_list(request):
"has_created": has_created,
"role": role,
"unverified": unverified,
"roles": ["superadmin", "manager", "member", "guest"],
"roles": [
"superadmin",
"manager",
"backend_developer",
"frontend_developer",
"ui_ux_designer",
"qa",
"tester",
"devops",
"data_analyst",
"product_owner",
"scrum_master",
],
"roles_label": {
"superadmin": "Superadmin",
"manager": "Manager",
"member": "Member",
"guest": "Guest",
"backend_developer": "Backend Developer",
"frontend_developer": "Frontend Developer",
"ui_ux_designer": "UI/UX Designer",
"qa": "QA",
"tester": "Tester",
"devops": "DevOps",
"data_analyst": "Data Analyst",
"product_owner": "Product Owner",
"scrum_master": "Scrum Master",
},
"per_page": per_page,
"columns": columns,
@@ -157,7 +176,7 @@ def user_create(request):
user = form.save()
user.is_active = True
if request.user.role == "superadmin":
role = request.POST.get("role", "member")
role = request.POST.get("role", "backend_developer")
user.role = role
user.save()
messages.success(
@@ -173,12 +192,31 @@ def user_create(request):
"users/form.html",
{
"form": form,
"roles": ["superadmin", "manager", "member", "guest"],
"roles": [
"superadmin",
"manager",
"backend_developer",
"frontend_developer",
"ui_ux_designer",
"qa",
"tester",
"devops",
"data_analyst",
"product_owner",
"scrum_master",
],
"roles_label": {
"superadmin": "Superadmin",
"manager": "Manager",
"member": "Member",
"guest": "Guest",
"backend_developer": "Backend Developer",
"frontend_developer": "Frontend Developer",
"ui_ux_designer": "UI/UX Designer",
"qa": "QA",
"tester": "Tester",
"devops": "DevOps",
"data_analyst": "Data Analyst",
"product_owner": "Product Owner",
"scrum_master": "Scrum Master",
},
"can_change_role": request.user.role == "superadmin",
},
@@ -204,8 +242,15 @@ def user_detail(request, user_id):
"roles_label": {
"superadmin": "Superadmin",
"manager": "Manager",
"member": "Member",
"guest": "Guest",
"backend_developer": "Backend Developer",
"frontend_developer": "Frontend Developer",
"ui_ux_designer": "UI/UX Designer",
"qa": "QA",
"tester": "Tester",
"devops": "DevOps",
"data_analyst": "Data Analyst",
"product_owner": "Product Owner",
"scrum_master": "Scrum Master",
},
"projects_count": project_count,
"assigned_count": assigned_count,
@@ -254,7 +299,7 @@ def user_edit(request, user_id):
if form.is_valid():
user_obj = form.save()
if request.user.role == "superadmin":
role = request.POST.get("role", "member")
role = request.POST.get("role", "backend_developer")
user_obj.role = role
user_obj.save()
messages.success(
@@ -301,12 +346,31 @@ def user_edit(request, user_id):
"editing": True,
"user_obj": user_obj,
"current_role": current_role,
"roles": ["superadmin", "manager", "member", "guest"],
"roles": [
"superadmin",
"manager",
"backend_developer",
"frontend_developer",
"ui_ux_designer",
"qa",
"tester",
"devops",
"data_analyst",
"product_owner",
"scrum_master",
],
"roles_label": {
"superadmin": "Superadmin",
"manager": "Manager",
"member": "Member",
"guest": "Guest",
"backend_developer": "Backend Developer",
"frontend_developer": "Frontend Developer",
"ui_ux_designer": "UI/UX Designer",
"qa": "QA",
"tester": "Tester",
"devops": "DevOps",
"data_analyst": "Data Analyst",
"product_owner": "Product Owner",
"scrum_master": "Scrum Master",
},
"can_change_role": request.user.role == "superadmin",
"user_memberships": proj_page,
+2 -2
View File
@@ -13,7 +13,7 @@ User = get_user_model()
@require_http_methods(["GET"])
def recent_activity(request):
user = request.user
is_super = user.role in ["superadmin", "manager"]
is_super = user.is_admin
qs = TicketHistory.objects.select_related(
"ticket", "user", "status", "ticket__project"
@@ -37,7 +37,7 @@ def recent_activity(request):
@require_http_methods(["GET"])
def recent_activity_all(request):
user = request.user
is_super = user.role in ["superadmin", "manager"]
is_super = user.is_admin
qs = TicketHistory.objects.select_related(
"ticket", "user", "status", "ticket__project"
+3 -3
View File
@@ -17,7 +17,7 @@ User = get_user_model()
@require_http_methods(["GET"])
def tickets_per_project_chart(request):
user = request.user
is_super = user.role in ["superadmin", "manager"]
is_super = user.is_admin
qs = Project.objects.annotate(count=Count("tickets")).order_by("name")
if not is_super:
@@ -53,7 +53,7 @@ def tickets_per_project_chart(request):
@require_http_methods(["GET"])
def user_statistics_data(request):
user = request.user
is_super = user.role in ["superadmin", "manager"]
is_super = user.is_admin
users_qs = User.objects.annotate(
project_count=Count("projects", distinct=True),
@@ -80,7 +80,7 @@ def user_statistics_chart(request):
@require_http_methods(["GET"])
def monthly_trend_chart(request):
user = request.user
is_super = user.role in ["superadmin", "manager"]
is_super = user.is_admin
earliest = Ticket.objects.all()
if not is_super:
+2 -2
View File
@@ -21,7 +21,7 @@ def dashboard(request):
days=now.weekday()
)
is_super = user.role in ["superadmin", "manager"]
is_super = user.is_admin
if is_super:
total_projects = Project.objects.count()
@@ -204,7 +204,7 @@ def stats_cards(request):
days=now.weekday()
)
is_super = user.role in ["superadmin", "manager"]
is_super = user.is_admin
if is_super:
total_projects = Project.objects.count()
+2 -2
View File
@@ -13,10 +13,10 @@ User = get_user_model()
def project_timeline_widget(request):
filter_type = request.GET.get("filter", "pinned")
user = request.user
is_super = user.role in ["superadmin", "manager"]
is_super = user.is_admin
qs = Project.objects.filter(start_date__isnull=False, end_date__isnull=False)
if request.user.role == "member":
if not request.user.is_admin:
qs = qs.filter(members=request.user)
if not is_super:
qs = qs.filter(members=user)