3030DATA_DIR = f"{ PROJECT_ROOT } /profile_library"
3131REPO_OWNER = "bramstroker"
3232REPO_NAME = "homeassistant-powercalc"
33+ MAX_CONCURRENT_FILE_TASKS = 50
3334
3435@dataclass
3536class Author :
@@ -103,28 +104,60 @@ async def generate_library_json(model_listing: list[dict]) -> None:
103104 print ("Generated library.json" )
104105
105106
106- async def update_authors (model_listing : list [dict ]) -> None :
107- tasks = []
108- for model in model_listing :
109- author = model .get ("author_info" )
110- model_json_path = model .get ("full_path" )
111- if author :
112- continue
113-
114- tasks .append (process_author_update (model_json_path ))
115-
116- if tasks :
117- await asyncio .gather (* tasks )
107+ async def update_authors (_model_listing : list [dict ]) -> None :
108+ model_json_paths = sorted (
109+ glob .glob (f"{ DATA_DIR } /**/model.json" , recursive = True ),
110+ key = lambda model_json_path : (len (Path (model_json_path ).parts ), model_json_path ),
111+ )
112+ for model_json_path in model_json_paths :
113+ await process_author_update (model_json_path )
118114
119115async def process_author_update (model_json_path : str ) -> None :
120116 """Process a single author update asynchronously"""
121- author = await find_first_commit_author (model_json_path )
122- if author is None :
123- print (f"Skipping { model_json_path } , author not found" )
117+ async with aiofiles .open (model_json_path , mode = "r" ) as file :
118+ content = await file .read ()
119+ json_data = json .loads (content )
120+
121+ changed = False
122+ if is_main_model_json (model_json_path ) and not has_author_info (json_data ):
123+ author = await find_first_commit_author (model_json_path )
124+ if author is None :
125+ print (f"Skipping { model_json_path } , author not found" )
126+ else :
127+ json_data ["author_info" ] = author_to_json (author )
128+ changed = True
129+
130+ if "author" in json_data :
131+ del json_data ["author" ]
132+ changed = True
133+
134+ if not changed :
124135 return
125136
126- await write_author_to_file (model_json_path , author )
127- print (f"Updated { model_json_path } with author { author } " )
137+ async with aiofiles .open (model_json_path , mode = "w" ) as file :
138+ await file .write (json .dumps (json_data , indent = 2 , ensure_ascii = False ) + "\n " )
139+ print (f"Updated author metadata in { model_json_path } " )
140+
141+
142+ def has_author_info (model_data : dict ) -> bool :
143+ author_info = model_data .get ("author_info" )
144+ return isinstance (author_info , dict ) and bool (author_info .get ("name" )) and bool (author_info .get ("github" ))
145+
146+
147+ def is_main_model_json (model_json_path : str ) -> bool :
148+ relative_path = Path (model_json_path ).relative_to (DATA_DIR )
149+ return len (relative_path .parts ) == 3
150+
151+
152+ def author_to_json (author : Author ) -> dict :
153+ author_json = {
154+ "name" : author .name ,
155+ "github" : author .github_username ,
156+ }
157+ if author .email :
158+ author_json ["email" ] = author .email
159+ return author_json
160+
128161
129162async def update_translations (model_listing : list [dict ]) -> None :
130163 data_translations : dict [str , str ] = {}
@@ -204,9 +237,13 @@ async def get_model_list() -> list[dict]:
204237 recursive = True ,
205238 )
206239
207- # Process files concurrently
208- tasks = [process_model_file (json_path ) for json_path in json_paths ]
209- models = await asyncio .gather (* tasks )
240+ semaphore = asyncio .Semaphore (MAX_CONCURRENT_FILE_TASKS )
241+
242+ async def process_with_limit (json_path : str ) -> dict :
243+ async with semaphore :
244+ return await process_model_file (json_path )
245+
246+ models = await asyncio .gather (* (process_with_limit (json_path ) for json_path in json_paths ))
210247
211248 # Filter out None values (if any)
212249 return [model for model in models if model ]
@@ -216,6 +253,7 @@ async def process_model_file(json_path: str) -> dict:
216253 async with aiofiles .open (json_path , mode = 'r' ) as json_file :
217254 content = await json_file .read ()
218255 model_data : dict = json .loads (content )
256+ model_data .pop ("author" , None )
219257 model_directory = os .path .dirname (json_path )
220258 model_data ['id' ] = os .path .basename (model_directory )
221259 if "linked_profile" in model_data :
@@ -417,34 +455,6 @@ async def find_first_commit_author(file: str, check_paths: bool = True) -> Autho
417455 return author
418456 return None
419457
420- async def read_author_from_file (file_path : str ) -> str | None :
421- """Read the author from the model.json file."""
422- async with aiofiles .open (file_path , mode = 'r' ) as file :
423- content = await file .read ()
424- json_data = json .loads (content )
425-
426- return json_data .get ("author" )
427-
428-
429- async def write_author_to_file (file_path : str , author : Author ) -> None :
430- """Write the author to the model.json file."""
431- # Read the existing content
432- async with aiofiles .open (file_path , mode = 'r' ) as file :
433- content = await file .read ()
434- json_data = json .loads (content )
435-
436- author_json = {
437- "name" : author .name ,
438- "github" : author .github_username ,
439- }
440- if author .email :
441- author_json ["email" ] = author .email
442- json_data ["author_info" ] = author_json
443- json_data ["author" ] = author .name # For backward compatibility, will be removed later
444-
445- async with aiofiles .open (file_path , mode = 'w' ) as file :
446- await file .write (json .dumps (json_data , indent = 2 ) + "\n " )
447-
448458async def main_async ():
449459 parser = argparse .ArgumentParser (description = "Process profiles JSON files and perform updates." )
450460 parser .add_argument ("--authors" , action = "store_true" , help = "Update authors" )
0 commit comments