Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 35 additions & 8 deletions frontend/src/components/chat/FundusRecordCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import {
import Grid from "@mui/material/Grid2";
import React, { useEffect, useState } from "react";
import { useAgentService } from "../../hooks/useLookupService";
import { FundusRecord, FundusRecordImage } from "../../types/fundusTypes";
import { FundusCollection, FundusRecord, FundusRecordImage } from "../../types/fundusTypes";

interface FundusRecordCardProps {
muragId: string;
Expand All @@ -26,11 +26,12 @@ interface FundusRecordCardProps {
const FundusRecordCard: React.FC<FundusRecordCardProps> = ({ muragId }) => {
const [record, setRecord] = useState<FundusRecord | undefined>(undefined);
const [image, setImage] = useState<FundusRecordImage | undefined>(undefined);
const [collection, setCollection] = useState<FundusCollection | undefined>(undefined);
const [loading, setLoading] = useState<boolean>(true);
const [modalOpen, setModalOpen] = useState<boolean>(false);
const [imageLoaded, setImageLoaded] = useState(false);
const [elevation, setElevation] = useState(1);
const { getFundusRecord, getFundusRecordImage } = useAgentService();
const { getFundusRecord, getFundusRecordImage, getFundusCollectionByName } = useAgentService();

useEffect(() => {
const fetchRecordData = async () => {
Expand All @@ -42,6 +43,11 @@ const FundusRecordCard: React.FC<FundusRecordCardProps> = ({ muragId }) => {
if (imageData) {
setImage(imageData);
}
// Fetch collection to get field ordering information
const collectionData = await getFundusCollectionByName(recordData.collection_name);
if (collectionData) {
setCollection(collectionData);
}
}
} catch (error) {
console.error("Error fetching record:", error);
Expand All @@ -51,7 +57,7 @@ const FundusRecordCard: React.FC<FundusRecordCardProps> = ({ muragId }) => {
};

fetchRecordData();
}, [muragId, getFundusRecord, getFundusRecordImage]);
}, [muragId, getFundusRecord, getFundusRecordImage, getFundusCollectionByName]);

const handleOpenModal = () => {
setModalOpen(true);
Expand Down Expand Up @@ -170,11 +176,32 @@ const FundusRecordCard: React.FC<FundusRecordCardProps> = ({ muragId }) => {
<Typography variant="subtitle1" fontWeight="bold">
Additional Details
</Typography>
{Object.entries(record.details).map(([key, value]) => (
<Typography key={key}>
<strong>{key}:</strong> {value}
</Typography>
))}
{(() => {
// Order details based on position from collection field configuration
const detailEntries = Object.entries(record.details);

if (collection && collection.fields) {
// Create a map of field labels to positions
const fieldPositions = new Map(
collection.fields
.filter((f): f is FundusRecordField & { position: number } => f.position !== null)
.map(f => [f.label_en, f.position])
);

// Sort details by position
detailEntries.sort((a, b) => {
const posA = fieldPositions.get(a[0]) ?? 999;
const posB = fieldPositions.get(b[0]) ?? 999;
return posA - posB;
});
}

return detailEntries.map(([key, value]) => (
<Typography key={key}>
<strong>{key}:</strong> {value}
</Typography>
));
})()}
</Grid>

<Grid size={12}>
Expand Down
16 changes: 15 additions & 1 deletion frontend/src/hooks/useLookupService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,19 @@ export function useAgentService() {
}
}, []);

return { loading, error, getFundusRecord, getFundusRecordImage, getFundusCollection };
const getFundusCollectionByName = useCallback(async (collectionName: string): Promise<FundusCollection | undefined> => {
setLoading(true);
setError(null);
try {
const collection = await lookupService.getFundusCollectionByName(collectionName);
return collection;
} catch (err) {
setError(err as Error);
return undefined;
} finally {
setLoading(false);
}
}, []);

return { loading, error, getFundusRecord, getFundusRecordImage, getFundusCollection, getFundusCollectionByName };
}
8 changes: 8 additions & 0 deletions frontend/src/services/lookupApiService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,14 @@ export const lookupService = {
throw new Error('Failed to fetch Fundus Collection');
}
return response.json();
},
async getFundusCollectionByName(collectionName: string): Promise<FundusCollection> {
const params = new URLSearchParams({ collection_name: collectionName });
const response = await fetch(`/api/data/lookup/collections?${params}`);
if (!response.ok) {
throw new Error('Failed to fetch Fundus Collection');
}
return response.json();
}
};

