77import plotly .express as px
88import plotly .graph_objects as go
99
10- from app .services .epmc_client import prepare_epmc_data , get_authors_by_article
10+ from app .services .epmc_client import prepare_epmc_data , get_affiliations_by_article
1111from app .constants .constants import COUNTRIES_WHITELIST
1212
1313
@@ -242,11 +242,11 @@ def update_most_cited_table(_top_n):
242242 )
243243 def show_epmc_details (selected_rows , search_value , year_filter , affiliation_filter ):
244244 if not selected_rows or entries_df .empty :
245- return dbc .Alert ("Select an entry to see details" , color = "info" ), None
245+ return dbc .Alert ("Select an entry to see details" , color = "info" ), None , None
246246
247247 filtered_df = get_filtered_sorted_df (search_value , year_filter , affiliation_filter )
248248 if filtered_df .empty or selected_rows [0 ] >= len (filtered_df ):
249- return dbc .Alert ("Select an entry to see details" , color = "info" ), None
249+ return dbc .Alert ("Select an entry to see details" , color = "info" ), None , None
250250
251251 entry = filtered_df .iloc [selected_rows [0 ]]
252252
@@ -269,72 +269,79 @@ def show_epmc_details(selected_rows, search_value, year_filter, affiliation_filt
269269 or parsed .get ("article_id" )
270270 or parsed .get ("id" )
271271 )
272- authors = get_authors_by_article (pm_id ) if pm_id else []
273- valid_authors = [a for a in authors if isinstance (a , dict )] if isinstance (authors , list ) else []
274- if valid_authors and any ("author_order" in a for a in valid_authors ):
275- valid_authors = sorted (
276- valid_authors ,
277- key = lambda a : (a .get ("author_order" ) is None , a .get ("author_order" ) or 0 ),
278- )
272+ affiliation_rows = get_affiliations_by_article (pm_id ) if pm_id else []
273+ affiliation_rows = [r for r in affiliation_rows if isinstance (r , dict )]
274+ affiliation_rows = sorted (
275+ affiliation_rows ,
276+ key = lambda r : (
277+ r .get ("author_order" ) is None ,
278+ r .get ("author_order" ) or 0 ,
279+ r .get ("affiliation_order" ) is None ,
280+ r .get ("affiliation_order" ) or 0 ,
281+ ),
282+ )
279283
280- # Build affiliation index: org_name -> number, and reverse map: number -> list of author names
281- raw_affiliations = parsed .get ("affiliations" ) or []
282- affiliation_index = {} # org_name -> aff_number
283- affiliation_list = [] # list of (number, org_name)
284- aff_counter = 1
285-
286- # First pass: build affiliation index
287- if isinstance (raw_affiliations , list ):
288- for aff in sorted (raw_affiliations , key = lambda x : x .get ("affiliation_order" , 0 )):
289- org = aff .get ("org_name" , "" ).strip ()
290- if org and org not in affiliation_index :
291- affiliation_index [org ] = aff_counter
292- affiliation_list .append ((aff_counter , org ))
293- aff_counter += 1
294-
295- # Build affiliation -> author names map
284+ # Build ordered author map from affiliation rows
285+ author_order = []
296286 author_id_to_name = {}
297- for a in valid_authors :
298- first = (a .get ("firstname" ) or "" ).strip ()
299- last = (a .get ("lastname" ) or "" ).strip ()
300- full = f"{ first } { last } " .strip () or (a .get ("fullname" ) or "" ).strip ()
301- if full :
302- author_id_to_name [a .get ("id" )] = full
303-
304- aff_to_authors = {} # aff_number -> list of author names
305- if isinstance (raw_affiliations , list ):
306- for aff in raw_affiliations :
307- org = aff .get ("org_name" , "" ).strip ()
308- aid = aff .get ("author_id" )
309- if org in affiliation_index and aid in author_id_to_name :
310- aff_num = affiliation_index [org ]
311- if aff_num not in aff_to_authors :
312- aff_to_authors [aff_num ] = []
313- author_name = author_id_to_name [aid ]
314- if author_name not in aff_to_authors [aff_num ]:
315- aff_to_authors [aff_num ].append (author_name )
316-
317- # Build author items with superscript aff numbers
318- author_id_to_affs = {} # author_id -> list of aff numbers
319- if isinstance (raw_affiliations , list ):
320- for aff in raw_affiliations :
321- org = aff .get ("org_name" , "" ).strip ()
322- aid = aff .get ("author_id" )
323- if org in affiliation_index and aid :
324- if aid not in author_id_to_affs :
325- author_id_to_affs [aid ] = []
326- aff_num = affiliation_index [org ]
327- if aff_num not in author_id_to_affs [aid ]:
328- author_id_to_affs [aid ].append (aff_num )
287+ for row in affiliation_rows :
288+ aid = row .get ("author_id" )
289+ if aid is None :
290+ continue
291+ first = (row .get ("firstname" ) or "" ).strip ()
292+ last = (row .get ("lastname" ) or "" ).strip ()
293+ full = f"{ first } { last } " .strip () or (row .get ("fullname" ) or "" ).strip ()
294+ if not full :
295+ continue
296+ if aid not in author_id_to_name :
297+ author_id_to_name [aid ] = full
298+ author_order .append (aid )
299+
300+ # Build affiliation index and reverse map in affiliation order
301+ affiliation_index = {}
302+ affiliation_list = []
303+ for row in sorted (
304+ affiliation_rows ,
305+ key = lambda r : (
306+ r .get ("affiliation_order" ) is None ,
307+ r .get ("affiliation_order" ) or 0 ,
308+ r .get ("author_order" ) is None ,
309+ r .get ("author_order" ) or 0 ,
310+ ),
311+ ):
312+ org = (row .get ("org_name" ) or "" ).strip ()
313+ if org and org not in affiliation_index :
314+ aff_num = len (affiliation_index ) + 1
315+ affiliation_index [org ] = aff_num
316+ affiliation_list .append ((aff_num , org ))
317+
318+ aff_to_authors = {}
319+ author_id_to_affs = {}
320+ for row in affiliation_rows :
321+ aid = row .get ("author_id" )
322+ org = (row .get ("org_name" ) or "" ).strip ()
323+ if aid is None or org not in affiliation_index or aid not in author_id_to_name :
324+ continue
325+
326+ aff_num = affiliation_index [org ]
327+ author_name = author_id_to_name [aid ]
328+
329+ if aff_num not in aff_to_authors :
330+ aff_to_authors [aff_num ] = []
331+ if author_name not in aff_to_authors [aff_num ]:
332+ aff_to_authors [aff_num ].append (author_name )
333+
334+ if aid not in author_id_to_affs :
335+ author_id_to_affs [aid ] = []
336+ if aff_num not in author_id_to_affs [aid ]:
337+ author_id_to_affs [aid ].append (aff_num )
329338
330339 author_items = []
331- for a in valid_authors :
332- first = (a .get ("firstname" ) or "" ).strip ()
333- last = (a .get ("lastname" ) or "" ).strip ()
334- full = f"{ first } { last } " .strip () or (a .get ("fullname" ) or "" ).strip ()
340+ for aid in author_order :
341+ full = author_id_to_name .get (aid )
335342 if not full :
336343 continue
337- aff_nums = author_id_to_affs .get (a . get ( "id" ) , [])
344+ aff_nums = author_id_to_affs .get (aid , [])
338345 superscript = (" " + "," .join (f"[{ n } ]" for n in sorted (aff_nums ))) if aff_nums else ""
339346 author_items .append (f"{ full } { superscript } " )
340347
@@ -351,6 +358,7 @@ def aff_item(num, org):
351358 ], style = {"marginBottom" : "6px" })
352359
353360 first_aff_component = aff_item (* affiliation_list [0 ]) if affiliation_list else html .P ("N/A" )
361+ first_aff_text = f"{ affiliation_list [0 ][0 ]} . { affiliation_list [0 ][1 ]} " if affiliation_list else "N/A"
354362 rest_aff_components = [aff_item (num , org ) for num , org in affiliation_list [1 :]] if len (affiliation_list ) > 1 else []
355363
356364 # Abstract HTML to Markdown conversion
@@ -434,7 +442,7 @@ def aff_item(num, org):
434442 ]),
435443 ], style = {"boxShadow" : "0 4px 10px rgba(0,0,0,0.1)" })
436444
437- return card , first_author_store , first_aff_component
445+ return card , first_author_store , first_aff_text
438446
439447 # -----------------------
440448 # Build initial charts from cached data
0 commit comments