Add downsample series functionality and related UI updates
- Implemented asynchronous downsample task for series with progress tracking. - Added new URL endpoint for downsample status. - Updated Series model to include source_series_instance_uid. - Enhanced case display template with new buttons for series actions. - Added password reset functionality in user profile with appropriate alerts. - Created migration for new source_series_instance_uid field in Series model.
This commit is contained in:
+210
-210
File diff suppressed because one or more lines are too long
@@ -1,5 +1,6 @@
|
||||
|
||||
from django.urls import reverse
|
||||
from django.core import mail
|
||||
|
||||
from rich.pretty import pprint
|
||||
|
||||
@@ -89,3 +90,53 @@ def test_profile_access_and_management(db, client, create_users, create_cid_mana
|
||||
assert BeautifulSoup(update_profile_response.content, "html.parser").find(id="id_registration_number")["value"]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_create_user_reuses_existing_account(client, create_cid_manager, django_user_model):
|
||||
existing_user = django_user_model.objects.create_user(
|
||||
username="existing@example.com",
|
||||
email="existing@example.com",
|
||||
password="password123",
|
||||
first_name="Old",
|
||||
last_name="Name",
|
||||
)
|
||||
|
||||
client.force_login(create_cid_manager)
|
||||
|
||||
response = client.post(
|
||||
reverse("create_user"),
|
||||
{
|
||||
"username": "existing@example.com",
|
||||
"first_name": "Updated",
|
||||
"last_name": "User",
|
||||
"grade": "",
|
||||
},
|
||||
)
|
||||
|
||||
assert response.status_code == 302
|
||||
assert response.url == reverse("accounts_list")
|
||||
|
||||
existing_user.refresh_from_db()
|
||||
assert existing_user.first_name == "Updated"
|
||||
assert existing_user.last_name == "User"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_cid_manager_can_send_password_reset_from_account_profile(client, create_cid_manager, django_user_model):
|
||||
managed_user = django_user_model.objects.create_user(
|
||||
username="managed-user",
|
||||
email="managed@example.com",
|
||||
password="password123",
|
||||
)
|
||||
|
||||
client.force_login(create_cid_manager)
|
||||
response = client.post(
|
||||
reverse("account_send_password_reset", kwargs={"slug": managed_user.username}),
|
||||
{"next": reverse("account_profile", kwargs={"slug": managed_user.username})},
|
||||
)
|
||||
|
||||
assert response.status_code == 302
|
||||
assert "password_reset_action=sent" in response.url
|
||||
assert len(mail.outbox) == 1
|
||||
assert mail.outbox[0].to == ["managed@example.com"]
|
||||
|
||||
|
||||
|
||||
@@ -107,6 +107,11 @@ urlpatterns = [
|
||||
views.account_set_supervisor,
|
||||
name="account_set_supervisor",
|
||||
),
|
||||
path(
|
||||
"accounts/profile/<str:slug>/send-password-reset/",
|
||||
views.account_send_password_reset,
|
||||
name="account_send_password_reset",
|
||||
),
|
||||
#path("", TemplateView.as_view(template_name="index.html"), name="home"),
|
||||
path("", views.index, name="home"),
|
||||
path("cid/results/<int:cid>/", views.cid_results, name="cid_results"),
|
||||
|
||||
@@ -30,6 +30,7 @@ from django.utils.safestring import mark_safe
|
||||
|
||||
# from django.contrib.auth.models import User
|
||||
from django.contrib.auth.decorators import login_required, user_passes_test
|
||||
from django.contrib.auth.forms import PasswordResetForm
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
from django.contrib.contenttypes.models import ContentType
|
||||
@@ -167,6 +168,7 @@ def profile(request):
|
||||
"is_cid_user_manager": is_cid_user_manager,
|
||||
"supervisor_account": supervisor_account,
|
||||
"supervisor_action": request.GET.get("supervisor_action", ""),
|
||||
"password_reset_action": request.GET.get("password_reset_action", ""),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -216,6 +218,7 @@ def account_profile(request, slug):
|
||||
"is_cid_user_manager": is_cid_user_manager,
|
||||
"supervisor_account": supervisor_account,
|
||||
"supervisor_action": request.GET.get("supervisor_action", ""),
|
||||
"password_reset_action": request.GET.get("password_reset_action", ""),
|
||||
},
|
||||
)
|
||||
|
||||
@@ -257,6 +260,38 @@ def account_set_supervisor(request, slug):
|
||||
return redirect(f"{next_url}?supervisor_action=created")
|
||||
|
||||
|
||||
@require_POST
|
||||
@user_is_cid_user_manager
|
||||
def account_send_password_reset(request, slug):
|
||||
"""Send a password reset email for an existing user from their profile page."""
|
||||
user = get_object_or_404(User, username=slug)
|
||||
|
||||
next_url = request.POST.get("next") or reverse("account_profile", kwargs={"slug": user.username})
|
||||
email = (user.email or user.username or "").strip()
|
||||
|
||||
if not email:
|
||||
return redirect(f"{next_url}?password_reset_action=no_email")
|
||||
|
||||
if not user.is_active:
|
||||
return redirect(f"{next_url}?password_reset_action=inactive")
|
||||
|
||||
form = PasswordResetForm({"email": email})
|
||||
if not form.is_valid():
|
||||
return redirect(f"{next_url}?password_reset_action=invalid")
|
||||
|
||||
try:
|
||||
form.save(
|
||||
request=request,
|
||||
use_https=request.is_secure(),
|
||||
from_email=getattr(settings, "DEFAULT_FROM_EMAIL", None),
|
||||
)
|
||||
except Exception:
|
||||
logger.exception("Failed to send password reset email for user {}", user.pk)
|
||||
return redirect(f"{next_url}?password_reset_action=error")
|
||||
|
||||
return redirect(f"{next_url}?password_reset_action=sent")
|
||||
|
||||
|
||||
@user_passes_test(lambda u: u.is_superuser)
|
||||
def logs_view(request):
|
||||
"""Display production logs from log.txt with search filtering.
|
||||
|
||||
Reference in New Issue
Block a user