Expand Down
1 change: 1 addition & 0 deletions frontend/src/types/fundusTypes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ export interface FundusRecordField {
name: string;
label_en: string;
label_de: string;
position: number | null;
}

export interface FundusCollection {
Expand Down
2 changes: 2 additions & 0 deletions src/fundus_murag/data/dtos/fundus.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,13 @@ class FundusRecordField(BaseModel):
name (str): The name of the field.
label_en (str): The label of the field in English.
label_de (str): The label of the field in German.
position (int | None): The position/order of the field for display purposes.
"""

name: str
label_en: str
label_de: str
position: int | None = None


class FundusCollection(BaseModel):
Expand Down
9 changes: 4 additions & 5 deletions src/fundus_murag/data/vector_db.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,12 +290,11 @@ def _resolve_detail_field_names(self, details: list[dict[str, str]], collection_
if detail["value"] == "None" or detail["value"] == "":
continue
field_value = detail["value"]
if detail["key"] == "ident_nr" or detail["key"] not in fields:
field_name = detail["key"]
else:
# Only include fields that are configured in the collection's fields
# This ensures only visible fields are shown
if detail["key"] in fields:
field_name = fields[detail["key"]]

resolved[field_name] = field_value
resolved[field_name] = field_value

return resolved

Expand Down
30 changes: 29 additions & 1 deletion src/fundus_murag/scripts/generate_murag_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,7 @@ def _add_collection_fields(fields_dir: Path, collections_df: pd.DataFrame) -> pd
"field_name": [],
"csv_headers": [],
"labels": [],
"positions": [],
"title_fields": [],
}
for k, v in fields_dfs.items():
Expand All @@ -328,6 +329,12 @@ def _add_collection_fields(fields_dir: Path, collections_df: pd.DataFrame) -> pd
data["labels"].append(
[{"de": ld, "en": le} for ld, le in zip(list(v["label_de"]), list(v["label_en"]))]
)

# Extract position field if it exists
if "position" in list(v.columns):
data["positions"].append(v["position"].values)
else:
data["positions"].append([None] * len(v))

title_fields = {}
for _, row in v.iterrows():
Expand Down Expand Up @@ -359,16 +366,19 @@ def _add_collection_fields(fields_dir: Path, collections_df: pd.DataFrame) -> pd

fns = row["field_name"]
labels = row["labels"]
positions = row["positions"]
fields = []
for fn, label in zip(fns, labels):
for fn, label, pos in zip(fns, labels, positions):
ld, le = label["de"], label["en"]
# Skip fields without labels (they should not be displayed)
if (ld is None or ld == "") and (le is None or le == ""):
continue
fields.append(
{
"name": fn,
"label_de": ld,
"label_en": le,
"position": int(pos) if pos is not None and not pd.isna(pos) else None,
}
)
data["fields"].append(fields)
Expand Down Expand Up @@ -478,6 +488,24 @@ def _create_records_df(
axis=1,
).drop("details", axis=1)

# Filter details columns to only include fields visible for each collection
def filter_details_for_collection(row):
collection_name = row["collection_name"]
collection = collections_df[collections_df.collection_name == collection_name].iloc[0]
visible_field_names = {field["name"] for field in collection.fields}

# Create a new row with only visible detail fields
filtered_row = {k: v for k, v in row.items() if not k.startswith("details_")}
for k, v in row.items():
if k.startswith("details_"):
field_name = k.replace("details_", "")
if field_name in visible_field_names:
filtered_row[k] = v

return pd.Series(filtered_row)

records_df = records_df.apply(filter_details_for_collection, axis=1)

# Add title
records_df["title"] = records_df.apply(lambda x: _get_record_title(x, collections_df), axis=1)
title = records_df.pop("title")
Expand Down