11"""Sync Mediebank employee portraits to S3."""
22
3- import unicodedata
4-
53import duckdb
64import requests
75import s3fs
1210
1311from .settings import log
1412
15- PAGE_SIZE = 199
13+ PAGE_SIZE = 100
1614
1715app = typer .Typer (help = "Commands to handle Mediebank employee portraits" )
1816
@@ -77,12 +75,6 @@ def download_asset(token: str, api_url: str, asset_id: str) -> bytes | None:
7775 return response .content
7876
7977
80- def normalize (value : str ) -> str :
81- """Lowercase, strip diacritics and non-alphanumeric characters."""
82- normalized = unicodedata .normalize ("NFKD" , value .lower ())
83- return "" .join (c for c in normalized if c .isalnum ())
84-
85-
8678def load_employees (employees_parquet : str ) -> dict [str , dict ]:
8779 """Load employees with a portrait url from the employees parquet."""
8880 with duckdb .connect () as connection :
@@ -91,48 +83,30 @@ def load_employees(employees_parquet: str) -> dict[str, dict]:
9183 [employees_parquet ],
9284 ).fetchall ()
9385 return {
94- str (employee_id ): {
95- "display" : f"{ first } { last } " ,
96- "norm" : normalize (f"{ first } { last } " ),
97- }
86+ str (employee_id ): {"name" : f"{ first } { last } " }
9887 for employee_id , first , last , url in rows
9988 if url and "ansattbilder/" in url
10089 }
10190
10291
103- def asset_names (asset : dict ) -> set [str ]:
104- """Normalized names associated with a Mediebank asset."""
105- names = [asset .get ("headline" )]
106- names += [person .get ("personName" , "" ) for person in asset .get ("personsShown" , [])]
107- names .append (asset .get ("file" , {}).get ("originalFilename" , "" ))
108- return {normalize (name ) for name in names if name }
109-
110-
11192def match_assets_to_employees (
11293 assets : list [dict ], employees : dict [str , dict ]
11394) -> dict [str , dict ]:
114- """Map each employee to their best portrait (newest dateArchived wins) ."""
115- by_norm = {
116- employee ["norm " ]: employee_id for employee_id , employee in employees .items ()
95+ """Map each employee to their newest portrait, matched by headline ."""
96+ by_name = {
97+ employee ["name " ]: employee_id for employee_id , employee in employees .items ()
11798 }
11899
119100 candidates : dict [str , list [dict ]] = {}
120101 for asset in assets :
121- stem = asset .get ("file" , {}).get ("originalFilename" , "" ).split ("." )[0 ]
122- employee_id = stem if stem .isdigit () and stem in employees else None
123- if not employee_id :
124- employee_id = next (
125- (by_norm [name ] for name in asset_names (asset ) if name in by_norm ),
126- None ,
127- )
102+ employee_id = by_name .get (asset ["headline" ])
128103 if employee_id :
129104 candidates .setdefault (employee_id , []).append (asset )
130105
131- log .info ("matched assets to employees" , matched = len (candidates ))
132- return {
133- employee_id : max (assets , key = lambda asset : asset .get ("dateArchived" ) or "" )
134- for employee_id , assets in candidates .items ()
135- }
106+ matched : dict [str , dict ] = {}
107+ for employee_id , portraits in candidates .items ():
108+ matched [employee_id ] = max (portraits , key = lambda asset : asset ["dateArchived" ])
109+ return matched
136110
137111
138112@app .command (help = "Sync employee portraits from Mediebank to S3" )
@@ -212,7 +186,7 @@ def employees_portraits(
212186 log .warning (
213187 "no portrait found" ,
214188 employee_id = employee_id ,
215- name = employees [employee_id ]["display " ],
189+ name = employees [employee_id ]["name " ],
216190 )
217191
218192 log .info ("Mediebank employee portraits sync completed" )
0 commit comments