send group info from server
This commit is contained in:
+161
-18
@@ -105,6 +105,157 @@ def findMiddle(input_list):
|
||||
return (input_list[int(middle)], input_list[int(middle - 1)])
|
||||
|
||||
|
||||
def _normalise_group_rule_value(value):
|
||||
if value is None:
|
||||
return ""
|
||||
|
||||
if isinstance(value, pydicom.multival.MultiValue):
|
||||
value = list(value)
|
||||
|
||||
if isinstance(value, (list, tuple)):
|
||||
return ",".join([str(v).strip() for v in value if str(v).strip()])
|
||||
|
||||
text = str(value).strip()
|
||||
if text == "":
|
||||
return ""
|
||||
|
||||
try:
|
||||
numeric = float(text)
|
||||
if abs(numeric - round(numeric)) < 1e-6:
|
||||
return str(int(round(numeric)))
|
||||
return f"{numeric:.1f}"
|
||||
except (TypeError, ValueError):
|
||||
return text
|
||||
|
||||
|
||||
def _build_declared_series_groups(series, series_images_with_urls):
|
||||
rules = [
|
||||
"dwiBValue",
|
||||
"dwiDirection",
|
||||
"temporalPosition",
|
||||
"triggerTime",
|
||||
"contentTime",
|
||||
"echoTime",
|
||||
"echoNumber",
|
||||
]
|
||||
|
||||
rows = []
|
||||
|
||||
for image_obj, image_url in series_images_with_urls:
|
||||
ds = image_obj.get_dicom_data()
|
||||
|
||||
def get_value(*keys):
|
||||
for key in keys:
|
||||
if key in ds:
|
||||
normalized = _normalise_group_rule_value(ds[key].value)
|
||||
if normalized != "":
|
||||
return normalized
|
||||
return ""
|
||||
|
||||
rule_values = {
|
||||
"dwiBValue": get_value("DiffusionBValue", "DiffusionbValue"),
|
||||
"dwiDirection": get_value("DiffusionGradientDirection", "DiffusionGradientOrientation"),
|
||||
"temporalPosition": get_value("TemporalPositionIdentifier"),
|
||||
"triggerTime": get_value("TriggerTime"),
|
||||
"contentTime": get_value("ContentTime", "AcquisitionTime"),
|
||||
"echoTime": get_value("EchoTime"),
|
||||
"echoNumber": get_value("EchoNumbers", "EchoNumber"),
|
||||
}
|
||||
|
||||
b_value = None
|
||||
try:
|
||||
if rule_values["dwiBValue"] != "":
|
||||
b_value = float(rule_values["dwiBValue"])
|
||||
except (TypeError, ValueError):
|
||||
b_value = None
|
||||
|
||||
rows.append(
|
||||
{
|
||||
"imageId": image_url,
|
||||
"ruleValues": rule_values,
|
||||
"bValue": b_value,
|
||||
}
|
||||
)
|
||||
|
||||
if len(rows) == 0:
|
||||
return []
|
||||
|
||||
active_rules = []
|
||||
for rule_id in rules:
|
||||
values = [row["ruleValues"][rule_id] for row in rows if row["ruleValues"][rule_id] != ""]
|
||||
distinct = set(values)
|
||||
coverage = len(values) / len(rows)
|
||||
if len(distinct) > 1 and len(distinct) < max(2, len(rows)) and coverage >= 0.7:
|
||||
active_rules.append(rule_id)
|
||||
|
||||
if len(active_rules) == 0:
|
||||
return [
|
||||
{
|
||||
"key": f"SERVER_SERIES_{series.pk}_0",
|
||||
"label": "Series 1",
|
||||
"imageIds": [row["imageId"] for row in rows],
|
||||
"seriesInstanceUID": series.series_instance_uid,
|
||||
"bValue": None,
|
||||
"groupValues": {},
|
||||
}
|
||||
]
|
||||
|
||||
grouped = {}
|
||||
for row in rows:
|
||||
key_parts = []
|
||||
for rule_id in active_rules:
|
||||
value = row["ruleValues"][rule_id]
|
||||
key_parts.append(f"{rule_id}:{value}" if value != "" else f"{rule_id}:")
|
||||
group_key = "|".join(key_parts)
|
||||
|
||||
if group_key not in grouped:
|
||||
grouped[group_key] = {
|
||||
"imageIds": [],
|
||||
"groupValues": {rule_id: row["ruleValues"][rule_id] for rule_id in active_rules if row["ruleValues"][rule_id] != ""},
|
||||
"bValue": row["bValue"],
|
||||
}
|
||||
|
||||
grouped[group_key]["imageIds"].append(row["imageId"])
|
||||
|
||||
if grouped[group_key]["bValue"] is None and row["bValue"] is not None:
|
||||
grouped[group_key]["bValue"] = row["bValue"]
|
||||
|
||||
ordered_groups = []
|
||||
for idx, (_, payload) in enumerate(grouped.items()):
|
||||
details = []
|
||||
group_values = payload.get("groupValues", {})
|
||||
|
||||
if group_values.get("dwiBValue"):
|
||||
details.append(f"b={group_values['dwiBValue']}")
|
||||
if group_values.get("dwiDirection"):
|
||||
details.append(f"dir={group_values['dwiDirection']}")
|
||||
if group_values.get("temporalPosition"):
|
||||
details.append(f"tp={group_values['temporalPosition']}")
|
||||
if group_values.get("triggerTime"):
|
||||
details.append(f"tr={group_values['triggerTime']}")
|
||||
if group_values.get("contentTime"):
|
||||
details.append(f"t={group_values['contentTime']}")
|
||||
if group_values.get("echoTime"):
|
||||
details.append(f"TE={group_values['echoTime']}")
|
||||
if group_values.get("echoNumber"):
|
||||
details.append(f"echo={group_values['echoNumber']}")
|
||||
|
||||
label = " • ".join(details) if len(details) > 0 else f"Series {idx + 1}"
|
||||
|
||||
ordered_groups.append(
|
||||
{
|
||||
"key": f"SERVER_SERIES_{series.pk}_{idx}",
|
||||
"label": label,
|
||||
"imageIds": payload["imageIds"],
|
||||
"seriesInstanceUID": series.series_instance_uid,
|
||||
"bValue": payload.get("bValue"),
|
||||
"groupValues": group_values,
|
||||
}
|
||||
)
|
||||
|
||||
return ordered_groups
|
||||
|
||||
|
||||
class SynMixin(object):
|
||||
# class Meta:
|
||||
# abstract = True
|
||||
@@ -837,21 +988,17 @@ class Case(models.Model, AuthorMixin, QuestionMixin):
|
||||
logger.debug(f"Building stacks for case {case_obj.pk} with prefix '{prefix}'")
|
||||
stacks = []
|
||||
for series in case_obj.get_ordered_series():
|
||||
images = [f"{REMOTE_URL}{img.image.url}" for img in series.get_images()]
|
||||
series_images = list(series.get_images())
|
||||
series_images_with_urls = [(img, f"{REMOTE_URL}{img.image.url}") for img in series_images]
|
||||
images = [url for _, url in series_images_with_urls]
|
||||
declared_groups = _build_declared_series_groups(series, series_images_with_urls)
|
||||
name = f"{prefix}: {series}" if prefix else str(series)
|
||||
stacks.append(
|
||||
{
|
||||
"name": name,
|
||||
"imageIds": images,
|
||||
"seriesId": series.pk,
|
||||
"series": [
|
||||
{
|
||||
"name": name,
|
||||
"imageIds": images,
|
||||
"seriesId": series.pk,
|
||||
"seriesInstanceUID": series.series_instance_uid,
|
||||
}
|
||||
],
|
||||
"series": declared_groups,
|
||||
}
|
||||
)
|
||||
return stacks
|
||||
@@ -1943,21 +2090,17 @@ class CaseDetail(models.Model):
|
||||
def build_stacks_for(case_obj, prefix=None):
|
||||
stacks = []
|
||||
for series in case_obj.get_ordered_series():
|
||||
images = [f"{REMOTE_URL}{img.image.url}" for img in series.get_images()]
|
||||
series_images = list(series.get_images())
|
||||
series_images_with_urls = [(img, f"{REMOTE_URL}{img.image.url}") for img in series_images]
|
||||
images = [url for _, url in series_images_with_urls]
|
||||
declared_groups = _build_declared_series_groups(series, series_images_with_urls)
|
||||
name = f"{prefix}: {series}" if prefix else str(series)
|
||||
stacks.append(
|
||||
{
|
||||
"name": name,
|
||||
"imageIds": images,
|
||||
"seriesId": series.pk,
|
||||
"series": [
|
||||
{
|
||||
"name": name,
|
||||
"imageIds": images,
|
||||
"seriesId": series.pk,
|
||||
"seriesInstanceUID": series.series_instance_uid,
|
||||
}
|
||||
],
|
||||
"series": declared_groups,
|
||||
}
|
||||
)
|
||||
return stacks
|
||||
|
||||
+27
-27
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user