feat: admin panel enrichment

This commit is contained in:
mohamad
2026-07-19 14:31:28 +03:30
parent d8d7819eca
commit 1042fba121
77 changed files with 4616 additions and 2 deletions
+1
View File
@@ -0,0 +1 @@
@@ -0,0 +1 @@
@@ -0,0 +1,235 @@
from datetime import date
from django.contrib.auth import get_user_model
from django.core.management.base import BaseCommand
from django.db import transaction
from django.db.models.signals import post_save, pre_save
from apps.accounts.models import ClientProfile
from apps.projects.models import ProjectBrief, ProjectInternalNote
from apps.projects.signals import capture_previous_status, handle_brief_notifications
CLIENTS = [
{
"username": "client1",
"email": "client1@example.com",
"first_name": "Alex",
"last_name": "Morgan",
"password": "ClientDemo2026!",
"company": "Northside Imaging Lab",
"phone": "+1 604 555 0101",
"timezone": "America/Vancouver",
},
{
"username": "sarah.research",
"email": "sarah.research@example.com",
"first_name": "Sarah",
"last_name": "Chen",
"password": "ClientDemo2026!",
"company": "Pacific Oncology Research",
"phone": "+1 604 555 0102",
"timezone": "America/Los_Angeles",
},
{
"username": "devteam",
"email": "devteam@example.com",
"first_name": "Jordan",
"last_name": "Lee",
"password": "ClientDemo2026!",
"company": "BioFlow Analytics",
"phone": "+44 20 7946 0103",
"timezone": "Europe/London",
},
]
PROJECTS = [
{
"username": "client1",
"title": "Radiomics pipeline audit",
"category": ProjectBrief.CATEGORY_MEDICAL_IMAGING,
"description": (
"We need Tecvico to review our current radiomics extraction workflow "
"and align it with IBSI 2.0 standards across PET/CT datasets.\n\n"
"Goals: Standardize preprocessing, document gaps, and deliver a "
"remediation plan."
),
"budget_range": ProjectBrief.BUDGET_5K_15K,
"desired_deadline": date(2026, 9, 30),
"reference_links": "https://example.com/radiomics-spec\nhttps://example.com/sample-dataset",
"status": ProjectBrief.STATUS_UNDER_REVIEW,
"quote_text": "",
"internal_notes": ["Client has 120-case retrospective cohort ready."],
},
{
"username": "client1",
"title": "Clinical trial dashboard",
"category": ProjectBrief.CATEGORY_WEB,
"description": (
"Build a secure web dashboard for monitoring recruitment, imaging QC, "
"and milestone completion across two active trials.\n\n"
"Goals: Role-based views for coordinators and PI weekly status exports."
),
"budget_range": ProjectBrief.BUDGET_15K_50K,
"desired_deadline": date(2026, 11, 15),
"reference_links": "https://example.com/wireframes",
"status": ProjectBrief.STATUS_QUOTED,
"quote_text": (
"Phase 1 discovery and UX: $8,500\n"
"Phase 2 build and QA: $22,000\n"
"Estimated timeline: 10 weeks."
),
"internal_notes": ["Needs SSO discussion on kickoff call."],
},
{
"username": "sarah.research",
"title": "Workflow automation for cohort exports",
"category": ProjectBrief.CATEGORY_WORKFLOW,
"description": (
"Automate export of segmented lesions and feature tables from Tecvico "
"into our downstream R analysis environment.\n\n"
"Goals: One-click export with audit log and reproducible config files."
),
"budget_range": ProjectBrief.BUDGET_5K_15K,
"desired_deadline": date(2026, 8, 1),
"reference_links": "",
"status": ProjectBrief.STATUS_IN_PROGRESS,
"quote_text": "Fixed fee $11,500 — delivery in 6 weeks with two revision rounds.",
"internal_notes": ["Milestone 1 approved.", "Waiting on sample DICOM push."],
},
{
"username": "sarah.research",
"title": "Publication figure package",
"category": ProjectBrief.CATEGORY_RESEARCH,
"description": (
"Prepare publication-ready figures and methods appendix for a radiomics "
"paper.\n\n"
"Goals: Journal-compliant figures, caption draft, and reproducibility "
"checklist."
),
"budget_range": ProjectBrief.BUDGET_UNDER_5K,
"desired_deadline": date(2026, 7, 20),
"reference_links": "https://example.com/journal-guidelines",
"status": ProjectBrief.STATUS_DELIVERED,
"quote_text": "",
"internal_notes": ["Delivered v2 figures on June 12."],
},
{
"username": "devteam",
"title": "Multi-site data harmonization study",
"category": ProjectBrief.CATEGORY_DATA,
"description": (
"Assess batch effects across three hospital sites and propose "
"harmonization strategy before model training.\n\n"
"Goals: Site effect report, recommended normalization approach, and "
"pilot notebook."
),
"budget_range": ProjectBrief.BUDGET_50K_PLUS,
"desired_deadline": date(2027, 1, 31),
"reference_links": "https://example.com/data-dictionary",
"status": ProjectBrief.STATUS_SUBMITTED,
"quote_text": "",
"internal_notes": [],
},
]
class Command(BaseCommand):
help = "Seed demo client accounts and project briefs for the managed projects portal."
def add_arguments(self, parser):
parser.add_argument(
"--flush",
action="store_true",
help="Delete seeded demo users and their project briefs before re-seeding.",
)
def handle(self, *args, **options):
post_save.disconnect(handle_brief_notifications, sender=ProjectBrief)
pre_save.disconnect(capture_previous_status, sender=ProjectBrief)
try:
with transaction.atomic():
if options["flush"]:
self._flush()
users = self._seed_clients()
count = self._seed_projects(users)
finally:
post_save.connect(handle_brief_notifications, sender=ProjectBrief)
pre_save.connect(capture_previous_status, sender=ProjectBrief)
self.stdout.write(self.style.SUCCESS(f"Seeded {len(users)} clients and {count} project briefs."))
self.stdout.write("Demo login: client1 / ClientDemo2026!")
def _flush(self):
User = get_user_model()
usernames = [client["username"] for client in CLIENTS]
users = User.objects.filter(username__in=usernames)
ProjectBrief.objects.filter(client__in=users).delete()
ClientProfile.objects.filter(user__in=users).delete()
deleted, _ = users.delete()
self.stdout.write(f" Removed {deleted} demo user records")
def _seed_clients(self):
User = get_user_model()
users = {}
for client in CLIENTS:
user, created = User.objects.get_or_create(
username=client["username"],
defaults={
"email": client["email"],
"first_name": client["first_name"],
"last_name": client["last_name"],
},
)
user.email = client["email"]
user.first_name = client["first_name"]
user.last_name = client["last_name"]
user.set_password(client["password"])
user.save()
ClientProfile.objects.update_or_create(
user=user,
defaults={
"company": client["company"],
"phone": client["phone"],
"timezone": client["timezone"],
},
)
action = "Created" if created else "Updated"
self.stdout.write(f" {action} client: {user.username}")
users[user.username] = user
return users
def _seed_projects(self, users):
admin = get_user_model().objects.filter(is_superuser=True).first()
count = 0
for project in PROJECTS:
client = users[project["username"]]
brief, created = ProjectBrief.objects.update_or_create(
client=client,
title=project["title"],
defaults={
"category": project["category"],
"description": project["description"],
"budget_range": project["budget_range"],
"desired_deadline": project["desired_deadline"],
"reference_links": project["reference_links"],
"status": project["status"],
"quote_text": project["quote_text"],
},
)
if created:
count += 1
action = "Created"
else:
action = "Updated"
self.stdout.write(f" {action} project: {brief.title}")
if project["internal_notes"] and admin:
for note in project["internal_notes"]:
ProjectInternalNote.objects.get_or_create(
brief=brief,
note=note,
defaults={"author": admin},
)
return count