from collections import defaultdict from dataclasses import dataclass from difflib import HtmlDiff, SequenceMatcher from django import forms from django.http import HttpResponse, JsonResponse from django.shortcuts import get_object_or_404, render from django.views.generic.edit import UpdateView, CreateView from django.views.generic.detail import DetailView from atlas.models import Subspecialty from .models import Entry, Formats # Create your views here. from .tables import EntryTable from .filters import EntryFilter from django_filters.views import FilterView from django.contrib.auth.decorators import user_passes_test from django_tables2 import SingleTableView, SingleTableMixin class EntryUpdateView(UpdateView): model = Entry template_name = "oef/entry_update_view.html" fields = ["modality", "exam_code", "exam_name", "questions", "comments", "inactive"] class EntryDetailView(DetailView): model = Entry template_name = "oef/entry_detail_view.html" class FormatsCreateView(CreateView): model = Formats template_name_suffix = "_update_form" fields = ["name", "format", "inherits"] class FormatsForm(forms.ModelForm): class Meta: model = Formats fields = ["name", "format", "specialties", "inherits"] widgets = {"specialties": forms.CheckboxSelectMultiple()} class FormatsUpdateView(UpdateView): model = Formats template_name_suffix = "_update_form" # fields = ['name', 'format', 'specialties', 'inherits'] form_class = FormatsForm class EntryTableView(SingleTableMixin, FilterView): model = Entry table_class = EntryTable template_name = "oef/entry_table.html" filterset_class = EntryFilter @dataclass class FormatOutput: name: str action: str = "" field_seq: int = 100 field_label: str = "" field_name: str = "" field_status: str = "" field_type: str = "" field_codeset: str = "" def output(request): formats = Formats.objects.all() formats = sorted(formats, key=lambda x: len(x.format)) outputs = [] codesets = [] codeset_start = 100000 for format in formats: seq = 100 questions = format.format.strip().split("---") for question in questions: if not question: continue name, *other = question.strip().splitlines() status = "OPTIONAL" if name.endswith("*"): status = "REQUIRED" name = name[:-1] field_type = "" match other: case ["Yes", "No"]: field_type = "YES/NO" case ["Date/Time"]: field_type = "DATE/TIME" case ["Freetext"]: field_type = "ALPHANUMERIC" case [*codeset]: field_type = "CODESET" case _: field_type = "unknown" o = FormatOutput( format.name, field_label=name, field_name=name, field_seq=seq, field_status=status, field_type=field_type, ) if field_type == "CODESET": o.field_codeset = codeset_start codesets.append((codeset_start, other)) codeset_start = codeset_start + 100 outputs.append(o) seq = seq + 100 return render( request, "oef/output.html", {"formats": formats, "outputs": outputs, "codesets": codesets}, ) def formats_view(request): formats = Formats.objects.all() return render(request, "oef/formats_view.html", {"formats": formats}) def formats_dupe_view(request): formats = Formats.objects.all() format_dict = defaultdict(list) dupes = [] for format in formats: if format.format in format_dict: dupes.append((format, format_dict[format.format])) format_dict[format.format].append(format) return render(request, "oef/formats_dupe_view.html", {"dupes": dupes, "format_dict": format_dict}) @user_passes_test(lambda u: u.is_superuser) def formats_delete(request, pk): f = get_object_or_404(Formats, pk=pk) f.delete() return HttpResponse("Format deleted") def formats_apply(request, pk): format = get_object_or_404(Formats, pk=pk) search_text = format.get_format() entries = Entry.objects.filter(questions__icontains=search_text, inactive=False) entry_list = [] for entry in entries: entry_list.append((entry, entry.questions.replace(search_text, ""))) return render( request, "oef/formats_apply.html", {"format": format, "entry_list": entry_list, "entries": entries}, ) def entry_detail_formats(request, pk): entry = get_object_or_404(Entry, pk=pk) formats = Formats.objects.all() questions = entry.questions matching_formats = [] for format in formats: if format.get_format() in questions: matching_formats.append(format) matching_formats = sorted(matching_formats, key=lambda x: len(x.get_format())) return render( request, "oef/entry_detail_formats.html", {"entry": entry, "matching_formats": matching_formats}, ) def replace_questions(request): if request.method == "POST": search = request.POST.get("search") replace = request.POST.get("replace") if not search or not replace: return HttpResponse("Please provide both search and replace values") if request.POST.get("exact", False): entries = Entry.objects.filter(questions=search, inactive=False) formats = Formats.objects.filter(format=search) else: entries = Entry.objects.filter(questions__contains=search, inactive=False) formats = Formats.objects.filter(format__contains=search) for entry in entries: entry.questions = entry.questions.replace(search, replace) entry.save() for format in formats: format.format = format.format.replace(search, replace) format.save() return HttpResponse(f"{entries.count()} questions replaced") else: return render(request, "oef/replace_questions.html", {}) def entries_table(request): entries = Entry.objects.filter(inactive=False).order_by("exam_code") return render(request, "oef/entries_table.html", {"entries": entries}) def entries(request): entries = Entry.objects.all().order_by("exam_code") return render(request, "oef/entries.html", {"entries": entries}) def format_search(request): if request.method == "POST": search = request.POST.get("search") entries = Entry.objects.filter(questions__icontains=search, inactive=False) return render(request, "oef/format_search_results.html", {"entries": entries}) return render( request, "oef/format_search.html", ) def subspecialties(request): subspecialties = Subspecialty.objects.all() format_map = [] for s in subspecialties: format_map.append((s, s.formats_set.all())) format_map.append( ("Nuclear Medicine", Formats.objects.filter(name__startswith="NM")) ) return render( request, "oef/subspecialties.html", context={"format_map": format_map} ) def questions(request): questions = set( Entry.objects.filter(inactive=False).values_list("questions", flat=True) ) questions = sorted(questions, key=lambda x: len(x)) questions_and_formats = [] for question in questions: try: format = Formats.objects.get(format=question) except Formats.DoesNotExist: format = None except Formats.MultipleObjectsReturned: format = Formats.objects.filter(format=question) return HttpResponse(f"Multiple (duplicate) formats found [{",".join([f.name for f in format])}]") questions_and_formats.append((question, format)) return render( request, "oef/questions.html", {"questions": questions, "questions_and_formats": questions_and_formats}, ) def entries_by_question(request): if request.method == "POST": question = request.POST.get("question") entries = Entry.objects.filter(questions=question, inactive=False) return render(request, "oef/entries_by_question.html", {"entries": entries}) return HttpResponse("Please provide a question") def entries_by_question_codes(request): if request.method == "POST": question = request.POST.get("question") entries = Entry.objects.filter(questions=question, inactive=False) return render( request, "oef/entries_by_question_codes.html", {"entries": entries} ) return HttpResponse("Please provide a question") def questions_diff(request): if request.method == "POST": question1 = request.POST.get("question1", False) question2 = request.POST.get("question2", False) if not question1: question1 = request.POST.get("search") question2 = request.POST.get("replace") html = HtmlDiff().make_table(question1.split("\n"), question2.split("\n")) return HttpResponse(html) return HttpResponse("Please provide two questions") def questions_sym(request): if request.method == "POST": question = request.POST.get("question") questions = set( Entry.objects.filter(inactive=False).values_list("questions", flat=True) ) output = [] for q in questions: ratio = SequenceMatcher(None, question, q).ratio() if ratio > 0.5: output.append((q, ratio)) output = sorted(output, key=lambda x: x[1], reverse=True) return render(request, "oef/questions_sym.html", {"output": output}) return HttpResponse("Please provide a question set") def formats_save(request): if request.method == "POST": question = request.POST.get("question") format_name = request.POST.get("format_name") if not question: return HttpResponse("Please provide a question") if Formats.objects.filter(name=format_name).exists(): return HttpResponse("Format name already exists") if Formats.objects.filter(format=question).exists(): return HttpResponse("Format questions already exists") Formats.objects.create(name=format_name, format=question) return HttpResponse("Format saved") return HttpResponse("Please provide a question")