from django.contrib.auth.models import User from django.urls import reverse from django.utils.safestring import mark_safe from django.conf import settings import json # Default filters for exam search widget. Can be overridden in Django settings # as `GENERIC_EXAM_SEARCH_DEFAULT_FILTERS` mapping, e.g.: # GENERIC_EXAM_SEARCH_DEFAULT_FILTERS = { 'archive': False, 'exam_mode': True } DEFAULT_EXAM_SEARCH_FILTERS = getattr( settings, "GENERIC_EXAM_SEARCH_DEFAULT_FILTERS", {"archive": False, "exam_mode": True} ) class UserSearchSelectMultipleWidget: """Reusable lightweight widget renderer for a searchable multi-user selector. Usage: instantiate with (name, value) where value is an iterable of user ids and call .render() to get the HTML fragment. """ def __init__(self, name, value): self.name = name self.value = value or [] def render(self): field_id = f"id_{self.name}" search_id = f"user_search_{self.name}" results_id = f"user_search_results_{self.name}" selected_list_id = f"selected_users_{self.name}" users = User.objects.filter(pk__in=self.value) if self.value else [] options_html = "" selected_html = "" for u in users: # determine grade_text first, then include data-user-grade attribute on option grade_text = "" try: up = getattr(u, "userprofile", None) if up and getattr(up, "grade", None): g = up.grade grade_text = getattr(g, "name", str(g)) if g is not None else "" except Exception: grade_text = "" safe_grade_attr = (grade_text.replace('"', '"') if grade_text else '') options_html += f'' # Build the selected item HTML incrementally to avoid accidental # mixing of boolean values into string concatenation (was causing # a TypeError in some environments). selected_html += ( f'