Skip to content

Commit 997a36a

Browse files
Copilotpesc101
andcommitted
Add position field to collection fields and filter visible metadata
Co-authored-by: pesc101 <53601317+pesc101@users.noreply.github.com>
1 parent 1d94d8f commit 997a36a

File tree

7 files changed

+113
-15
lines changed

7 files changed

+113
-15
lines changed

frontend/src/components/chat/FundusRecordCard.tsx

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
import Grid from "@mui/material/Grid2";
1818
import React, { useEffect, useState } from "react";
1919
import { useAgentService } from "../../hooks/useLookupService";
20-
import { FundusRecord, FundusRecordImage } from "../../types/fundusTypes";
20+
import { FundusCollection, FundusRecord, FundusRecordImage } from "../../types/fundusTypes";
2121

2222
interface FundusRecordCardProps {
2323
muragId: string;
@@ -26,11 +26,12 @@ interface FundusRecordCardProps {
2626
const FundusRecordCard: React.FC<FundusRecordCardProps> = ({ muragId }) => {
2727
const [record, setRecord] = useState<FundusRecord | undefined>(undefined);
2828
const [image, setImage] = useState<FundusRecordImage | undefined>(undefined);
29+
const [collection, setCollection] = useState<FundusCollection | undefined>(undefined);
2930
const [loading, setLoading] = useState<boolean>(true);
3031
const [modalOpen, setModalOpen] = useState<boolean>(false);
3132
const [imageLoaded, setImageLoaded] = useState(false);
3233
const [elevation, setElevation] = useState(1);
33-
const { getFundusRecord, getFundusRecordImage } = useAgentService();
34+
const { getFundusRecord, getFundusRecordImage, getFundusCollectionByName } = useAgentService();
3435

3536
useEffect(() => {
3637
const fetchRecordData = async () => {
@@ -42,6 +43,11 @@ const FundusRecordCard: React.FC<FundusRecordCardProps> = ({ muragId }) => {
4243
if (imageData) {
4344
setImage(imageData);
4445
}
46+
// Fetch collection to get field ordering information
47+
const collectionData = await getFundusCollectionByName(recordData.collection_name);
48+
if (collectionData) {
49+
setCollection(collectionData);
50+
}
4551
}
4652
} catch (error) {
4753
console.error("Error fetching record:", error);
@@ -51,7 +57,7 @@ const FundusRecordCard: React.FC<FundusRecordCardProps> = ({ muragId }) => {
5157
};
5258

5359
fetchRecordData();
54-
}, [muragId, getFundusRecord, getFundusRecordImage]);
60+
}, [muragId, getFundusRecord, getFundusRecordImage, getFundusCollectionByName]);
5561

5662
const handleOpenModal = () => {
5763
setModalOpen(true);
@@ -170,11 +176,32 @@ const FundusRecordCard: React.FC<FundusRecordCardProps> = ({ muragId }) => {
170176
<Typography variant="subtitle1" fontWeight="bold">
171177
Additional Details
172178
</Typography>
173-
{Object.entries(record.details).map(([key, value]) => (
174-
<Typography key={key}>
175-
<strong>{key}:</strong> {value}
176-
</Typography>
177-
))}
179+
{(() => {
180+
// Order details based on position from collection field configuration
181+
const detailEntries = Object.entries(record.details);
182+
183+
if (collection && collection.fields) {
184+
// Create a map of field labels to positions
185+
const fieldPositions = new Map(
186+
collection.fields
187+
.filter(f => f.position !== null)
188+
.map(f => [f.label_en, f.position!])
189+
);
190+
191+
// Sort details by position
192+
detailEntries.sort((a, b) => {
193+
const posA = fieldPositions.get(a[0]) ?? 999;
194+
const posB = fieldPositions.get(b[0]) ?? 999;
195+
return posA - posB;
196+
});
197+
}
198+
199+
return detailEntries.map(([key, value]) => (
200+
<Typography key={key}>
201+
<strong>{key}:</strong> {value}
202+
</Typography>
203+
));
204+
})()}
178205
</Grid>
179206

180207
<Grid size={12}>

frontend/src/hooks/useLookupService.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,19 @@ export function useAgentService() {
4949
}
5050
}, []);
5151

52-
return { loading, error, getFundusRecord, getFundusRecordImage, getFundusCollection };
52+
const getFundusCollectionByName = useCallback(async (collectionName: string): Promise<FundusCollection | undefined> => {
53+
setLoading(true);
54+
setError(null);
55+
try {
56+
const collection = await lookupService.getFundusCollectionByName(collectionName);
57+
return collection;
58+
} catch (err) {
59+
setError(err as Error);
60+
return undefined;
61+
} finally {
62+
setLoading(false);
63+
}
64+
}, []);
65+
66+
return { loading, error, getFundusRecord, getFundusRecordImage, getFundusCollection, getFundusCollectionByName };
5367
}

frontend/src/services/lookupApiService.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,13 @@ export const lookupService = {
2121
throw new Error('Failed to fetch Fundus Collection');
2222
}
2323
return response.json();
24+
},
25+
async getFundusCollectionByName(collectionName: string): Promise<FundusCollection> {
26+
const response = await fetch('/api/data/lookup/collections?collection_name=' + collectionName);
27+
if (!response.ok) {
28+
throw new Error('Failed to fetch Fundus Collection');
29+
}
30+
return response.json();
2431
}
2532
};
2633

