Implement synonym management in condition detail: add forms for adding synonyms and handle POST requests for synonym creation.

This commit is contained in:
Ross
2025-11-17 23:08:34 +00:00
parent 81dc5bf48f
commit a12a9e38f3
3 changed files with 96 additions and 2 deletions
+31
View File
@@ -1033,6 +1033,37 @@ class ConditionAutocomplete(ModelAutocomplete):
model = Condition
search_attrs = ["name"]
@classmethod
def get_items_from_keys(cls, keys, context_obj=None):
"""Return items for given keys, ignoring empty strings.
The autocomplete frontend sometimes sends empty string keys which
causes the ORM to raise ValueError when filtering numeric PK fields.
Filter out falsy/blank keys before querying. If no valid keys remain
return an empty queryset.
"""
# normalize and remove empty/blank values
clean_keys = [k for k in keys if k is not None and str(k).strip() != ""]
if not clean_keys:
return cls.model.objects.none()
# try to convert numeric keys to ints where possible
parsed_keys = []
for k in clean_keys:
try:
parsed_keys.append(int(k))
except Exception:
parsed_keys.append(k)
qs = cls.model.objects.filter(pk__in=parsed_keys)
# Return a list of dicts matching the autocomplete core expectations
# (items subscriptable by keys like 'key' / 'label').
return [
{"key": obj.pk, "label": getattr(obj, "name", str(obj)), "value": str(obj)}
for obj in qs
]
class ConditionAutocompleteForm(Form):
condition = ModelChoiceField(
@@ -36,6 +36,28 @@
{% empty %}
—
{% endfor %}
{% if request.user.is_authenticated and can_merge %}
<div class="mt-2">
<a class="btn btn-sm btn-outline-primary" data-bs-toggle="collapse" href="#addSynonymCollapse" role="button" aria-expanded="false" aria-controls="addSynonymCollapse">Add synonym</a>
</div>
<div class="collapse mt-2" id="addSynonymCollapse">
<div class="card card-body">
<form method="POST">
{% csrf_token %}
<input type="hidden" name="action" value="add_synonym" />
<div class="mb-2">
<label class="form-label small">Select existing condition</label>
{{ synonym_form.condition }}
</div>
<div class="mb-2">
<label class="form-label small">Or create new synonym name</label>
<input class="form-control" type="text" name="synonym_name" placeholder="e.g. Granulomatosis with Polyangiitis" />
</div>
<button class="btn btn-primary btn-sm" type="submit">Add</button>
</form>
</div>
</div>
{% endif %}
</dd>
<dt class="col-sm-4">Parent</dt>
+43 -2
View File
@@ -830,7 +830,12 @@ def question_schema_schemas(request):
def condition_detail(request, pk):
condition = get_object_or_404(Condition, pk=pk)
form = ConditionAutocompleteForm()
# form used for the merge UI
merge_form = ConditionAutocompleteForm()
# form used to add a synonym (can post either an existing condition via autocomplete
# or provide a plain name in 'synonym_name')
synonym_form = ConditionAutocompleteForm()
can_merge = False
@@ -840,11 +845,47 @@ def condition_detail(request, pk):
):
can_merge = True
# Handle POST to add a synonym
if request.method == "POST" and request.POST.get("action") == "add_synonym":
# only allow staff/editors
if not (
request.user.is_superuser
or request.user.groups.filter(name="atlas_editor").exists()
):
return HttpResponse("Forbidden", status=403)
# Try autocomplete-selected condition first
form = ConditionAutocompleteForm(request.POST)
other = None
if form.is_valid():
other = form.cleaned_data.get("condition")
# If no autocomplete selection, accept a free-text name
synonym_name = request.POST.get("synonym_name")
if not other and synonym_name:
other, created = Condition.objects.get_or_create(name=synonym_name)
if created:
# mark created synonym as non-primary
other.primary = False
other.save()
if other and other != condition:
# add mutual synonym relationship
condition.synonym.add(other)
other.synonym.add(condition)
return redirect("atlas:condition_detail", pk=condition.pk)
# logging.debug(atlas.subspecialty.first().name.all())
return render(
request,
"atlas/condition_detail.html",
{"condition": condition, "form": form, "can_merge": can_merge},
{
"condition": condition,
"form": merge_form,
"synonym_form": synonym_form,
"can_merge": can_merge,
},
)