Add canonical field and update synonym handling in Condition model
- Introduced a new `canonical` ForeignKey field in the Condition model to manage alias relationships. - Enhanced synonym retrieval methods to prefer canonical names and handle aliases more effectively. - Updated condition detail templates to reflect canonical status and improved synonym display. - Implemented migration scripts to establish canonical relationships based on existing synonym links.
This commit is contained in:
+74
-11
@@ -116,30 +116,66 @@ class SynMixin(object):
|
||||
return f"{self.name} [syn]"
|
||||
|
||||
def get_old_str(self) -> str:
|
||||
# Prefer using a model-specific get_synonyms (e.g. Condition.get_synonyms)
|
||||
try:
|
||||
syns = self.get_synonyms()
|
||||
except Exception:
|
||||
syns = None
|
||||
|
||||
if self.primary:
|
||||
if self.synonym.count() == 0:
|
||||
return self.name
|
||||
else:
|
||||
if syns is None:
|
||||
if getattr(self, "synonym", None) is None or self.synonym.count() == 0:
|
||||
return self.name
|
||||
synonyms = ",".join([i.name for i in self.synonym.all()])
|
||||
return f"{self.name} ({synonyms})"
|
||||
else:
|
||||
if not syns:
|
||||
return self.name
|
||||
synonyms = ",".join([i.name for i in syns])
|
||||
return f"{self.name} ({synonyms})"
|
||||
else:
|
||||
return f"{self.name} [syn]"
|
||||
|
||||
def get_synonym(self):
|
||||
if self.primary:
|
||||
# Return a readable synonym string. For models that implement
|
||||
# get_synonyms (Condition with canonical/aliases), use that.
|
||||
try:
|
||||
syns = self.get_synonyms()
|
||||
except Exception:
|
||||
syns = None
|
||||
|
||||
if getattr(self, "primary", False):
|
||||
return "[Primary]"
|
||||
else:
|
||||
s = self.synonym.filter(primary=True).values_list("name", flat=True)
|
||||
return ", ".join(s)
|
||||
if syns is None:
|
||||
s = self.synonym.filter(primary=True).values_list("name", flat=True)
|
||||
return ", ".join(s)
|
||||
else:
|
||||
# prefer primary names among synonyms if present
|
||||
primary_names = [i.name for i in syns if getattr(i, "primary", False)]
|
||||
if primary_names:
|
||||
return ", ".join(primary_names)
|
||||
return ", ".join([i.name for i in syns])
|
||||
|
||||
def get_synonym_link(self):
|
||||
if self.primary:
|
||||
try:
|
||||
syns = self.get_synonyms()
|
||||
except Exception:
|
||||
syns = None
|
||||
|
||||
if getattr(self, "primary", False):
|
||||
return "[Primary]"
|
||||
else:
|
||||
syns = self.synonym.filter(primary=True)
|
||||
return ", ".join(
|
||||
[f"<a href='{s.get_absolute_url()}'>{s.name}</a>" for s in syns]
|
||||
)
|
||||
if syns is None:
|
||||
# fall back to all M2M synonyms (not just primary) for models
|
||||
# that still use the old synonym field (e.g. Finding, Structure)
|
||||
syns_qs = self.synonym.all()
|
||||
items = [f"<a href='{s.get_absolute_url()}'>{s.name}</a>" for s in syns_qs]
|
||||
return ", ".join(items)
|
||||
else:
|
||||
# render links for synonyms/canonical group
|
||||
items = [f"<a href='{s.get_absolute_url()}'>{s.name}</a>" for s in syns]
|
||||
return ", ".join(items)
|
||||
|
||||
def get_link(self):
|
||||
return format_html("<a href='{}'>{}</a>", self.get_absolute_url(), self.name)
|
||||
@@ -163,6 +199,16 @@ class Condition(SynMixin, models.Model):
|
||||
blank=True,
|
||||
help_text="Use if a direct synonym for the condition exists, e.g. 'Wegener granulomatosis' and 'Granulomatosis with Polyangitis'.",
|
||||
)
|
||||
# New canonical/alias field (Option B): if set, this Condition is an alias
|
||||
# and points to the canonical/master Condition.
|
||||
canonical = models.ForeignKey(
|
||||
"self",
|
||||
null=True,
|
||||
blank=True,
|
||||
on_delete=models.SET_NULL,
|
||||
related_name="aliases",
|
||||
help_text="If set, this Condition is an alias and points to the canonical Condition.",
|
||||
)
|
||||
parent = models.ManyToManyField(
|
||||
"self",
|
||||
blank=True,
|
||||
@@ -198,6 +244,23 @@ class Condition(SynMixin, models.Model):
|
||||
def get_absolute_url(self):
|
||||
return reverse("atlas:condition_detail", kwargs={"pk": self.pk})
|
||||
|
||||
@property
|
||||
def canonical_condition(self):
|
||||
"""Return the canonical/master condition for this Condition (self if none)."""
|
||||
return self.canonical if self.canonical else self
|
||||
|
||||
def get_synonyms(self):
|
||||
"""Return other Conditions that are synonyms/aliases for this concept.
|
||||
|
||||
Behaviour:
|
||||
- If this Condition is an alias (canonical set), return the canonical and
|
||||
any other aliases (excluding self).
|
||||
- If this Condition is canonical, return all aliases (and exclude self).
|
||||
"""
|
||||
master = self.canonical_condition
|
||||
qs = Condition.objects.filter(models.Q(canonical=master) | models.Q(pk=master.pk)).exclude(pk=self.pk)
|
||||
return qs
|
||||
|
||||
def get_children(self):
|
||||
return self.child.all()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user