frontend/src/types/fundusTypes.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ export interface FundusRecordField {
1818
name: string;
1919
label_en: string;
2020
label_de: string;
21+
position: number | null;
2122
}
2223

2324
export interface FundusCollection {

src/fundus_murag/data/dtos/fundus.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,13 @@ class FundusRecordField(BaseModel):
3838
name (str): The name of the field.
3939
label_en (str): The label of the field in English.
4040
label_de (str): The label of the field in German.
41+
position (int | None): The position/order of the field for display purposes.
4142
"""
4243

4344
name: str
4445
label_en: str
4546
label_de: str
47+
position: int | None = None
4648

4749

4850
class FundusCollection(BaseModel):

src/fundus_murag/data/vector_db.py

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -290,12 +290,11 @@ def _resolve_detail_field_names(self, details: list[dict[str, str]], collection_
290290
if detail["value"] == "None" or detail["value"] == "":
291291
continue
292292
field_value = detail["value"]
293-
if detail["key"] == "ident_nr" or detail["key"] not in fields:
294-
field_name = detail["key"]
295-
else:
293+
# Only include fields that are configured in the collection's fields
294+
# This ensures only visible fields are shown
295+
if detail["key"] in fields:
296296
field_name = fields[detail["key"]]
297-
298-
resolved[field_name] = field_value
297+
resolved[field_name] = field_value
299298

300299
return resolved
301300

src/fundus_murag/scripts/generate_murag_data.py

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ def _add_collection_fields(fields_dir: Path, collections_df: pd.DataFrame) -> pd
311311
"field_name": [],
312312
"csv_headers": [],
313313
"labels": [],
314+
"positions": [],
314315
"title_fields": [],
315316
}
316317
for k, v in fields_dfs.items():
@@ -328,6 +329,12 @@ def _add_collection_fields(fields_dir: Path, collections_df: pd.DataFrame) -> pd
328329
data["labels"].append(
329330
[{"de": ld, "en": le} for ld, le in zip(list(v["label_de"]), list(v["label_en"]))]
330331
)
332+
333+
# Extract position field if it exists
334+
if "position" in list(v.columns):
335+
data["positions"].append(v["position"].values)
336+
else:
337+
data["positions"].append([None] * len(v))
331338

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

360367
fns = row["field_name"]
361368
labels = row["labels"]
369+
positions = row["positions"]
362370
fields = []
363-
for fn, label in zip(fns, labels):
371+
for fn, label, pos in zip(fns, labels, positions):
364372
ld, le = label["de"], label["en"]
373+
# Skip fields without labels (they should not be displayed)
365374
if (ld is None or ld == "") and (le is None or le == ""):
366375
continue
367376
fields.append(
368377
{
369378
"name": fn,
370379
"label_de": ld,
371380
"label_en": le,
381+
"position": int(pos) if pos is not None and not pd.isna(pos) else None,
372382
}
373383
)
374384
data["fields"].append(fields)
@@ -430,6 +440,26 @@ def _resolve_image_paths(records_df: pd.DataFrame, record_pix_dir: Path, worker_
430440
return records_df
431441

432442

443+
def _filter_record_details(row, collections_df: pd.DataFrame) -> dict[str, str]:
444+
"""
445+
Filter record details to only include fields that are visible for the collection.
446+
Fields are visible if they have a label defined in the collection's field configuration.
447+
"""
448+
collection_name = row["collection_name"]
449+
collection = collections_df[collections_df.collection_name == collection_name].iloc[0]
450+
visible_field_names = {field["name"] for field in collection.fields}
451+
452+
# Extract all detail fields from the row
453+
all_details = {
454+
k.replace("details_", ""): v for k, v in row.items() if k.startswith("details_") and v is not None and v != ""
455+
}
456+
457+
# Filter to only include visible fields
458+
filtered_details = {k: v for k, v in all_details.items() if k in visible_field_names}
459+
460+
return filtered_details
461+
462+
433463
def _get_record_title(row, collections_df: pd.DataFrame) -> str:
434464
collection_name = row["collection_name"]
435465
collection = collections_df[collections_df.collection_name == collection_name].iloc[0]
@@ -478,6 +508,24 @@ def _create_records_df(
478508
axis=1,
479509
).drop("details", axis=1)
480510

511+
# Filter details columns to only include fields visible for each collection
512+
def filter_details_for_collection(row, collections_df):
513+
collection_name = row["collection_name"]
514+
collection = collections_df[collections_df.collection_name == collection_name].iloc[0]
515+
visible_field_names = {field["name"] for field in collection.fields}
516+
517+
# Create a new row with only visible detail fields
518+
filtered_row = {k: v for k, v in row.items() if not k.startswith("details_")}
519+
for k, v in row.items():
520+
if k.startswith("details_"):
521+
field_name = k.replace("details_", "")
522+
if field_name in visible_field_names:
523+
filtered_row[k] = v
524+
525+
return pd.Series(filtered_row)
526+
527+
records_df = records_df.apply(lambda x: filter_details_for_collection(x, collections_df), axis=1)
528+
481529
# Add title
482530
records_df["title"] = records_df.apply(lambda x: _get_record_title(x, collections_df), axis=1)
483531
title = records_df.pop("title")

0 commit comments

Comments
 (0)