From 6de1177e64b64f16be8ced2ef273c9cc6a4d59d1 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Fri, 25 Apr 2025 11:40:50 +0000 Subject: [PATCH 01/33] deem scroe values updates --- .vscode/settings.json | 4 +--- etl/hubspot_to_invoice.py | 2 +- etl/scraper/scraper.py | 7 +++---- etl/surveyPrice/surveyPrice.py | 20 ++++++++------------ 4 files changed, 13 insertions(+), 20 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9868a02..49f0804 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,9 +1,7 @@ { "jupyter.interactiveWindow.textEditor.executeSelection": true, "python.REPL.sendToNativeREPL": true, - "notebook.output.scrolling": true, - "notebook.output.textLineLimit": 0 - + "notebook.output.scrolling": true // Hot reload setting that needs to be in user settings // "jupyter.runStartupCommands": [ diff --git a/etl/hubspot_to_invoice.py b/etl/hubspot_to_invoice.py index eca4271..d969c93 100644 --- a/etl/hubspot_to_invoice.py +++ b/etl/hubspot_to_invoice.py @@ -54,7 +54,7 @@ sp.move_deals_to_completed(deal_ids) # Empty # ( in hubspot ) 29 Lower King ( empty ) - 500 - 400 # Foam -# ( in hubspot ) 6 STOKESAY STREET (foam) - 400 - 200 +# ( in hubspot ) 6 STOKESAY STREET (foam) - 400 # SCIS # 3 examples of Solar diff --git a/etl/scraper/scraper.py b/etl/scraper/scraper.py index 64d5750..a379894 100644 --- a/etl/scraper/scraper.py +++ b/etl/scraper/scraper.py @@ -8,7 +8,6 @@ from etl.utils.sharepoint.sharepoint import SharePointClient from functools import wraps import re from etl.validator.validator import DomnaSharePointValidator -from tqdm import tqdm from datetime import datetime, timedelta @@ -154,7 +153,7 @@ class SharePointScraper(): @ensure_surveyor_names_loaded def get_date_folder_names(self): - for name in tqdm(self.surveyor_names): + for name in self.surveyor_names: dates_folders = self.get_folders_in_path(f"/{name}") if 'value' not in dates_folders: raise RuntimeError(f"Failed to get dates folder from {name} in {self.sharepoint_drive.name}") @@ -234,7 +233,7 @@ class SharePointScraper(): @ensure_housing_assosiation_is_loaded def get_number_of_surverys_completed(self): - for name in tqdm(self.surveyor_names): + for name in self.surveyor_names: if name in self.surveyor_to_housing_assosications: for house_ass in self.surveyor_to_housing_assosications[name]: address_folders = self.get_folders_in_path(f"/{name}/{WEEK_COMMENCING}/{house_ass}") @@ -273,7 +272,7 @@ class SharePointScraper(): @ensure_housing_assosiation_is_loaded def download_file_for_each_address(self): paths = [] - for name in tqdm(self.surveyor_names): + for name in self.surveyor_names: if WEEK_COMMENCING in self.surveyor_to_dates_folder[name]: for house_ass in self.surveyor_to_housing_assosications[name]: address_files = self.get_folders_in_path(f"/{name}/{WEEK_COMMENCING}/{house_ass}") diff --git a/etl/surveyPrice/surveyPrice.py b/etl/surveyPrice/surveyPrice.py index a5190a5..f54da9b 100644 --- a/etl/surveyPrice/surveyPrice.py +++ b/etl/surveyPrice/surveyPrice.py @@ -42,27 +42,27 @@ class SurveyPrice(): "JJC - ECO4 CWI EMPTY": "JJC - EMPTIES", "JJC - GBIS CWI EMPTY": "JJC - EMPTIES", "JJC - ECO4 CWI REMEDIAL - FOAM": "JJC - FORMALDEHYDE EXTRACTION", - "JJC - ECO4 CWI REMEDIAL - GENERAL": "JJC - GENERAL EXTRACTIONS", + "JJC - ECO4 CWI REMEDIAL": "JJC - GENERAL EXTRACTIONS", "JJC - GBIS CWI REMEDIAL - FOAM": "JJC - FORMALDEHYDE EXTRACTION", - "JJC - GBIS CWI REMEDIAL - GENERAL": "JJC - GENERAL EXTRACTIONS", + "JJC - GBIS CWI REMEDIAL": "JJC - GENERAL EXTRACTIONS", # SCIS "SCIS - ECO4 PV": "SCIS - SOLAR", "SCIS - ECO4 CWI EMPTY": "SCIS - EMPTIES", "SCIS - GBIS CWI EMPTY": "SCIS - EMPTIES", "SCIS - ECO4 CWI REMEDIAL - FOAM": "SCIS - GENERAL EXTRACTIONS", - "SCIS - ECO4 CWI REMEDIAL - GENERAL": "SCIS - GENERAL EXTRACTIONS", + "SCIS - ECO4 CWI REMEDIAL": "SCIS - GENERAL EXTRACTIONS", "SCIS - GBIS CWI REMEDIAL - FOAM": "SCIS - GENERAL EXTRACTIONS", - "SCIS - GBIS CWI REMEDIAL - GENERAL": "SCIS - GENERAL EXTRACTIONS", + "SCIS - GBIS CWI REMEDIAL": "SCIS - GENERAL EXTRACTIONS", # SGEC "SGEC - ECO4 CWI EMPTY": "SGEC - EMPTIES", "SGEC - GBIS CWI EMPTY": "SGEC - EMPTIES", "SGEC - ECO4 CWI REMEDIAL - FOAM": "SGEC - FORMALDEHYDE EXTRACTION", - "SGEC - ECO4 CWI REMEDIAL - GENERAL": "SGEC - GENERAL EXTRACTIONS", + "SGEC - ECO4 CWI REMEDIAL": "SGEC - GENERAL EXTRACTIONS", "SGEC - GBIS CWI REMEDIAL - FOAM": "SGEC - FORMALDEHYDE EXTRACTION", - "SGEC - GBIS CWI REMEDIAL - GENERAL": "SGEC - GENERAL EXTRACTIONS", + "SGEC - GBIS CWI REMEDIAL": "SGEC - GENERAL EXTRACTIONS", } def download_price_card(self): @@ -172,7 +172,7 @@ class SurveyPrice(): file_paths = sp.download_file_for_each_address() surveys = [] - for eachAddress in file_paths: + for eachAddress in tqdm(file_paths): for address, files in eachAddress.items(): surveys.append(surveyedDataProcessor(address, files)) @@ -336,11 +336,7 @@ class SurveyPrice(): else: # Cavity wall sheet_name = f'{self.installer[row["HUBSPOT_INSTALLER"]]} - {row["HUBSPOT_WORK_TYPE"].upper()}' - if row['HUBSPOT_WALL_INSULATION'].upper() == "BEAD/FIBRE/WOOL/OTHER": - sheet_name += " - GENERAL" - elif row['HUBSPOT_WALL_INSULATION'].upper() == "EMPTY": - pass - else: + if row['HUBSPOT_WALL_INSULATION'].upper() == "FORMALDEHYDE UFFI FOAM": sheet_name += " - FOAM" sheet_name = self.hubspot_job_to_price_sheet_convertor[sheet_name] price_matrix = self.get_price_matrix(sheet_name) From 0e06f509d1e2e6deec9c4f066db081d862e95d46 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Fri, 25 Apr 2025 12:57:26 +0000 Subject: [PATCH 02/33] deem scroe values updates --- etl/hubspot_to_invoice.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/etl/hubspot_to_invoice.py b/etl/hubspot_to_invoice.py index d969c93..ab3bf2c 100644 --- a/etl/hubspot_to_invoice.py +++ b/etl/hubspot_to_invoice.py @@ -73,6 +73,5 @@ sp.move_deals_to_completed(deal_ids) # Observation: -""" -2 queensway is wrong due the fact that csr and empty cavity but deem score says cavity -""" \ No newline at end of file + + From e4a91a559655f424cc6ed6769907e3172ffb6fcd Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 28 Apr 2025 09:06:21 +0000 Subject: [PATCH 03/33] hubspot to db --- etl/hubspot_to_db.py | 1 + etl/hubspot_to_invoice.py | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) create mode 100644 etl/hubspot_to_db.py diff --git a/etl/hubspot_to_db.py b/etl/hubspot_to_db.py new file mode 100644 index 0000000..8cde782 --- /dev/null +++ b/etl/hubspot_to_db.py @@ -0,0 +1 @@ +print("hello world") diff --git a/etl/hubspot_to_invoice.py b/etl/hubspot_to_invoice.py index ab3bf2c..b13ac3c 100644 --- a/etl/hubspot_to_invoice.py +++ b/etl/hubspot_to_invoice.py @@ -31,7 +31,8 @@ sp.upload_to_sharepoint(sp.get_master_rate_card_path(), "COPY_OF_RATE_CARD_USED. deal_ids = df["HUBSPOT_DEAL_ID"].tolist() -sp.move_deals_to_completed(deal_ids) +# Commented out as i don't want to sync up hubspot_to_db just yet +# sp.move_deals_to_completed(deal_ids) """ TODO: From 102281f08d953a97c73c439ac3ce497c07598102 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 28 Apr 2025 09:12:40 +0000 Subject: [PATCH 04/33] load to DB --- etl/db/load.py | 5 +++++ etl/hubspot_to_db.py | 13 ++++++++++++- etl/hubspot_to_invoice.py | 3 ++- etl/surveyPrice/surveyPrice.py | 2 +- 4 files changed, 20 insertions(+), 3 deletions(-) create mode 100644 etl/db/load.py diff --git a/etl/db/load.py b/etl/db/load.py new file mode 100644 index 0000000..ec9145b --- /dev/null +++ b/etl/db/load.py @@ -0,0 +1,5 @@ + + +class loadToDb(): + def __init__(self): + pass \ No newline at end of file diff --git a/etl/hubspot_to_db.py b/etl/hubspot_to_db.py index 8cde782..867cc37 100644 --- a/etl/hubspot_to_db.py +++ b/etl/hubspot_to_db.py @@ -1 +1,12 @@ -print("hello world") +import os + + +os.environ["SHAREPOINT_CLIENT_ID"] = "895e3b77-b1d7-43ec-b18f-dcfe07cdfeaf" +os.environ["SHAREPOINT_CLIENT_SECRET"] = "SOf8Q~-is4wdQiqvEEm9FlJQRAY9ELGaj5Qz-a6E" +os.environ["SHAREPOINT_TENANT_ID"] = "c3f7519c-2719-4547-af04-6da6cbfd8f8f" +os.environ["SOUTH_COAST_INSULATION_SERVICE_SHAREPOINT_ID"] = "b5a51507-9427-4ee0-b03e-90ec7681e2d3" +os.environ["JJC_SERVICE_SHAREPOINT_ID"] = "7fdd0485-bbf3-4b29-b30f-98c81c2a6284" + +p + + diff --git a/etl/hubspot_to_invoice.py b/etl/hubspot_to_invoice.py index b13ac3c..c243fac 100644 --- a/etl/hubspot_to_invoice.py +++ b/etl/hubspot_to_invoice.py @@ -6,6 +6,7 @@ os.environ["SHAREPOINT_CLIENT_SECRET"] = "SOf8Q~-is4wdQiqvEEm9FlJQRAY9ELGaj5Qz-a os.environ["SHAREPOINT_TENANT_ID"] = "c3f7519c-2719-4547-af04-6da6cbfd8f8f" os.environ["SOUTH_COAST_INSULATION_SERVICE_SHAREPOINT_ID"] = "b5a51507-9427-4ee0-b03e-90ec7681e2d3" os.environ["JJC_SERVICE_SHAREPOINT_ID"] = "7fdd0485-bbf3-4b29-b30f-98c81c2a6284" +os.environ["DATABASE_URL"] = "postgresql://postgres:makingwarmhomes@db:5432/postgres" from etl.surveyPrice.surveyPrice import SurveyPrice @@ -32,7 +33,7 @@ sp.upload_to_sharepoint(sp.get_master_rate_card_path(), "COPY_OF_RATE_CARD_USED. deal_ids = df["HUBSPOT_DEAL_ID"].tolist() # Commented out as i don't want to sync up hubspot_to_db just yet -# sp.move_deals_to_completed(deal_ids) +sp.move_deals_to_completed(deal_ids) """ TODO: diff --git a/etl/surveyPrice/surveyPrice.py b/etl/surveyPrice/surveyPrice.py index f54da9b..db85108 100644 --- a/etl/surveyPrice/surveyPrice.py +++ b/etl/surveyPrice/surveyPrice.py @@ -164,7 +164,7 @@ class SurveyPrice(): scis_pd = self.sharepoint_data_for_installer(SharePointInstaller.SOUTH_COAST_INSULATION) self.all_survey_info_from_sharepoint = pd.concat([jjc_pd, scis_pd], ignore_index=True) return self.all_survey_info_from_sharepoint - + def sharepoint_data_for_installer(self, installer): From 581d9862e7813a5532b78371ed79613ef3367d69 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 28 Apr 2025 09:38:01 +0000 Subject: [PATCH 05/33] makeing db loading possible --- etl/db/load.py | 13 ++++++++++--- etl/hubSpotClient/hubspot.py | 2 ++ etl/hubSpotClient/types.py | 3 ++- etl/hubspot_to_db.py | 5 +++-- etl/surveyPrice/surveyPrice.py | 4 +++- 5 files changed, 20 insertions(+), 7 deletions(-) diff --git a/etl/db/load.py b/etl/db/load.py index ec9145b..e402073 100644 --- a/etl/db/load.py +++ b/etl/db/load.py @@ -1,5 +1,12 @@ +from etl.hubSpotClient.hubspot import HubSpotClient, DealStage +from etl.surveyPrice.surveyPrice import SurveyPrice - -class loadToDb(): +class HubspotTodb(): def __init__(self): - pass \ No newline at end of file + self.hubspot = HubSpotClient() + self.deals_in_hubspot = None + + def get_all_deals(self): + sp = SurveyPrice() + self.deals_in_hubspot = sp.get_all_surveys_from_hubspot() + return self.deals_in_hubspot diff --git a/etl/hubSpotClient/hubspot.py b/etl/hubSpotClient/hubspot.py index 90c551b..eb982fa 100644 --- a/etl/hubSpotClient/hubspot.py +++ b/etl/hubSpotClient/hubspot.py @@ -136,6 +136,7 @@ class HubSpotClient(): "domna_survey_post_sap", "existing_wall_insulation", "installer", + "submission_folder", ], limit=200, after=after, @@ -157,6 +158,7 @@ class HubSpotClient(): existing_wall_insulation=deal.properties.get("existing_wall_insulation") if deal.properties.get("existing_wall_insulation") else "None", no_of_wet_rooms=int(deal.properties["number_of_wet_rooms_needing_ventilation"]), installer=deal.properties["installer"], + submission_folder_path = deal.properties["submission_folder"], )) return all_deals diff --git a/etl/hubSpotClient/types.py b/etl/hubSpotClient/types.py index b4dd30e..7d069df 100644 --- a/etl/hubSpotClient/types.py +++ b/etl/hubSpotClient/types.py @@ -18,4 +18,5 @@ class SubmissionInfoFromDeal(BaseModel): post_sap_score: int existing_wall_insulation: str no_of_wet_rooms: int - installer: str \ No newline at end of file + installer: str + submission_folder_path: str \ No newline at end of file diff --git a/etl/hubspot_to_db.py b/etl/hubspot_to_db.py index 867cc37..626bd0c 100644 --- a/etl/hubspot_to_db.py +++ b/etl/hubspot_to_db.py @@ -1,12 +1,13 @@ import os - os.environ["SHAREPOINT_CLIENT_ID"] = "895e3b77-b1d7-43ec-b18f-dcfe07cdfeaf" os.environ["SHAREPOINT_CLIENT_SECRET"] = "SOf8Q~-is4wdQiqvEEm9FlJQRAY9ELGaj5Qz-a6E" os.environ["SHAREPOINT_TENANT_ID"] = "c3f7519c-2719-4547-af04-6da6cbfd8f8f" os.environ["SOUTH_COAST_INSULATION_SERVICE_SHAREPOINT_ID"] = "b5a51507-9427-4ee0-b03e-90ec7681e2d3" os.environ["JJC_SERVICE_SHAREPOINT_ID"] = "7fdd0485-bbf3-4b29-b30f-98c81c2a6284" -p +from etl.db.load import HubspotTodb +dbLoader = HubspotTodb() +dbLoader.get_all_deals() \ No newline at end of file diff --git a/etl/surveyPrice/surveyPrice.py b/etl/surveyPrice/surveyPrice.py index db85108..4a5ffd2 100644 --- a/etl/surveyPrice/surveyPrice.py +++ b/etl/surveyPrice/surveyPrice.py @@ -14,7 +14,7 @@ class SurveyPrice(): self.master_rate_card_path = None self.all_hubspot_submissions = None self.all_survey_info_from_sharepoint = None - self.download_price_card() + self.required_sheets = [ 'JJC - EMPTIES', @@ -148,6 +148,7 @@ class SurveyPrice(): "HUBSPOT_POST_INSTALL_SAP_SCORE": deal.post_sap_score, "HUBSPOT_INSTALLER": deal.installer, "HUBSPOT_WETROOMS": deal.no_of_wet_rooms, + "HUBSPOT_SHAREPOINT_PATH": deal.submission_folder_path, }) self.all_hubspot_submissions = pd.DataFrame(all_deals) @@ -318,6 +319,7 @@ class SurveyPrice(): return merged_df def calculate_all_price(self): + self.download_price_card() self.get_all_surveys_from_hubspot() self.get_all_surveyed_data_from_sharepoint() submission_data = self.merge_hub_spot_and_survey_information() From 9049704888a5bbd41b863089de1de29d59a48d2d Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 28 Apr 2025 10:21:32 +0000 Subject: [PATCH 06/33] load db to hubspot --- etl/db/load.py | 31 +++++++++++++++++++++++++++++++ etl/hubspot_to_db.py | 2 +- 2 files changed, 32 insertions(+), 1 deletion(-) diff --git a/etl/db/load.py b/etl/db/load.py index e402073..1f4fbd4 100644 --- a/etl/db/load.py +++ b/etl/db/load.py @@ -1,12 +1,43 @@ from etl.hubSpotClient.hubspot import HubSpotClient, DealStage from etl.surveyPrice.surveyPrice import SurveyPrice +from etl.surveyedData.surveryedData import surveyedDataProcessor +from urllib.parse import unquote + class HubspotTodb(): def __init__(self): self.hubspot = HubSpotClient() self.deals_in_hubspot = None + self.data_in_sharepoint = [] def get_all_deals(self): sp = SurveyPrice() self.deals_in_hubspot = sp.get_all_surveys_from_hubspot() return self.deals_in_hubspot + + def get_sharepoint_path(self, url): + url_parts = url.split('/') + # Find the index of 'Forms' + forms_index = url_parts.index('Forms') + # Get the part after 'Forms' + after_forms = url_parts[forms_index + 1] + + # Find 'id=' and extract after it + if 'id=' in after_forms: + id_part = after_forms.split('id=')[1] + # Only keep the path before '&' (to ignore other parameters) + id_path = id_part.split('&')[0] + # Decode the path + decoded_path = unquote(id_path) + # Now, remove the leading '/sites/xxx/Shared Documents/' part + parts = decoded_path.split('Shared Documents') + if len(parts) > 1: + final_path = parts[1].strip('/') + return final_path + else: + return decoded_path.strip('/') + + def gather_data_from_each_sharepoint(self): + self.get_all_deals() + for _, row in self.deals_in_hubspot.iterrows(): + print(self.get_sharepoint_path(row["HUBSPOT_SHAREPOINT_PATH"])) \ No newline at end of file diff --git a/etl/hubspot_to_db.py b/etl/hubspot_to_db.py index 626bd0c..60eff81 100644 --- a/etl/hubspot_to_db.py +++ b/etl/hubspot_to_db.py @@ -10,4 +10,4 @@ from etl.db.load import HubspotTodb dbLoader = HubspotTodb() -dbLoader.get_all_deals() \ No newline at end of file +dbLoader.gather_data_from_each_sharepoint() \ No newline at end of file From 581b86a89ee445d3ac58b5273a4b2c6fbeff77cd Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 28 Apr 2025 14:06:47 +0000 Subject: [PATCH 07/33] added pre_ste_note --- etl/db/load.py | 48 ++++++++++++++++++- etl/hubspot_to_db.py | 2 +- etl/pdfReader/sitenotes.py | 2 +- etl/simple_load_example.py | 2 +- etl/tests/test_csr_class.py | 2 +- etl/tests/test_pre_site_note_class.py | 2 +- .../{types.py => preSiteNoteTypes.py} | 0 7 files changed, 52 insertions(+), 6 deletions(-) rename etl/transform/{types.py => preSiteNoteTypes.py} (100%) diff --git a/etl/db/load.py b/etl/db/load.py index 1f4fbd4..6763a04 100644 --- a/etl/db/load.py +++ b/etl/db/load.py @@ -1,6 +1,8 @@ from etl.hubSpotClient.hubspot import HubSpotClient, DealStage from etl.surveyPrice.surveyPrice import SurveyPrice from etl.surveyedData.surveryedData import surveyedDataProcessor +from etl.scraper.scraper import SharePointScraper, SharePointInstaller + from urllib.parse import unquote @@ -36,8 +38,52 @@ class HubspotTodb(): return final_path else: return decoded_path.strip('/') + + def get_sharepoint_scraper(self, installer): + sp = None + if installer.upper() == "J & J CRUMP": + sp = SharePointScraper(SharePointInstaller.JJC) + elif installer.upper() == "SCIS": + sp = SharePointScraper(SharePointInstaller.SOUTH_COAST_INSULATION) + else: + sp = None + + return sp + + def create_files_locally(self, sp, path, address): + address_paths = {} + file_names_to_download = {} + avoid = [".jpg",".mov", ".JPG", ".heic", ".HEIC", ".png", ".PNG", ".jpeg", ".JPEG", ".mov", ".MOV", ".mp4", ".MP4"] + + + microsoft_graph_data = sp.get_folders_in_path(path) + for file in microsoft_graph_data['value']: + if 'file' in file: + if any(file["name"].endswith(ext) for ext in avoid): + continue + file_names_to_download.update({file["name"]: file['@microsoft.graph.downloadUrl']}) + + each_file = [] + for file_name, url in file_names_to_download.items(): + content = sp.get_file_content(url) + file_path = sp.create_temp_file(content, f"{address}/{file_name}") + each_file.append(file_path) + address_paths.update({address: each_file}) + return address_paths def gather_data_from_each_sharepoint(self): self.get_all_deals() for _, row in self.deals_in_hubspot.iterrows(): - print(self.get_sharepoint_path(row["HUBSPOT_SHAREPOINT_PATH"])) \ No newline at end of file + sp = self.get_sharepoint_scraper(row["HUBSPOT_INSTALLER"]) + path = self.get_sharepoint_path(row["HUBSPOT_SHAREPOINT_PATH"]) + data_loc = self.create_files_locally(sp, path, row["HUBSPOT_DEAL_ADDRESS"]) + + for add, file_loc in data_loc.items(): + self.data_in_sharepoint.append(surveyedDataProcessor(add, file_loc)) + + def print_address(self): + self.gather_data_from_each_sharepoint() + for dp in self.data_in_sharepoint: + print(dp.address) + + diff --git a/etl/hubspot_to_db.py b/etl/hubspot_to_db.py index 60eff81..c60be8d 100644 --- a/etl/hubspot_to_db.py +++ b/etl/hubspot_to_db.py @@ -10,4 +10,4 @@ from etl.db.load import HubspotTodb dbLoader = HubspotTodb() -dbLoader.gather_data_from_each_sharepoint() \ No newline at end of file +dbLoader.print_address() \ No newline at end of file diff --git a/etl/pdfReader/sitenotes.py b/etl/pdfReader/sitenotes.py index ce9ed0f..957da4f 100644 --- a/etl/pdfReader/sitenotes.py +++ b/etl/pdfReader/sitenotes.py @@ -1,5 +1,5 @@ from etl.pdfReader.reportType import ReportType -from etl.transform.types import ( +from etl.transform.preSiteNoteTypes import ( CompanyInfo, SurverySummaryInfo, AssessorInfo, PropertyDescription, PropertyDetail, Dimension, Walls, Roofs, Floors, Door, VentilationAndCooling, diff --git a/etl/simple_load_example.py b/etl/simple_load_example.py index b8f8c64..aa36924 100644 --- a/etl/simple_load_example.py +++ b/etl/simple_load_example.py @@ -2,7 +2,7 @@ import os from etl.surveyedData.surveryedData import surveyedDataProcessor from etl.db.db import get_db_session, init_db -from etl.transform.types import AssessorInfo +from etl.transform.preSiteNoteTypes import AssessorInfo pre_site_note_path = os.path.join(os.getcwd(), "..", "example_data", "pre_site_note.pdf") diff --git a/etl/tests/test_csr_class.py b/etl/tests/test_csr_class.py index 12ddf1c..4ec1c27 100644 --- a/etl/tests/test_csr_class.py +++ b/etl/tests/test_csr_class.py @@ -2,7 +2,7 @@ import os from etl.surveyedData.surveryedData import surveyedDataProcessor from etl.db.db import get_db_session, init_db -from etl.transform.types import AssessorInfo +from etl.transform.preSiteNoteTypes import AssessorInfo import pytest from etl.jjc_old_lewis_manual_way_ import work_out_total_floor_area diff --git a/etl/tests/test_pre_site_note_class.py b/etl/tests/test_pre_site_note_class.py index ec89a49..21fccef 100644 --- a/etl/tests/test_pre_site_note_class.py +++ b/etl/tests/test_pre_site_note_class.py @@ -2,7 +2,7 @@ import os from etl.surveyedData.surveryedData import surveyedDataProcessor from etl.db.db import get_db_session, init_db -from etl.transform.types import AssessorInfo +from etl.transform.preSiteNoteTypes import AssessorInfo import pytest from etl.jjc_old_lewis_manual_way_ import work_out_total_floor_area diff --git a/etl/transform/types.py b/etl/transform/preSiteNoteTypes.py similarity index 100% rename from etl/transform/types.py rename to etl/transform/preSiteNoteTypes.py From c4299c02f1c567b6a771214651ac48055f51bd45 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 28 Apr 2025 14:08:36 +0000 Subject: [PATCH 08/33] hubspotload --- etl/db/{load.py => hubSpotLoad.py} | 0 etl/hubspot_to_db.py | 2 +- 2 files changed, 1 insertion(+), 1 deletion(-) rename etl/db/{load.py => hubSpotLoad.py} (100%) diff --git a/etl/db/load.py b/etl/db/hubSpotLoad.py similarity index 100% rename from etl/db/load.py rename to etl/db/hubSpotLoad.py diff --git a/etl/hubspot_to_db.py b/etl/hubspot_to_db.py index c60be8d..11ea79b 100644 --- a/etl/hubspot_to_db.py +++ b/etl/hubspot_to_db.py @@ -6,7 +6,7 @@ os.environ["SHAREPOINT_TENANT_ID"] = "c3f7519c-2719-4547-af04-6da6cbfd8f8f" os.environ["SOUTH_COAST_INSULATION_SERVICE_SHAREPOINT_ID"] = "b5a51507-9427-4ee0-b03e-90ec7681e2d3" os.environ["JJC_SERVICE_SHAREPOINT_ID"] = "7fdd0485-bbf3-4b29-b30f-98c81c2a6284" -from etl.db.load import HubspotTodb +from etl.db.hubSpotLoad import HubspotTodb dbLoader = HubspotTodb() From 3ca373175c86d052af7f27fe91b34da6c191535f Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 28 Apr 2025 15:36:00 +0000 Subject: [PATCH 09/33] datbase work sve so far --- etl/db/hubSpotLoad.py | 16 ++++++++++++---- etl/hubspot_to_db.py | 2 +- etl/surveyedData/surveryedData.py | 19 +++++++++++++++++++ 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/etl/db/hubSpotLoad.py b/etl/db/hubSpotLoad.py index 6763a04..a2b19cb 100644 --- a/etl/db/hubSpotLoad.py +++ b/etl/db/hubSpotLoad.py @@ -2,12 +2,14 @@ from etl.hubSpotClient.hubspot import HubSpotClient, DealStage from etl.surveyPrice.surveyPrice import SurveyPrice from etl.surveyedData.surveryedData import surveyedDataProcessor from etl.scraper.scraper import SharePointScraper, SharePointInstaller +from etl.db.db import get_db_session, init_db from urllib.parse import unquote class HubspotTodb(): def __init__(self): + init_db() self.hubspot = HubSpotClient() self.deals_in_hubspot = None self.data_in_sharepoint = [] @@ -80,10 +82,16 @@ class HubspotTodb(): for add, file_loc in data_loc.items(): self.data_in_sharepoint.append(surveyedDataProcessor(add, file_loc)) - - def print_address(self): + + def load_all(self): self.gather_data_from_each_sharepoint() - for dp in self.data_in_sharepoint: - print(dp.address) + with get_db_session() as session: + self.load_pre_site_note(session) + session.commit() + + def load_pre_site_note(self, db_session): + for surveyedData in self.data_in_sharepoint: + surveyedData.load_assessor_table(db_session) + diff --git a/etl/hubspot_to_db.py b/etl/hubspot_to_db.py index 11ea79b..f5b14ea 100644 --- a/etl/hubspot_to_db.py +++ b/etl/hubspot_to_db.py @@ -10,4 +10,4 @@ from etl.db.hubSpotLoad import HubspotTodb dbLoader = HubspotTodb() -dbLoader.print_address() \ No newline at end of file +dbLoader.load_all() \ No newline at end of file diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 1b5248e..ec1acb6 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -1,6 +1,7 @@ from etl.pdfReader.pdfReaderToText import pdfReaderToText from etl.pdfReader.reportType import ReportType import math +from etl.transform.preSiteNoteTypes import AssessorInfo class surveyedDataProcessor(): def __init__(self, address, files): @@ -22,6 +23,24 @@ class surveyedDataProcessor(): elif pdf.type == ReportType.CHARTED_SURVEYOR_REPORT: self.csr = pdf.get_reader() + def load_assessor_table(self, db_session): + assessor_data = self.pre_site_note.assessor_information.__dict__ + accreditation_number = assessor_data.get('accreditation_number') + + existing_assessor = db_session.query(AssessorInfo).filter_by( + accreditation_number=accreditation_number + ).first() + + if existing_assessor: + # Update existing record + for key, value in assessor_data.items(): + setattr(existing_assessor, key, value) + else: + # Insert new record + db_session.add(AssessorInfo(**assessor_data)) + + db_session.commit() + def get_insulation_info(self): if self.csr: if self.csr.insulation_info: From 7e4b83f646d851a6b79494f603d3772536088e11 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Fri, 2 May 2025 10:04:54 +0000 Subject: [PATCH 10/33] update sharepoint automation script --- etl/osmosis_data/asset_list.xlsx | Bin 22573 -> 11718 bytes ...osmosis_monday_to_sharepoint_automation.py | 41 ++++++++++-------- etl/scraper/scraper.py | 3 +- 3 files changed, 24 insertions(+), 20 deletions(-) diff --git a/etl/osmosis_data/asset_list.xlsx b/etl/osmosis_data/asset_list.xlsx index 965de921bacdb372bf04ccc09def9b1b72fc1851..d891cb51fa1b88279d37a20c0d2be33a60a3be4f 100644 GIT binary patch delta 7328 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 diff --git a/etl/osmosis_monday_to_sharepoint_automation.py b/etl/osmosis_monday_to_sharepoint_automation.py index 58c094d..313bccf 100644 --- a/etl/osmosis_monday_to_sharepoint_automation.py +++ b/etl/osmosis_monday_to_sharepoint_automation.py @@ -7,46 +7,49 @@ os.environ["SHAREPOINT_TENANT_ID"] = "10d5af8b-2cfd-4882-9ccd-b96e4812dacf" from etl.scraper.scraper import SharePointInstaller from etl.scraper.scraper import SharePointScraper import pandas as pd +from tqdm import tqdm -osmosis = SharePointScraper(SharePointInstaller.OSMOSIS) -parent_folder = "Automated Example" -osmosis.create_dir(parent_folder, "/JTK Test Folder") +osmosis = SharePointScraper(SharePointInstaller.OSMOSIS_WAVE_2) -asset_list = pd.read_excel("osmosis_data/asset_list.xlsx", sheet_name="2502 accent housing") + +parent_folder = "/Osmosis ACD/Osmosis ACD Projects/Stonewater/Stonewater Property ID Folders/12. Decent Homes" + +asset_list = pd.read_excel("osmosis_data/asset_list.xlsx", sheet_name="Sheet1") new_asset_list = [] -parent_folder = "JTK Test Folder/Automated Example" # Create asset list and location -for index, address in asset_list.iterrows(): - webUrl = osmosis.create_dir(address['Name'], parent_folder) +for index, address in tqdm(asset_list.iterrows()): + folder_name = address['Name'] + " " + address['Postcode'] + webUrl = osmosis.create_dir(folder_name, parent_folder) first_folder = "1. Retrofit Assessment" - osmosis.create_dir(first_folder, parent_folder + f"/{address['Name']}") - osmosis.create_dir("A. Assessment", parent_folder + f"/{address['Name']}/{first_folder}") - osmosis.create_dir("B. Air Tightness Tests", parent_folder + f"/{address['Name']}/{first_folder}") + osmosis.create_dir(first_folder, parent_folder + f"/{folder_name}") + osmosis.create_dir("A. Assessment", parent_folder + f"/{folder_name}/{first_folder}") + osmosis.create_dir("B. Air Tightness Tests", parent_folder + f"/{folder_name}/{first_folder}") second_folder = "2. RC Mid-Term Plan" - osmosis.create_dir(second_folder, parent_folder + f"/{address['Name']}") - osmosis.create_dir("SAP", parent_folder + f"/{address['Name']}/{second_folder}") + osmosis.create_dir(second_folder, parent_folder + f"/{folder_name}") + osmosis.create_dir("SAP", parent_folder + f"/{folder_name}/{second_folder}") third_folder = "3. Retrofit Design" - osmosis.create_dir(third_folder, parent_folder + f"/{address['Name']}") + osmosis.create_dir(third_folder, parent_folder + f"/{folder_name}") fourth_folder = "4. Post EPC" - osmosis.create_dir(fourth_folder, parent_folder + f"/{address['Name']}") - osmosis.create_dir(f"{address['Name']} - POST EPC Photos", parent_folder + f"/{address['Name']}/{fourth_folder}") + osmosis.create_dir(fourth_folder, parent_folder + f"/{folder_name}") + osmosis.create_dir(f"{address['Name']} - POST EPC Photos", parent_folder + f"/{folder_name}/{fourth_folder}") fifth_folder = "5. Trustmark Lodgement" - osmosis.create_dir(fifth_folder, parent_folder + f"/{address['Name']}") - osmosis.create_dir("1. Works", parent_folder + f"/{address['Name']}/{fifth_folder}") + osmosis.create_dir(fifth_folder, parent_folder + f"/{folder_name}") + osmosis.create_dir("1. Works", parent_folder + f"/{folder_name}/{fifth_folder}") - osmosis.create_dir("2. Required Documents", parent_folder + f"/{address['Name']}/{fifth_folder}") - osmosis.create_dir("3. Additional Documents", parent_folder + f"/{address['Name']}/{fifth_folder}") + osmosis.create_dir("2. Required Documents", parent_folder + f"/{folder_name}/{fifth_folder}") + osmosis.create_dir("3. Additional Documents", parent_folder + f"/{folder_name}/{fifth_folder}") asset_data = { "Name": address['Name'], + "Postcode": address['Postcode'], "Sharepoint": webUrl, } diff --git a/etl/scraper/scraper.py b/etl/scraper/scraper.py index a379894..ffe1075 100644 --- a/etl/scraper/scraper.py +++ b/etl/scraper/scraper.py @@ -26,7 +26,8 @@ class SharePointInstaller(Enum): SGEC = os.getenv("SGEC_SERVICE_SHAREPOINT_ID", None) BAXTER_KELLY = os.getenv("BAXTER_KELLY_SERVICE_SHAREPOINT_ID", "6f930bf3-572d-4f91-b1ae-ec536fa319e2") DOMNA = os.getenv("DOMNA_SHAREPOINT_ID", "8ab64924-ccde-4b56-b0dc-4e11596446e4") - OSMOSIS = os.getenv("OSMOSIS_SHAREPOINT_ID", "350a3b48-8311-4506-8abb-69bafc280d6f") + OSMOSIS_WAVE_3 = os.getenv("OSMOSIS_SHAREPOINT_ID", "350a3b48-8311-4506-8abb-69bafc280d6f") + OSMOSIS_WAVE_2 = os.getenv("OSMOSIS_SHAREPOINT_ID", "bc925a9a-ad0b-4de9-9a3c-e61014cc7489") WARMFRONT = os.getenv("WARMFRONT_SHARPOINT_ID", "bea71c30-d366-454c-a484-ae4d6fd95bc4") class SharePointScraper(): From cfc3fe595fa9f80627c738bb6901160723836060 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Fri, 2 May 2025 10:57:46 +0000 Subject: [PATCH 11/33] code added for company info --- etl/surveyedData/surveryedData.py | 22 +++++++++++++++++++++- etl/transform/preSiteNoteTypes.py | 10 ++++++++-- 2 files changed, 29 insertions(+), 3 deletions(-) diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index ec1acb6..3bfb418 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -1,7 +1,7 @@ from etl.pdfReader.pdfReaderToText import pdfReaderToText from etl.pdfReader.reportType import ReportType import math -from etl.transform.preSiteNoteTypes import AssessorInfo +from etl.transform.preSiteNoteTypes import AssessorInfo, CompanyInfo class surveyedDataProcessor(): def __init__(self, address, files): @@ -23,8 +23,28 @@ class surveyedDataProcessor(): elif pdf.type == ReportType.CHARTED_SURVEYOR_REPORT: self.csr = pdf.get_reader() + def load_company_table(self, db_session): + company_data = self.pre_site_note.company_information.__dict__ + + company_name = company_data.get('trading_name') + + existing_company = db_session.query(CompanyInfo).filter_by( + trading_name=company_name + ).first() + + if existing_company: + return existing_company + else: + new_company = CompanyInfo(**company_data) + db_session.add(new_company) + db_session.commit() + return new_company + def load_assessor_table(self, db_session): + company = self.load_company_table(db_session) assessor_data = self.pre_site_note.assessor_information.__dict__ + assessor_data['company_id'] = company.id + accreditation_number = assessor_data.get('accreditation_number') existing_assessor = db_session.query(AssessorInfo).filter_by( diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index 7e25682..278e153 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -9,7 +9,7 @@ from sqlalchemy.dialects.postgresql import UUID class BaseModel(SQLModel): id: uuid.UUID = Field( default_factory=uuid.uuid4, - sa_column=Column(UUID(as_uuid=True), primary_key=True) + primary_key=True, ) class Dimension(BaseModel): @@ -18,11 +18,13 @@ class Dimension(BaseModel): loss_perimeter_m: float party_wall_length_m: float -class CompanyInfo(BaseModel): +class CompanyInfo(BaseModel, table=True): trading_name: str post_code: str fax_number: Optional[str] = None related_party_disclosure: Optional[str] = None + + __table_args__ = {"extend_existing": True} @field_validator('related_party_disclosure') def set_none_if_none_of_the_above(cls, v): @@ -84,6 +86,10 @@ class AssessorInfo(BaseModel, table=True): name: str phone_number: Optional[str] = None email_address: Optional[EmailStr] = None + company_id: uuid.UUID = Field( + foreign_key="companyinfo.id", # Referencing CompanyInfo + nullable=False + ) class VentilationAndCooling(BaseModel): no_of_open_fireplaces: int From f6d8710eb3c1d23b450438ac3c866e78b751e6b2 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Fri, 2 May 2025 11:07:07 +0000 Subject: [PATCH 12/33] assessor information is in --- etl/pdfReader/sitenotes.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/etl/pdfReader/sitenotes.py b/etl/pdfReader/sitenotes.py index 957da4f..8f33bc9 100644 --- a/etl/pdfReader/sitenotes.py +++ b/etl/pdfReader/sitenotes.py @@ -59,8 +59,6 @@ class CSR(SiteNotesExtractor): type=dict_.get('detailed_description_of_existing_cavity_wall_insulation_', "") ) if dict_ is not None else None - - From 5b55e4e07b646cef1dcb2c80b3259fb38de6a581 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 5 May 2025 17:30:58 +0000 Subject: [PATCH 13/33] testing ssh --- etl/age_band_calculator.py | 1 - 1 file changed, 1 deletion(-) diff --git a/etl/age_band_calculator.py b/etl/age_band_calculator.py index 61eb207..13b24d1 100644 --- a/etl/age_band_calculator.py +++ b/etl/age_band_calculator.py @@ -7,7 +7,6 @@ os.environ["JJC_SERVICE_SHAREPOINT_ID"] = "7fdd0485-bbf3-4b29-b30f-98c81c2a6284" from etl.scraper.scraper import SharePointScraper, SharePointInstaller, WEEK_COMMENCING import pandas as pd from etl.surveyedData.surveryedData import surveyedDataProcessor - import etl.scraper.scraper as scraper_module def return_pandas_from_scraping(week_commencing, installer): From 94b3db48fb7b0611a30c36e68fe955b9eb7684dd Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 5 May 2025 17:32:42 +0000 Subject: [PATCH 14/33] file commited --- etl/age_band_calculator.py | 1 + 1 file changed, 1 insertion(+) diff --git a/etl/age_band_calculator.py b/etl/age_band_calculator.py index 13b24d1..fd0c8c5 100644 --- a/etl/age_band_calculator.py +++ b/etl/age_band_calculator.py @@ -9,6 +9,7 @@ import pandas as pd from etl.surveyedData.surveryedData import surveyedDataProcessor import etl.scraper.scraper as scraper_module + def return_pandas_from_scraping(week_commencing, installer): scraper_module.WEEK_COMMENCING = week_commencing sp = SharePointScraper(installer) From 46f380bf033c3183dc551f53063bf6d2f8b8b72e Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Tue, 6 May 2025 11:05:29 +0000 Subject: [PATCH 15/33] pre site note --- etl/db/hubSpotLoad.py | 8 +-- etl/pdfReader/sitenotes.py | 4 +- etl/surveyedData/surveryedData.py | 93 ++++++++++++++++++++++--------- etl/transform/preSiteNoteTypes.py | 23 ++++---- 4 files changed, 85 insertions(+), 43 deletions(-) diff --git a/etl/db/hubSpotLoad.py b/etl/db/hubSpotLoad.py index a2b19cb..e6772ed 100644 --- a/etl/db/hubSpotLoad.py +++ b/etl/db/hubSpotLoad.py @@ -91,7 +91,7 @@ class HubspotTodb(): def load_pre_site_note(self, db_session): for surveyedData in self.data_in_sharepoint: - surveyedData.load_assessor_table(db_session) - - - + # Loads Assessor information and Company information to db + assessor = surveyedData.load_assessor_table(db_session) + # Loads the pre site summary information + summary_info = surveyedData.load_pre_site_notes_summary_table(db_session) \ No newline at end of file diff --git a/etl/pdfReader/sitenotes.py b/etl/pdfReader/sitenotes.py index 8f33bc9..43b17e8 100644 --- a/etl/pdfReader/sitenotes.py +++ b/etl/pdfReader/sitenotes.py @@ -1,6 +1,6 @@ from etl.pdfReader.reportType import ReportType from etl.transform.preSiteNoteTypes import ( - CompanyInfo, SurverySummaryInfo, AssessorInfo, + CompanyInfo, PreSiteNotesSummaryInfo, AssessorInfo, PropertyDescription, PropertyDetail, Dimension, Walls, Roofs, Floors, Door, VentilationAndCooling, Lighting, WaterHeating, HotWaterCylinder, SolarWaterHeating, @@ -122,7 +122,7 @@ class QuidosSiteNotesExtractor(SiteNotesExtractor): else: including_9_92_emission_factor = None - self.survey_information = SurverySummaryInfo( + self.survey_information = PreSiteNotesSummaryInfo( reference_number = get_value('Reference Number'), epc_language = get_value('EPC Language'), uprn = get_value('UPRN'), diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 3bfb418..3837773 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -1,7 +1,7 @@ from etl.pdfReader.pdfReaderToText import pdfReaderToText from etl.pdfReader.reportType import ReportType import math -from etl.transform.preSiteNoteTypes import AssessorInfo, CompanyInfo +from etl.transform.preSiteNoteTypes import AssessorInfo, CompanyInfo, PreSiteNotesSummaryInfo class surveyedDataProcessor(): def __init__(self, address, files): @@ -23,43 +23,82 @@ class surveyedDataProcessor(): elif pdf.type == ReportType.CHARTED_SURVEYOR_REPORT: self.csr = pdf.get_reader() + def load_pre_site_notes_summary_table(self, db_session): + summary_data = self.pre_site_note.survey_information.__dict__ + return self.upsert_record( + db_session=db_session, + model_class=PreSiteNotesSummaryInfo, + data_dict=summary_data, + lookup_field="reference_number" + ) + def load_company_table(self, db_session): company_data = self.pre_site_note.company_information.__dict__ + return self.upsert_record( + db_session=db_session, + model_class=CompanyInfo, + data_dict=company_data, + lookup_field="trading_name" + ) + + def upsert_record( + self, + db_session, + model_class, + data_dict, + lookup_field: str, + update_if_exists: bool = False, + additional_fields: dict = None + ): + """ + Upserts a SQLAlchemy model instance based on a lookup field. - company_name = company_data.get('trading_name') + Args: + db_session: SQLAlchemy session. + model_class: SQLAlchemy model/table class. + data_dict: Dictionary of data for creating the model. + lookup_field: Unique identifier field name (str). + update_if_exists: Whether to update existing record or not. + additional_fields: Extra fields to inject (e.g., foreign keys). - existing_company = db_session.query(CompanyInfo).filter_by( - trading_name=company_name + Returns: + SQLAlchemy model instance. + """ + lookup_value = data_dict.get(lookup_field) + if not lookup_value: + raise ValueError(f"Missing lookup field '{lookup_field}' in data.") + + # Merge in additional fields if provided + if additional_fields: + data_dict.update(additional_fields) + + existing_record = db_session.query(model_class).filter( + getattr(model_class, lookup_field) == lookup_value ).first() - if existing_company: - return existing_company + if existing_record: + if update_if_exists: + for key, value in data_dict.items(): + setattr(existing_record, key, value) + db_session.commit() + return existing_record else: - new_company = CompanyInfo(**company_data) - db_session.add(new_company) + new_record = model_class(**data_dict) + db_session.add(new_record) db_session.commit() - return new_company - + return new_record + def load_assessor_table(self, db_session): company = self.load_company_table(db_session) assessor_data = self.pre_site_note.assessor_information.__dict__ - assessor_data['company_id'] = company.id - - accreditation_number = assessor_data.get('accreditation_number') - - existing_assessor = db_session.query(AssessorInfo).filter_by( - accreditation_number=accreditation_number - ).first() - - if existing_assessor: - # Update existing record - for key, value in assessor_data.items(): - setattr(existing_assessor, key, value) - else: - # Insert new record - db_session.add(AssessorInfo(**assessor_data)) - - db_session.commit() + return self.upsert_record( + db_session=db_session, + model_class=AssessorInfo, + data_dict=assessor_data, + lookup_field="accreditation_number", + update_if_exists=True, + additional_fields={"company_id": company.id} + ) def get_insulation_info(self): if self.csr: diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index 278e153..2ba96ca 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -1,4 +1,4 @@ -from sqlmodel import Field, SQLModel +from sqlmodel import Field, SQLModel, Relationship import uuid from datetime import datetime from pydantic import field_validator, EmailStr @@ -32,7 +32,7 @@ class CompanyInfo(BaseModel, table=True): return None return v -class SurverySummaryInfo(BaseModel): +class PreSiteNotesSummaryInfo(BaseModel, table=True): reference_number: str epc_language: str uprn: Optional[str] = "" @@ -51,7 +51,7 @@ class SurverySummaryInfo(BaseModel): current_annual_emissions: str current_annual_emission_including_0925_multiplayer: str current_annual_energy_costs: str - + class Walls(BaseModel): construction: str insulation: str @@ -209,11 +209,14 @@ class PropertyDescription(BaseModel): mainHeating2: Optional[Heating] secondaryHeatingType: Optional[HeatingType] -# class PropertyReport(): - # TODO: Property description - # TODO: Due consideration foregin key - # TODO: Which company did it (Osmosis, Warmfront etc) - # TODO: Links to more foreign keys per report etc - class Insulation(BaseModel): - type: str \ No newline at end of file + type: str + +# One class to rule them all +# class PreSiteNote(BaseModel, table=True): +# summary_info_id: uuid.UUID = Field( +# foreign_key="presitenotessummaryinfo.id", +# nullable=False +# ) +# # Relationship to summary info +# summary_info: Optional["PreSiteNotesSummaryInfo"] = Relationship(back_populates="pre_site_notes") \ No newline at end of file From bf54d759f237e2f2fc1f1bb1d82e3b5fd16bf3e4 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Tue, 6 May 2025 16:09:47 +0000 Subject: [PATCH 16/33] pre site table is now made --- etl/db/hubSpotLoad.py | 4 ++- etl/surveyedData/surveryedData.py | 46 ++++++++++++++++++++++++------- etl/transform/preSiteNoteTypes.py | 33 +++++++++++++++++----- 3 files changed, 65 insertions(+), 18 deletions(-) diff --git a/etl/db/hubSpotLoad.py b/etl/db/hubSpotLoad.py index e6772ed..0556fec 100644 --- a/etl/db/hubSpotLoad.py +++ b/etl/db/hubSpotLoad.py @@ -94,4 +94,6 @@ class HubspotTodb(): # Loads Assessor information and Company information to db assessor = surveyedData.load_assessor_table(db_session) # Loads the pre site summary information - summary_info = surveyedData.load_pre_site_notes_summary_table(db_session) \ No newline at end of file + summary_info = surveyedData.load_pre_site_notes_summary_table(db_session) + # Creates the a final pre site note table that links all information + surveyedData.create_pre_site_note_table(db_session, assessor, summary_info) diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 3837773..f0e5995 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -1,7 +1,10 @@ from etl.pdfReader.pdfReaderToText import pdfReaderToText from etl.pdfReader.reportType import ReportType import math -from etl.transform.preSiteNoteTypes import AssessorInfo, CompanyInfo, PreSiteNotesSummaryInfo +from etl.transform.preSiteNoteTypes import ( + AssessorInfo, CompanyInfo, + PreSiteNotesSummaryInfo, PreSiteNote, +) class surveyedDataProcessor(): def __init__(self, address, files): @@ -15,7 +18,6 @@ class surveyedDataProcessor(): def identify_files(self): for file in self.files: pdf = pdfReaderToText(file) - print(file) if pdf: if pdf.type == ReportType.QUIDOS_PRESITE_NOTE: self.pre_site_note = pdf.get_reader() @@ -40,6 +42,24 @@ class surveyedDataProcessor(): data_dict=company_data, lookup_field="trading_name" ) + + + def create_pre_site_note_table( + self, + db_session, + assessor, + summary_info + ): + preSiteNote = PreSiteNote( + summary_info_id=summary_info.id, + assessor_id=assessor.id, + ) + db_session.add(preSiteNote) + db_session.commit() + + return preSiteNote + + def upsert_record( self, @@ -64,39 +84,45 @@ class surveyedDataProcessor(): Returns: SQLAlchemy model instance. """ - lookup_value = data_dict.get(lookup_field) + # Filter out private/internal fields like _sa_instance_state + clean_data = { + k: v for k, v in data_dict.items() + if not k.startswith('_') and hasattr(model_class, k) + } + + # Merge additional fields if provided + if additional_fields: + clean_data.update(additional_fields) + + lookup_value = clean_data.get(lookup_field) if not lookup_value: raise ValueError(f"Missing lookup field '{lookup_field}' in data.") - # Merge in additional fields if provided - if additional_fields: - data_dict.update(additional_fields) - existing_record = db_session.query(model_class).filter( getattr(model_class, lookup_field) == lookup_value ).first() if existing_record: if update_if_exists: - for key, value in data_dict.items(): + for key, value in clean_data.items(): setattr(existing_record, key, value) db_session.commit() return existing_record else: - new_record = model_class(**data_dict) + new_record = model_class(**clean_data) db_session.add(new_record) db_session.commit() return new_record def load_assessor_table(self, db_session): company = self.load_company_table(db_session) + print(f"Company ID: {company.id}") assessor_data = self.pre_site_note.assessor_information.__dict__ return self.upsert_record( db_session=db_session, model_class=AssessorInfo, data_dict=assessor_data, lookup_field="accreditation_number", - update_if_exists=True, additional_fields={"company_id": company.id} ) diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index 2ba96ca..f90ac57 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -4,6 +4,7 @@ from datetime import datetime from pydantic import field_validator, EmailStr from typing import Optional, List from sqlalchemy import Column +from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import UUID class BaseModel(SQLModel): @@ -52,6 +53,12 @@ class PreSiteNotesSummaryInfo(BaseModel, table=True): current_annual_emission_including_0925_multiplayer: str current_annual_energy_costs: str + pre_site_note: Optional["PreSiteNote"] = Relationship( + back_populates="summary_info", + sa_relationship=relationship("PreSiteNote", back_populates="summary_info", uselist=False) + ) + + class Walls(BaseModel): construction: str insulation: str @@ -90,6 +97,8 @@ class AssessorInfo(BaseModel, table=True): foreign_key="companyinfo.id", # Referencing CompanyInfo nullable=False ) + + pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="assessor") class VentilationAndCooling(BaseModel): no_of_open_fireplaces: int @@ -213,10 +222,20 @@ class Insulation(BaseModel): type: str # One class to rule them all -# class PreSiteNote(BaseModel, table=True): -# summary_info_id: uuid.UUID = Field( -# foreign_key="presitenotessummaryinfo.id", -# nullable=False -# ) -# # Relationship to summary info -# summary_info: Optional["PreSiteNotesSummaryInfo"] = Relationship(back_populates="pre_site_notes") \ No newline at end of file +class PreSiteNote(BaseModel, table=True): + # Summary Info + summary_info_id: uuid.UUID = Field( + foreign_key="presitenotessummaryinfo.id", + nullable=False + ) + + summary_info: Optional["PreSiteNotesSummaryInfo"] = Relationship(back_populates="pre_site_note") + + + # Assessor Info + assessor_id: uuid.UUID = Field( + foreign_key="assessorinfo.id", + nullable=False + ) + + assessor: Optional["AssessorInfo"] = Relationship(back_populates="pre_site_notes") From 13503a7495e977518a4c54531eefceed0da64198 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Wed, 7 May 2025 15:02:10 +0000 Subject: [PATCH 17/33] back tow workign --- etl/db/hubSpotLoad.py | 3 ++- etl/surveyedData/surveryedData.py | 10 +++++++++- etl/transform/preSiteNoteTypes.py | 2 +- 3 files changed, 12 insertions(+), 3 deletions(-) diff --git a/etl/db/hubSpotLoad.py b/etl/db/hubSpotLoad.py index 0556fec..ac01be8 100644 --- a/etl/db/hubSpotLoad.py +++ b/etl/db/hubSpotLoad.py @@ -95,5 +95,6 @@ class HubspotTodb(): assessor = surveyedData.load_assessor_table(db_session) # Loads the pre site summary information summary_info = surveyedData.load_pre_site_notes_summary_table(db_session) + # Creates the a final pre site note table that links all information - surveyedData.create_pre_site_note_table(db_session, assessor, summary_info) + presitenote = surveyedData.create_pre_site_note_table(db_session, assessor, summary_info) diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index f0e5995..5597c22 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -4,7 +4,9 @@ import math from etl.transform.preSiteNoteTypes import ( AssessorInfo, CompanyInfo, PreSiteNotesSummaryInfo, PreSiteNote, + PropertyDescription, Dimension, ) +import uuid class surveyedDataProcessor(): def __init__(self, address, files): @@ -33,7 +35,14 @@ class surveyedDataProcessor(): data_dict=summary_data, lookup_field="reference_number" ) + + + def load_property_description(self, db_session, presitenote): + propertydescription = self.pre_site_note.property_description.__dict__ + print(propertydescription) + pass + def load_company_table(self, db_session): company_data = self.pre_site_note.company_information.__dict__ return self.upsert_record( @@ -116,7 +125,6 @@ class surveyedDataProcessor(): def load_assessor_table(self, db_session): company = self.load_company_table(db_session) - print(f"Company ID: {company.id}") assessor_data = self.pre_site_note.assessor_information.__dict__ return self.upsert_record( db_session=db_session, diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index f90ac57..c592d98 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -238,4 +238,4 @@ class PreSiteNote(BaseModel, table=True): nullable=False ) - assessor: Optional["AssessorInfo"] = Relationship(back_populates="pre_site_notes") + assessor: Optional["AssessorInfo"] = Relationship(back_populates="pre_site_notes") \ No newline at end of file From cade1e4cc16fd2eb46c0b1e8ee1c5039159fd871 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Wed, 7 May 2025 16:09:45 +0000 Subject: [PATCH 18/33] made more progress on tables --- etl/db/hubSpotLoad.py | 2 + etl/surveyedData/surveryedData.py | 95 ++++++++++++++++++++++--------- etl/transform/preSiteNoteTypes.py | 32 +++++++++-- 3 files changed, 97 insertions(+), 32 deletions(-) diff --git a/etl/db/hubSpotLoad.py b/etl/db/hubSpotLoad.py index ac01be8..a954a8f 100644 --- a/etl/db/hubSpotLoad.py +++ b/etl/db/hubSpotLoad.py @@ -98,3 +98,5 @@ class HubspotTodb(): # Creates the a final pre site note table that links all information presitenote = surveyedData.create_pre_site_note_table(db_session, assessor, summary_info) + + surveyedData.load_property_description(db_session) \ No newline at end of file diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 5597c22..9b56b56 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -4,7 +4,7 @@ import math from etl.transform.preSiteNoteTypes import ( AssessorInfo, CompanyInfo, PreSiteNotesSummaryInfo, PreSiteNote, - PropertyDescription, Dimension, + PropertyDescription, Dimension, HeatingType, Heating, HeatingSystemControls ) import uuid @@ -35,13 +35,55 @@ class surveyedDataProcessor(): data_dict=summary_data, lookup_field="reference_number" ) - - - def load_property_description(self, db_session, presitenote): - propertydescription = self.pre_site_note.property_description.__dict__ - print(propertydescription) - pass + def get_attribute_and_load(self, obj, attr_string, pydanticModel, db_session): + found = getattr(obj, attr_string, None) + if found: + db = self.upsert_record( + db_session=db_session, + model_class=pydanticModel, + data_dict=found.__dict__, + lookup_field=None + ) + return db + return None + + + def load_property_description(self, db_session): + def check_if_attribute_exists(obj, attribute): + a = getattr(obj, attribute, None) + if a: + return True + else: + return False + property_des = self.pre_site_note.property_description.__dict__ + secondary_heating = self.get_attribute_and_load(self.pre_site_note.property_description, "secondaryHeatingType", HeatingType, db_session) + + # main heating 2 and main heating 2 controls + if check_if_attribute_exists(self.pre_site_note.property_description, "mainHeating2"): + if check_if_attribute_exists(self.pre_site_note.property_description.mainHeating2, "controls"): + mainheating2controls = self.get_attribute_and_load(self.pre_site_note.property_description.mainHeating2, "controls", HeatingSystemControls, db_session) + data = self.pre_site_note.property_description.mainHeating2.__dict__ + data.pop("controls") + + mainheating2 = self.upsert_record( + db_session=db_session, + model_class=Heating, + data_dict=data, + lookup_field=None, + additional_fields= {"main_heating_2_id": mainheating2controls.id}, + ) + + else: + mainheating2controls = None + else: + mainheating2 = None + + + + + print(secondary_heating) + def load_company_table(self, db_session): company_data = self.pre_site_note.company_information.__dict__ @@ -51,7 +93,7 @@ class surveyedDataProcessor(): data_dict=company_data, lookup_field="trading_name" ) - + def create_pre_site_note_table( self, @@ -75,7 +117,7 @@ class surveyedDataProcessor(): db_session, model_class, data_dict, - lookup_field: str, + lookup_field, update_if_exists: bool = False, additional_fields: dict = None ): @@ -103,25 +145,26 @@ class surveyedDataProcessor(): if additional_fields: clean_data.update(additional_fields) - lookup_value = clean_data.get(lookup_field) - if not lookup_value: - raise ValueError(f"Missing lookup field '{lookup_field}' in data.") + if lookup_field is not None: + lookup_value = clean_data.get(lookup_field) + if not lookup_value: + raise ValueError(f"Missing lookup field '{lookup_field}' in data.") - existing_record = db_session.query(model_class).filter( - getattr(model_class, lookup_field) == lookup_value - ).first() + existing_record = db_session.query(model_class).filter( + getattr(model_class, lookup_field) == lookup_value + ).first() + + if existing_record: + if update_if_exists: + for key, value in clean_data.items(): + setattr(existing_record, key, value) + db_session.commit() + return existing_record + new_record = model_class(**clean_data) + db_session.add(new_record) + db_session.commit() + return new_record - if existing_record: - if update_if_exists: - for key, value in clean_data.items(): - setattr(existing_record, key, value) - db_session.commit() - return existing_record - else: - new_record = model_class(**clean_data) - db_session.add(new_record) - db_session.commit() - return new_record def load_assessor_table(self, db_session): company = self.load_company_table(db_session) diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index c592d98..a3559ab 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -109,7 +109,7 @@ class Lighting(BaseModel): total_no_of_light_fittings: int total_no_of_lel_fittings: int -class HeatingSystemControls(BaseModel): +class HeatingSystemControls(BaseModel, table=True): control_type: str flue_type: str fan_assisted_flue: bool @@ -117,7 +117,10 @@ class HeatingSystemControls(BaseModel): electricity_meter_type: Optional[str] = "" mains_gas_available: Optional[bool] = False -class Heating(BaseModel): + # Reverse relationship (optional) + heating: Optional["Heating"] = Relationship(back_populates="controls", sa_relationship=relationship("Heating", back_populates="controls", uselist=False)) + +class Heating(BaseModel, table=True): type: str heating_source: str efficiency_source: str @@ -127,9 +130,13 @@ class Heating(BaseModel): model_qualifer: str sap_2009_table: Optional[str] = "" percentage_of_heated_floor_area_served: Optional[str] = "" - controls: HeatingSystemControls -class HeatingType(BaseModel): + # Foregin Key to HeatingSystemControls + controls_id: uuid.UUID = Field(foreign_key="heatingsystemcontrols.id") + + controls: HeatingSystemControls = Relationship(back_populates="heating") + +class HeatingType(BaseModel, table=True): heating_type: str fuel_type: str @@ -215,8 +222,21 @@ class PropertyDescription(BaseModel): windTurbine: Optional[WindTurbine] otherDetails: Optional[OtherDetails] mainHeating: Optional[Heating] - mainHeating2: Optional[Heating] - secondaryHeatingType: Optional[HeatingType] + + # Foreign key to Main Heating 2 + main_heating_2_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="heatingtype.id" + ) + mainHeating2: Optional[Heating] = Relationship() + + # Foreign key to HeatingType + secondary_heating_type_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="heatingtype.id" + ) + + secondaryHeatingType: Optional[HeatingType] = Relationship() class Insulation(BaseModel): type: str From 81f31fd8b91d260ec15608470ec07aaba93f1a38 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Thu, 8 May 2025 06:55:17 +0000 Subject: [PATCH 19/33] migration start again --- alembic.ini | 119 +-------------------- alembic/versions/b650a366b88d_initla_db.py | 37 ------- 2 files changed, 1 insertion(+), 155 deletions(-) delete mode 100644 alembic/versions/b650a366b88d_initla_db.py diff --git a/alembic.ini b/alembic.ini index 6d91a48..e94d496 100644 --- a/alembic.ini +++ b/alembic.ini @@ -1,119 +1,2 @@ -# A generic, single database configuration. - [alembic] -# path to migration scripts -# Use forward slashes (/) also on windows to provide an os agnostic path -script_location = alembic/ - -# template used to generate migration file names; The default value is %%(rev)s_%%(slug)s -# Uncomment the line below if you want the files to be prepended with date and time -# see https://alembic.sqlalchemy.org/en/latest/tutorial.html#editing-the-ini-file -# for all available tokens -# file_template = %%(year)d_%%(month).2d_%%(day).2d_%%(hour).2d%%(minute).2d-%%(rev)s_%%(slug)s - -# sys.path path, will be prepended to sys.path if present. -# defaults to the current working directory. -prepend_sys_path = . - -# timezone to use when rendering the date within the migration file -# as well as the filename. -# If specified, requires the python>=3.9 or backports.zoneinfo library and tzdata library. -# Any required deps can installed by adding `alembic[tz]` to the pip requirements -# string value is passed to ZoneInfo() -# leave blank for localtime -# timezone = - -# max length of characters to apply to the "slug" field -# truncate_slug_length = 40 - -# set to 'true' to run the environment during -# the 'revision' command, regardless of autogenerate -# revision_environment = false - -# set to 'true' to allow .pyc and .pyo files without -# a source .py file to be detected as revisions in the -# versions/ directory -# sourceless = false - -# version location specification; This defaults -# to alembic//versions. When using multiple version -# directories, initial revisions must be specified with --version-path. -# The path separator used here should be the separator specified by "version_path_separator" below. -# version_locations = %(here)s/bar:%(here)s/bat:alembic//versions - -# version path separator; As mentioned above, this is the character used to split -# version_locations. The default within new alembic.ini files is "os", which uses os.pathsep. -# If this key is omitted entirely, it falls back to the legacy behavior of splitting on spaces and/or commas. -# Valid values for version_path_separator are: -# -# version_path_separator = : -# version_path_separator = ; -# version_path_separator = space -# version_path_separator = newline -# -# Use os.pathsep. Default configuration used for new projects. -version_path_separator = os - -# set to 'true' to search source files recursively -# in each "version_locations" directory -# new in Alembic version 1.10 -# recursive_version_locations = false - -# the output encoding used when revision files -# are written from script.py.mako -# output_encoding = utf-8 - -sqlalchemy.url = driver://user:pass@localhost/dbname - - -[post_write_hooks] -# post_write_hooks defines scripts or Python functions that are run -# on newly generated revision scripts. See the documentation for further -# detail and examples - -# format using "black" - use the console_scripts runner, against the "black" entrypoint -# hooks = black -# black.type = console_scripts -# black.entrypoint = black -# black.options = -l 79 REVISION_SCRIPT_FILENAME - -# lint with attempts to fix using "ruff" - use the exec runner, execute a binary -# hooks = ruff -# ruff.type = exec -# ruff.executable = %(here)s/.venv/bin/ruff -# ruff.options = check --fix REVISION_SCRIPT_FILENAME - -# Logging configuration -[loggers] -keys = root,sqlalchemy,alembic - -[handlers] -keys = console - -[formatters] -keys = generic - -[logger_root] -level = WARNING -handlers = console -qualname = - -[logger_sqlalchemy] -level = WARNING -handlers = -qualname = sqlalchemy.engine - -[logger_alembic] -level = INFO -handlers = -qualname = alembic - -[handler_console] -class = StreamHandler -args = (sys.stderr,) -level = NOTSET -formatter = generic - -[formatter_generic] -format = %(levelname)-5.5s [%(name)s] %(message)s -datefmt = %H:%M:%S +script_location = alembic diff --git a/alembic/versions/b650a366b88d_initla_db.py b/alembic/versions/b650a366b88d_initla_db.py deleted file mode 100644 index c5dfa7a..0000000 --- a/alembic/versions/b650a366b88d_initla_db.py +++ /dev/null @@ -1,37 +0,0 @@ -"""initla db - -Revision ID: b650a366b88d -Revises: -Create Date: 2025-03-28 10:36:15.235350 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa - - -# revision identifiers, used by Alembic. -revision: str = 'b650a366b88d' -down_revision: Union[str, None] = None -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - pass - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('assessorinfo', - sa.Column('id', sa.UUID(), autoincrement=False, nullable=False), - sa.Column('accreditation_number', sa.VARCHAR(), autoincrement=False, nullable=False), - sa.Column('name', sa.VARCHAR(), autoincrement=False, nullable=False), - sa.Column('phone_number', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.Column('email_address', sa.VARCHAR(), autoincrement=False, nullable=True), - sa.PrimaryKeyConstraint('id', name='assessorinfo_pkey') - ) - # ### end Alembic commands ### From 6a7742697d3567a75d7fe9a72cc8b98b14f1e65f Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Thu, 8 May 2025 06:55:45 +0000 Subject: [PATCH 20/33] migration script --- alembic/env.py | 64 +++------- .../versions/e351a98aba54_init_some_tables.py | 117 ++++++++++++++++++ migration_db.sh | 4 +- 3 files changed, 135 insertions(+), 50 deletions(-) create mode 100644 alembic/versions/e351a98aba54_init_some_tables.py diff --git a/alembic/env.py b/alembic/env.py index 82bf6e0..7966288 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -1,81 +1,49 @@ from logging.config import fileConfig -from sqlalchemy import engine_from_config +from sqlalchemy import create_engine from sqlalchemy import pool from alembic import context from sqlmodel import SQLModel + +from etl.transform.preSiteNoteTypes import * + import os # this is the Alembic Config object, which provides # access to the values within the .ini file in use. +# Load DB URL from env var db_url = os.getenv("DATABASE_URL") +if not db_url: + raise RuntimeError("Please set DATABASE_URL") if not db_url: raise RuntimeError("Please specify database url via DATABASE_URL in env variable") +import logging +logging.basicConfig(level=logging.INFO) + config = context.config -config.set_main_option("sqlalchemy.url", db_url) - - -# Interpret the config file for Python logging. -# This line sets up loggers basically. -if config.config_file_name is not None: - fileConfig(config.config_file_name) - -# add your model's MetaData object here -# for 'autogenerate' support -# from myapp import mymodel -# target_metadata = mymodel.Base.metadata target_metadata = SQLModel.metadata -# other values from the config, defined by the needs of env.py, -# can be acquired: -# my_important_option = config.get_main_option("my_important_option") -# ... etc. - - def run_migrations_offline() -> None: - """Run migrations in 'offline' mode. - - This configures the context with just a URL - and not an Engine, though an Engine is acceptable - here as well. By skipping the Engine creation - we don't even need a DBAPI to be available. - - Calls to context.execute() here emit the given string to the - script output. - - """ - url = config.get_main_option("sqlalchemy.url") context.configure( - url=url, + url=db_url, target_metadata=target_metadata, literal_binds=True, dialect_opts={"paramstyle": "named"}, ) - with context.begin_transaction(): context.run_migrations() + def run_migrations_online() -> None: - """Run migrations in 'online' mode. - - In this scenario we need to create an Engine - and associate a connection with the context. - - """ - connectable = engine_from_config( - config.get_section(config.config_ini_section, {}), - prefix="sqlalchemy.", - poolclass=pool.NullPool, - ) - - with connectable.connect() as connection: + engine = create_engine(db_url, poolclass=pool.NullPool) + with engine.connect() as connection: context.configure( - connection=connection, target_metadata=target_metadata + connection=connection, + target_metadata=target_metadata, ) - with context.begin_transaction(): context.run_migrations() diff --git a/alembic/versions/e351a98aba54_init_some_tables.py b/alembic/versions/e351a98aba54_init_some_tables.py new file mode 100644 index 0000000..43be0da --- /dev/null +++ b/alembic/versions/e351a98aba54_init_some_tables.py @@ -0,0 +1,117 @@ +"""init some tables + +Revision ID: e351a98aba54 +Revises: +Create Date: 2025-05-08 06:52:41.139547 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel + + +# revision identifiers, used by Alembic. +revision: str = 'e351a98aba54' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('companyinfo', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('trading_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('post_code', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('fax_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('related_party_disclosure', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('heatingsystemcontrols', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('control_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('flue_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('fan_assisted_flue', sa.Boolean(), nullable=False), + sa.Column('heat_emitter_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('electricity_meter_type', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('mains_gas_available', sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('heatingtype', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('heating_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('fuel_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('presitenotessummaryinfo', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('reference_number', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('epc_language', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('uprn', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('postcode', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('region', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('address', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('town', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('county', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('property_tenure', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('transaction_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('inspection_date', sa.DateTime(), nullable=False), + sa.Column('current_sap', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('potential_sap', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('current_ei', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('potential_ei', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('current_annual_emissions', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('current_annual_emission_including_0925_multiplayer', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('current_annual_energy_costs', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('assessorinfo', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('accreditation_number', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('phone_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('email_address', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('company_id', sa.Uuid(), nullable=False), + sa.ForeignKeyConstraint(['company_id'], ['companyinfo.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('heating', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('heating_source', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('efficiency_source', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('heating_fuel', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('brand_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('model_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('model_qualifer', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('sap_2009_table', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('percentage_of_heated_floor_area_served', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('controls_id', sa.Uuid(), nullable=False), + sa.ForeignKeyConstraint(['controls_id'], ['heatingsystemcontrols.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('presitenote', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('summary_info_id', sa.Uuid(), nullable=False), + sa.Column('assessor_id', sa.Uuid(), nullable=False), + sa.ForeignKeyConstraint(['assessor_id'], ['assessorinfo.id'], ), + sa.ForeignKeyConstraint(['summary_info_id'], ['presitenotessummaryinfo.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('presitenote') + op.drop_table('heating') + op.drop_table('assessorinfo') + op.drop_table('presitenotessummaryinfo') + op.drop_table('heatingtype') + op.drop_table('heatingsystemcontrols') + op.drop_table('companyinfo') + # ### end Alembic commands ### diff --git a/migration_db.sh b/migration_db.sh index b46bc72..d029b60 100644 --- a/migration_db.sh +++ b/migration_db.sh @@ -1,2 +1,2 @@ -poetry run alembic revision --autogenerate -m "some msg" -#poetry upgrade head +#poetry run alembic revision --autogenerate -m "init some tables" +poetry run alembic upgrade head From 261ad10937b94f7e04936f3c92084a0c5c88453a Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Thu, 8 May 2025 11:36:40 +0000 Subject: [PATCH 21/33] do linking later --- etl/simple_load_example.py | 2 +- etl/surveyedData/surveryedData.py | 77 +++++++++++++++++++------------ etl/transform/preSiteNoteTypes.py | 28 +++++++---- 3 files changed, 66 insertions(+), 41 deletions(-) diff --git a/etl/simple_load_example.py b/etl/simple_load_example.py index aa36924..7151f94 100644 --- a/etl/simple_load_example.py +++ b/etl/simple_load_example.py @@ -15,7 +15,7 @@ init_db() assessor0 = AssessorInfo( - **survey_one.pre_site_note.assessor_information.__dict__ + **survey_one.pre_site_note.assessor_information.model_dump() ) with get_db_session() as session: diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 9b56b56..42b78f1 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -4,7 +4,8 @@ import math from etl.transform.preSiteNoteTypes import ( AssessorInfo, CompanyInfo, PreSiteNotesSummaryInfo, PreSiteNote, - PropertyDescription, Dimension, HeatingType, Heating, HeatingSystemControls + PropertyDescription, Dimension, HeatingType, Heating, HeatingSystemControls, + OtherDetails, ) import uuid @@ -28,7 +29,7 @@ class surveyedDataProcessor(): self.csr = pdf.get_reader() def load_pre_site_notes_summary_table(self, db_session): - summary_data = self.pre_site_note.survey_information.__dict__ + summary_data = self.pre_site_note.survey_information.model_dump() return self.upsert_record( db_session=db_session, model_class=PreSiteNotesSummaryInfo, @@ -39,15 +40,17 @@ class surveyedDataProcessor(): def get_attribute_and_load(self, obj, attr_string, pydanticModel, db_session): found = getattr(obj, attr_string, None) if found: - db = self.upsert_record( - db_session=db_session, - model_class=pydanticModel, - data_dict=found.__dict__, - lookup_field=None - ) - return db + print(f"Uploading to data base {found}") + print(f"Uploaded to database with this dict {found.model_dump()}") + if found.model_dump(): + db = self.upsert_record( + db_session=db_session, + model_class=pydanticModel, + data_dict=found.model_dump(), + lookup_field=None + ) + return db return None - def load_property_description(self, db_session): def check_if_attribute_exists(obj, attribute): @@ -56,37 +59,51 @@ class surveyedDataProcessor(): return True else: return False - property_des = self.pre_site_note.property_description.__dict__ + property_des = self.pre_site_note.property_description.model_dump() + + # Seconday Heating secondary_heating = self.get_attribute_and_load(self.pre_site_note.property_description, "secondaryHeatingType", HeatingType, db_session) # main heating 2 and main heating 2 controls + mainheating2 = None + mainheating2controls = None if check_if_attribute_exists(self.pre_site_note.property_description, "mainHeating2"): if check_if_attribute_exists(self.pre_site_note.property_description.mainHeating2, "controls"): mainheating2controls = self.get_attribute_and_load(self.pre_site_note.property_description.mainHeating2, "controls", HeatingSystemControls, db_session) - data = self.pre_site_note.property_description.mainHeating2.__dict__ - data.pop("controls") + data = self.pre_site_note.property_description.mainHeating2.model_dump() + if data: + mainheating2 = self.upsert_record( + db_session=db_session, + model_class=Heating, + data_dict=data, + lookup_field=None, + additional_fields= {"controls_id": mainheating2controls.id}, + ) - mainheating2 = self.upsert_record( - db_session=db_session, - model_class=Heating, - data_dict=data, - lookup_field=None, - additional_fields= {"main_heating_2_id": mainheating2controls.id}, - ) - - else: - mainheating2controls = None - else: - mainheating2 = None + # main heating and main heating control + mainheating = None + mainheatingcontrols = None + if check_if_attribute_exists(self.pre_site_note.property_description, "mainHeating"): + if check_if_attribute_exists(self.pre_site_note.property_description.mainHeating, "controls"): + print(self.pre_site_note.property_description.mainHeating) + mainheatingcontrols = self.get_attribute_and_load(self.pre_site_note.property_description.mainHeating, 'controls', HeatingSystemControls, db_session) + data = self.pre_site_note.property_description.mainHeating.model_dump() + if data: + mainheating = self.upsert_record( + db_session=db_session, + model_class=Heating, + data_dict=data, + lookup_field=None, + additional_fields={"controls_id": mainheatingcontrols.id} + ) + # Other details + otherDetails = self.get_attribute_and_load(self.pre_site_note.property_description, "otherDetails", OtherDetails, db_session) - print(secondary_heating) - - def load_company_table(self, db_session): - company_data = self.pre_site_note.company_information.__dict__ + company_data = self.pre_site_note.company_information.model_dump() return self.upsert_record( db_session=db_session, model_class=CompanyInfo, @@ -168,7 +185,7 @@ class surveyedDataProcessor(): def load_assessor_table(self, db_session): company = self.load_company_table(db_session) - assessor_data = self.pre_site_note.assessor_information.__dict__ + assessor_data = self.pre_site_note.assessor_information.model_dump() return self.upsert_record( db_session=db_session, model_class=AssessorInfo, diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index a3559ab..b2a2e8f 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -93,7 +93,7 @@ class AssessorInfo(BaseModel, table=True): name: str phone_number: Optional[str] = None email_address: Optional[EmailStr] = None - company_id: uuid.UUID = Field( + company_id: Optional[uuid.UUID] = Field( foreign_key="companyinfo.id", # Referencing CompanyInfo nullable=False ) @@ -118,8 +118,9 @@ class HeatingSystemControls(BaseModel, table=True): mains_gas_available: Optional[bool] = False # Reverse relationship (optional) - heating: Optional["Heating"] = Relationship(back_populates="controls", sa_relationship=relationship("Heating", back_populates="controls", uselist=False)) - + heating: Optional["Heating"] = Relationship( + back_populates="controls", + ) class Heating(BaseModel, table=True): type: str heating_source: str @@ -132,9 +133,8 @@ class Heating(BaseModel, table=True): percentage_of_heated_floor_area_served: Optional[str] = "" # Foregin Key to HeatingSystemControls - controls_id: uuid.UUID = Field(foreign_key="heatingsystemcontrols.id") - - controls: HeatingSystemControls = Relationship(back_populates="heating") + controls_id: Optional[uuid.UUID] = Field(foreign_key="heatingsystemcontrols.id") + controls: Optional["HeatingSystemControls"] = Relationship(back_populates="heating") class HeatingType(BaseModel, table=True): heating_type: str @@ -168,7 +168,7 @@ class PhotovoltaicPanel(BaseModel): class WindTurbine(BaseModel): wind_turbine: bool -class OtherDetails(BaseModel): +class OtherDetails(BaseModel, table=True): electricity_meter_type: str main_gas_avalible: bool @@ -220,7 +220,15 @@ class PropertyDescription(BaseModel): flueGasHeatRecoverySystem: Optional[FlueGasHeatRecoverySystem] photovoltaicPanel: Optional[PhotovoltaicPanel] windTurbine: Optional[WindTurbine] - otherDetails: Optional[OtherDetails] + + + # other details + other_details_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="otherdetails.id" + ) + otherDetails: Optional["OtherDetails"] + mainHeating: Optional[Heating] # Foreign key to Main Heating 2 @@ -228,7 +236,7 @@ class PropertyDescription(BaseModel): default=None, foreign_key="heatingtype.id" ) - mainHeating2: Optional[Heating] = Relationship() + mainHeating2: Optional[Heating] # Foreign key to HeatingType secondary_heating_type_id: Optional[uuid.UUID] = Field( @@ -236,7 +244,7 @@ class PropertyDescription(BaseModel): foreign_key="heatingtype.id" ) - secondaryHeatingType: Optional[HeatingType] = Relationship() + secondaryHeatingType: Optional[HeatingType] class Insulation(BaseModel): type: str From 54d11e38dea36f51a76aeb6c1baa2ca09697843e Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Thu, 8 May 2025 11:58:27 +0000 Subject: [PATCH 22/33] get windturbine working --- etl/db/hubSpotLoad.py | 5 +++-- etl/hubspot_to_db.py | 2 +- etl/surveyedData/surveryedData.py | 5 ++++- etl/transform/preSiteNoteTypes.py | 6 ++++++ 4 files changed, 14 insertions(+), 4 deletions(-) diff --git a/etl/db/hubSpotLoad.py b/etl/db/hubSpotLoad.py index a954a8f..ed21b8a 100644 --- a/etl/db/hubSpotLoad.py +++ b/etl/db/hubSpotLoad.py @@ -83,8 +83,9 @@ class HubspotTodb(): for add, file_loc in data_loc.items(): self.data_in_sharepoint.append(surveyedDataProcessor(add, file_loc)) - def load_all(self): - self.gather_data_from_each_sharepoint() + def load_all(self, fast=False): + if fast is False: + self.gather_data_from_each_sharepoint() with get_db_session() as session: self.load_pre_site_note(session) session.commit() diff --git a/etl/hubspot_to_db.py b/etl/hubspot_to_db.py index f5b14ea..8516a17 100644 --- a/etl/hubspot_to_db.py +++ b/etl/hubspot_to_db.py @@ -10,4 +10,4 @@ from etl.db.hubSpotLoad import HubspotTodb dbLoader = HubspotTodb() -dbLoader.load_all() \ No newline at end of file +dbLoader.load_all() diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 42b78f1..b245b1d 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -5,7 +5,7 @@ from etl.transform.preSiteNoteTypes import ( AssessorInfo, CompanyInfo, PreSiteNotesSummaryInfo, PreSiteNote, PropertyDescription, Dimension, HeatingType, Heating, HeatingSystemControls, - OtherDetails, + OtherDetails, WindTurbine ) import uuid @@ -99,6 +99,9 @@ class surveyedDataProcessor(): # Other details otherDetails = self.get_attribute_and_load(self.pre_site_note.property_description, "otherDetails", OtherDetails, db_session) + + # windTurbine + windTurbine = self.get_attribute_and_load(self.pre_site_note.property_description, "windTurbine", WindTurbine, db_session) diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index b2a2e8f..a375ad3 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -219,6 +219,12 @@ class PropertyDescription(BaseModel): showerAndBaths: Optional[ShowerAndBaths] flueGasHeatRecoverySystem: Optional[FlueGasHeatRecoverySystem] photovoltaicPanel: Optional[PhotovoltaicPanel] + + # windTuribe + wind_turbine_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="windturbine.id" + ) windTurbine: Optional[WindTurbine] From 7da8b1079a8417f7568a9fcff841cfba2f5b06fc Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Thu, 8 May 2025 13:56:45 +0000 Subject: [PATCH 23/33] photo electric working --- etl/surveyedData/surveryedData.py | 29 +++++++++++++++++++++-------- etl/transform/preSiteNoteTypes.py | 10 ++++++++-- 2 files changed, 29 insertions(+), 10 deletions(-) diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index b245b1d..19fb0ff 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -5,7 +5,7 @@ from etl.transform.preSiteNoteTypes import ( AssessorInfo, CompanyInfo, PreSiteNotesSummaryInfo, PreSiteNote, PropertyDescription, Dimension, HeatingType, Heating, HeatingSystemControls, - OtherDetails, WindTurbine + OtherDetails, WindTurbine, PhotovoltaicPanel ) import uuid @@ -98,10 +98,27 @@ class surveyedDataProcessor(): ) # Other details - otherDetails = self.get_attribute_and_load(self.pre_site_note.property_description, "otherDetails", OtherDetails, db_session) + otherDetails = self.get_attribute_and_load( + self.pre_site_note.property_description, + "otherDetails", + OtherDetails, + db_session + ) # windTurbine - windTurbine = self.get_attribute_and_load(self.pre_site_note.property_description, "windTurbine", WindTurbine, db_session) + windTurbine = self.get_attribute_and_load( + self.pre_site_note.property_description, + "windTurbine", + WindTurbine, + db_session + ) + + photo_volatic_panel = self.get_attribute_and_load( + self.pre_site_note.property_description, + "photovoltaicPanel", + PhotovoltaicPanel, + db_session, + ) @@ -155,11 +172,7 @@ class surveyedDataProcessor(): Returns: SQLAlchemy model instance. """ - # Filter out private/internal fields like _sa_instance_state - clean_data = { - k: v for k, v in data_dict.items() - if not k.startswith('_') and hasattr(model_class, k) - } + clean_data = data_dict # Merge additional fields if provided if additional_fields: diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index a375ad3..478d7e5 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -161,11 +161,11 @@ class ShowerAndBaths(BaseModel): class FlueGasHeatRecoverySystem(BaseModel): fghrs_present: bool -class PhotovoltaicPanel(BaseModel): +class PhotovoltaicPanel(BaseModel, table=True): pvs_are_connected_to_dwelling_electricity_meter: bool percentage_of_external_roof_area_with_pvs: str -class WindTurbine(BaseModel): +class WindTurbine(BaseModel, table=True): wind_turbine: bool class OtherDetails(BaseModel, table=True): @@ -218,6 +218,12 @@ class PropertyDescription(BaseModel): solarWaterHeating: Optional[SolarWaterHeating] showerAndBaths: Optional[ShowerAndBaths] flueGasHeatRecoverySystem: Optional[FlueGasHeatRecoverySystem] + + # photovolaticPanel + photo_voltaic_panel_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key='photovoltaicpanel.id' + ) photovoltaicPanel: Optional[PhotovoltaicPanel] # windTuribe From 44f4ae331a356a95ae36abd271f3fbf0a4a9054a Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Thu, 8 May 2025 16:01:35 +0000 Subject: [PATCH 24/33] database almost complete, one or two things left --- etl/surveyedData/surveryedData.py | 72 ++++++++++++++++++++++++++++-- etl/transform/preSiteNoteTypes.py | 73 ++++++++++++++++++++++++++----- 2 files changed, 132 insertions(+), 13 deletions(-) diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 19fb0ff..ea66566 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -5,7 +5,9 @@ from etl.transform.preSiteNoteTypes import ( AssessorInfo, CompanyInfo, PreSiteNotesSummaryInfo, PreSiteNote, PropertyDescription, Dimension, HeatingType, Heating, HeatingSystemControls, - OtherDetails, WindTurbine, PhotovoltaicPanel + OtherDetails, WindTurbine, PhotovoltaicPanel, FlueGasHeatRecoverySystem, ShowerAndBaths, + SolarWaterHeating, HotWaterCylinder, WaterHeating, Lighting, VentilationAndCooling, + Door, ) import uuid @@ -113,14 +115,78 @@ class surveyedDataProcessor(): db_session ) + #photo_volatic_panel photo_volatic_panel = self.get_attribute_and_load( self.pre_site_note.property_description, "photovoltaicPanel", PhotovoltaicPanel, db_session, ) - - + + #fluegasheatrecoverysystem + flue_gas_heat_recovery_system = self.get_attribute_and_load( + self.pre_site_note.property_description, + "flueGasHeatRecoverySystem", + FlueGasHeatRecoverySystem, + db_session, + ) + + #shower and baths + shower_and_baths = self.get_attribute_and_load( + self.pre_site_note.property_description, + "showerAndBaths", + ShowerAndBaths, + db_session, + ) + + #solar water heating + solar_water_heating = self.get_attribute_and_load( + self.pre_site_note.property_description, + "solarWaterHeating", + SolarWaterHeating, + db_session, + ) + + # hotwatercycling + hot_water_cyclinder = self.get_attribute_and_load( + self.pre_site_note.property_description, + "hotWaterCylinder", + HotWaterCylinder, + db_session, + ) + + # water heating + water_heating = self.get_attribute_and_load( + self.pre_site_note.property_description, + "waterHeating", + WaterHeating, + db_session, + ) + + # lighting + lighting = self.get_attribute_and_load( + self.pre_site_note.property_description, + "lighting", + Lighting, + db_session, + ) + + # ventilation and cooling + ventilation_and_cooling = self.get_attribute_and_load( + self.pre_site_note.property_description, + "ventilationAndCooling", + VentilationAndCooling, + db_session, + ) + + # door + door = self.get_attribute_and_load( + self.pre_site_note.property_description, + "door", + Door, + db_session, + ) + def load_company_table(self, db_session): company_data = self.pre_site_note.company_information.model_dump() diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index 478d7e5..231971a 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -83,7 +83,7 @@ class Floors(BaseModel): floor_insulation_thickness_mm: Optional[float] = -1 u_value_known: bool -class Door(BaseModel): +class Door(BaseModel, table=True): no_of_doors: int no_of_insulated_doors: int u_value_w_m2_k: Optional[str] @@ -100,12 +100,12 @@ class AssessorInfo(BaseModel, table=True): pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="assessor") -class VentilationAndCooling(BaseModel): +class VentilationAndCooling(BaseModel, table=True): no_of_open_fireplaces: int ventilation_type: str space_cooling_system_present: bool -class Lighting(BaseModel): +class Lighting(BaseModel, table=True): total_no_of_light_fittings: int total_no_of_lel_fittings: int @@ -140,25 +140,25 @@ class HeatingType(BaseModel, table=True): heating_type: str fuel_type: str -class WaterHeating(BaseModel): +class WaterHeating(BaseModel, table=True): heating_type: str fuel_type: str -class HotWaterCylinder(BaseModel): +class HotWaterCylinder(BaseModel, table=True): volume: str insulation_type: str insulation_thickness: str thermostat: bool -class SolarWaterHeating(BaseModel): +class SolarWaterHeating(BaseModel, table=True): solar_water_heating_details_known: bool -class ShowerAndBaths(BaseModel): +class ShowerAndBaths(BaseModel, table=True): no_of_rooms_with_baths_and_or_shower: int no_of_rooms_with_mixer_shower_and_no_baths: int no_of_rooms_with_mixer_shower_and_baths: int -class FlueGasHeatRecoverySystem(BaseModel): +class FlueGasHeatRecoverySystem(BaseModel, table=True): fghrs_present: bool class PhotovoltaicPanel(BaseModel, table=True): @@ -194,6 +194,7 @@ class PropertyDescription(BaseModel): built_form: str detachment_or_position: str no_of_main_property: int + conservatory: bool no_of_extension_1: Optional[int] = 0 no_of_extension_2: Optional[int] = 0 no_of_extension_3: Optional[int] = 0 @@ -205,18 +206,71 @@ class PropertyDescription(BaseModel): percentage_of_draught_proofed: int terrain_type: str main_property: PropertyDetail + + ex1_property: Optional[PropertyDetail] = None ex2_property: Optional[PropertyDetail] = None ex3_property: Optional[PropertyDetail] = None ex4_property: Optional[PropertyDetail] = None - conservatory: bool + + # Door + door_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="door.id" + ) door: Optional[Door] + + + # ventilation and cooling + vetilation_and_cooling_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="ventilationandcooling.id" + ) ventilationAndCooling: Optional[VentilationAndCooling] + + + # lighting + lighting_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="lighting.id" + ) lighting: Optional[Lighting] + + + # water heating + water_heating_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="waterheating.id" + ) waterHeating: Optional[WaterHeating] + + + #hotwatercylinder + hot_water_cylinder_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="hotwatercylinder.id" + ) hotWaterCylinder: Optional[HotWaterCylinder] + + #solarAndWaterHeating + solar_water_heating_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="solarwaterheating.id" + ) solarWaterHeating: Optional[SolarWaterHeating] + + # shower and baths + shower_and_baths_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="showerandbaths.id" + ) showerAndBaths: Optional[ShowerAndBaths] + + # flueGasHeatRecoverySystem + flue_gas_heat_recovery_system_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="fluegasheatrecoverysystem.id" + ) flueGasHeatRecoverySystem: Optional[FlueGasHeatRecoverySystem] # photovolaticPanel @@ -255,7 +309,6 @@ class PropertyDescription(BaseModel): default=None, foreign_key="heatingtype.id" ) - secondaryHeatingType: Optional[HeatingType] class Insulation(BaseModel): From 2612a1f1dcbcc0cc5d43ac4929565cc9d2f42e1c Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 12 May 2025 10:21:18 +0000 Subject: [PATCH 25/33] save current pogress --- .vscode/settings.json | 8 +++- etl/osmosis_google_maps_.py | 7 +++- etl/surveyedData/surveryedData.py | 62 ++++++++++++++++++++++++++++++- etl/transform/preSiteNoteTypes.py | 42 ++++++++++++++++----- 4 files changed, 105 insertions(+), 14 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 49f0804..9158a79 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,7 +1,13 @@ { "jupyter.interactiveWindow.textEditor.executeSelection": true, "python.REPL.sendToNativeREPL": true, - "notebook.output.scrolling": true + "notebook.output.scrolling": true, + "terminal.integrated.defaultProfile.linux": "bash", + "terminal.integrated.profiles.linux": { + "bash": { + "path": "/bin/bash" + } + } // Hot reload setting that needs to be in user settings // "jupyter.runStartupCommands": [ diff --git a/etl/osmosis_google_maps_.py b/etl/osmosis_google_maps_.py index ccc1d16..c8c2654 100644 --- a/etl/osmosis_google_maps_.py +++ b/etl/osmosis_google_maps_.py @@ -1,10 +1,13 @@ from monday import MondayClient import json import requests +import time +from tqdm import tqdm + + board_id = "8829428746" monday_key = "eyJhbGciOiJIUzI1NiJ9.eyJ0aWQiOjQ5ODc2ODQxOCwiYWFpIjoxMSwidWlkIjozNjE3ODAzNCwiaWFkIjoiMjAyNS0wNC0xMVQxMToyMzoxNy40NjdaIiwicGVyIjoibWU6d3JpdGUiLCJhY3RpZCI6MTM5OTc4MjMsInJnbiI6InVzZTEifQ.-2Lit4s46ZF6AXuMW9t0TxIaFLkHqD4Yo-PyM9i2XZY" monday = MondayClient(monday_key) -import time def get_all_items(board_id, monday): # Parameters @@ -75,7 +78,7 @@ if not postcode_col_id or not location_col_id: raise Exception("Could not find 'postcode' or 'location' columns") items = get_all_items(board_id, monday) -for item in items: +for item in tqdm(items): item_name = item["name"] item_id = item["id"] diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index ea66566..44eda12 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -7,7 +7,7 @@ from etl.transform.preSiteNoteTypes import ( PropertyDescription, Dimension, HeatingType, Heating, HeatingSystemControls, OtherDetails, WindTurbine, PhotovoltaicPanel, FlueGasHeatRecoverySystem, ShowerAndBaths, SolarWaterHeating, HotWaterCylinder, WaterHeating, Lighting, VentilationAndCooling, - Door, + Door, Walls, Roofs, Floors, PropertyDetail, Windows ) import uuid @@ -187,6 +187,66 @@ class surveyedDataProcessor(): db_session, ) + def upload_property_detail(property_part="main_property"): + if check_if_attribute_exists(self.pre_site_note.property_description, property_part): + wall = None + obj = getattr(self.pre_site_note.property_description, property_part) + if check_if_attribute_exists(obj, "wall"): + wall = self.get_attribute_and_load(obj, "wall", Walls, db_session) + roof = None + if check_if_attribute_exists(obj, "roof"): + roof = self.get_attribute_and_load(obj, "roof", Roofs, db_session) + floor = None + if check_if_attribute_exists(obj, "floor"): + roof = self.get_attribute_and_load(obj, "floor", Floors, db_session) + + dimensions = [] + if check_if_attribute_exists(obj, "dimensions"): + dimension_obj = getattr(obj, "dimensions") + for eachDimension in dimension_obj: + data = eachDimension.model_dump() + dimension = self.upsert_record( + db_session=db_session, + model_class=Dimension, + data_dict=data, + lookup_field=None, + ) + dimensions.append(dimension.id) + + windows = [] + if check_if_attribute_exists(obj, "windows"): + windows_obj = getattr(obj, "windows") + for eachWindow in windows_obj: + data = eachWindow.model_dump() + window = self.upsert_record( + db_session=db_session, + model_class=Windows, + data_dict=data, + lookup_field=None, + ) + windows.append(windows.id) + + property_detail = self.upsert_record( + db_session=db_session, + model_class=PropertyDetail, + data = obj.model_dump(), + additional_fields={ + "windows_id": windows, + "dimensions_id": dimensions, + "floors_id": floor.id if floor else None, + "roof_id": roof.id if roof else None, + "wall_id": wall.id if wall else None, + + } + ) + + return property_detail + + # main_property + main_property = upload_property_detail("main_property") + print(main_property.id) + + def load_company_table(self, db_session): company_data = self.pre_site_note.company_information.model_dump() diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index 231971a..f77a2d7 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -13,7 +13,7 @@ class BaseModel(SQLModel): primary_key=True, ) -class Dimension(BaseModel): +class Dimension(BaseModel, table=True): floor_area_m2: float room_height_m: float loss_perimeter_m: float @@ -59,7 +59,7 @@ class PreSiteNotesSummaryInfo(BaseModel, table=True): ) -class Walls(BaseModel): +class Walls(BaseModel, table=True): construction: str insulation: str insulation_thickness_mm: str @@ -70,13 +70,13 @@ class Walls(BaseModel): dry_lining: bool alternative_wall_present: bool -class Roofs(BaseModel): +class Roofs(BaseModel, table=True): construction: str insulation_type: str insulation_thickness: str u_value_known: bool -class Floors(BaseModel): +class Floors(BaseModel, table=True): floor_type: str ground_floor_construction: str ground_floor_insulation_type: Optional[str] = "" @@ -172,7 +172,7 @@ class OtherDetails(BaseModel, table=True): electricity_meter_type: str main_gas_avalible: bool -class Windows(BaseModel): +class Windows(BaseModel, table=True): glazing_type: str area_m2: float roof_window: bool @@ -180,14 +180,35 @@ class Windows(BaseModel): u_value_w_m2_k: int g_value: int -class PropertyDetail(BaseModel): + property_db: Optional["PropertyDetail"] = Relationship(back_populates="windows_db") + + +class PropertyDetail(BaseModel, table=True): # change this name to build parts age_band: str - dimensions: List[Dimension] = [] - wall: Optional[Walls] = None - roof: Optional[Roofs] = None - floor: Optional[Floors] = None windows: Optional[List[Windows]] = [] + windows_db: List[Windows] = Relationship(back_populates="property_db") + + wall: Optional[Walls] = None + wall_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="walls.id" + ) + roof: Optional[Roofs] = None + roof_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="roofs.id" + ) + floor: Optional[Floors] = None + floor_id: Optional[uuid.UUID] = Field( + default=None, + foreign_key="floors.id" + ) + dimensions: List[Dimension] = [] + dimensions_id: Optional[List[uuid.UUID]] = Field( + default=None, + foreign_key="dimensions.id" + ) class PropertyDescription(BaseModel): @@ -212,6 +233,7 @@ class PropertyDescription(BaseModel): ex2_property: Optional[PropertyDetail] = None ex3_property: Optional[PropertyDetail] = None ex4_property: Optional[PropertyDetail] = None + # Do walls next, check if variable is real similar to main heating # Door door_id: Optional[uuid.UUID] = Field( From 7ca500b8c540c9fb348f51cc79546ee3def7d177 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 12 May 2025 10:33:07 +0000 Subject: [PATCH 26/33] remake load class --- alembic/env.py | 2 +- etl/load/preSiteNoteTypes.py | 234 ++++++++++++++++++++++++++++++ etl/transform/preSiteNoteTypes.py | 219 +++++----------------------- 3 files changed, 273 insertions(+), 182 deletions(-) create mode 100644 etl/load/preSiteNoteTypes.py diff --git a/alembic/env.py b/alembic/env.py index 7966288..c8174a7 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -5,7 +5,7 @@ from sqlalchemy import pool from alembic import context from sqlmodel import SQLModel -from etl.transform.preSiteNoteTypes import * +from etl.load.preSiteNoteTypes import * import os diff --git a/etl/load/preSiteNoteTypes.py b/etl/load/preSiteNoteTypes.py new file mode 100644 index 0000000..5ce4314 --- /dev/null +++ b/etl/load/preSiteNoteTypes.py @@ -0,0 +1,234 @@ +from sqlmodel import Field, SQLModel, Relationship +import uuid +from typing import Optional, List +from datetime import datetime +from pydantic import EmailStr +from sqlalchemy import Column +from sqlalchemy.dialects.postgresql import UUID + + +class BaseModel(SQLModel): + id: uuid.UUID = Field( + default_factory=uuid.uuid4, + sa_column=Column(UUID(as_uuid=True), primary_key=True) + ) + + +class Dimension(BaseModel, table=True): + floor_area_m2: float + room_height_m: float + loss_perimeter_m: float + party_wall_length_m: float + property_detail_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id") + + +class Walls(BaseModel, table=True): + construction: str + insulation: str + insulation_thickness_mm: str + wall_thickness_measured: bool + wall_thickness_mm: Optional[int] + u_value_known: bool + u_value_w_m2_k: Optional[float] + dry_lining: bool + alternative_wall_present: bool + + +class Roofs(BaseModel, table=True): + construction: str + insulation_type: str + insulation_thickness: str + u_value_known: bool + + +class Floors(BaseModel, table=True): + floor_type: str + ground_floor_construction: str + ground_floor_insulation_type: Optional[str] = "" + floor_insulation_thickness_mm: Optional[float] = -1 + u_value_known: bool + + +class Windows(BaseModel, table=True): + glazing_type: str + area_m2: float + roof_window: bool + orientation: str + u_value_w_m2_k: int + g_value: int + property_detail_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id") + + +class PropertyDetail(BaseModel, table=True): + age_band: str + wall_id: Optional[uuid.UUID] = Field(default=None, foreign_key="walls.id") + roof_id: Optional[uuid.UUID] = Field(default=None, foreign_key="roofs.id") + floor_id: Optional[uuid.UUID] = Field(default=None, foreign_key="floors.id") + + dimensions: List[Dimension] = Relationship(back_populates="property_detail") + windows: List[Windows] = Relationship(back_populates="property_detail") + + +Dimension.property_detail = Relationship(back_populates="dimensions") +Windows.property_detail = Relationship(back_populates="windows") + + +class Door(BaseModel, table=True): + no_of_doors: int + no_of_insulated_doors: int + u_value_w_m2_k: Optional[str] + + +class VentilationAndCooling(BaseModel, table=True): + no_of_open_fireplaces: int + ventilation_type: str + space_cooling_system_present: bool + + +class Lighting(BaseModel, table=True): + total_no_of_light_fittings: int + total_no_of_lel_fittings: int + + +class HeatingSystemControls(BaseModel, table=True): + control_type: str + flue_type: str + fan_assisted_flue: bool + heat_emitter_type: str + electricity_meter_type: Optional[str] = "" + mains_gas_available: Optional[bool] = False + + +class Heating(BaseModel, table=True): + type: str + heating_source: str + efficiency_source: str + heating_fuel: str + brand_name: str + model_name: str + model_qualifer: str + sap_2009_table: Optional[str] = "" + percentage_of_heated_floor_area_served: Optional[str] = "" + controls_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingsystemcontrols.id") + + +class HeatingType(BaseModel, table=True): + heating_type: str + fuel_type: str + + +class WaterHeating(BaseModel, table=True): + heating_type: str + fuel_type: str + + +class HotWaterCylinder(BaseModel, table=True): + volume: str + insulation_type: str + insulation_thickness: str + thermostat: bool + + +class SolarWaterHeating(BaseModel, table=True): + solar_water_heating_details_known: bool + + +class ShowerAndBaths(BaseModel, table=True): + no_of_rooms_with_baths_and_or_shower: int + no_of_rooms_with_mixer_shower_and_no_baths: int + no_of_rooms_with_mixer_shower_and_baths: int + + +class FlueGasHeatRecoverySystem(BaseModel, table=True): + fghrs_present: bool + + +class PhotovoltaicPanel(BaseModel, table=True): + pvs_are_connected_to_dwelling_electricity_meter: bool + percentage_of_external_roof_area_with_pvs: str + + +class WindTurbine(BaseModel, table=True): + wind_turbine: bool + + +class OtherDetails(BaseModel, table=True): + electricity_meter_type: str + main_gas_avalible: bool + + +class PropertyDescription(BaseModel, table=True): + built_form: str + detachment_or_position: str + no_of_main_property: int + no_of_extension_1: Optional[int] = 0 + no_of_extension_2: Optional[int] = 0 + no_of_extension_3: Optional[int] = 0 + no_of_extension_4: Optional[int] = 0 + no_of_habitable_rooms: int + no_of_heated_rooms: int + heated_basement: bool + conservatory_type: str + percentage_of_draught_proofed: int + terrain_type: str + conservatory: bool + + main_property_id: uuid.UUID = Field(foreign_key="propertydetail.id") + ex1_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id") + ex2_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id") + ex3_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id") + ex4_property_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id") + + door_id: Optional[uuid.UUID] = Field(default=None, foreign_key="door.id") + ventilation_and_cooling_id: Optional[uuid.UUID] = Field(default=None, foreign_key="ventilationandcooling.id") + lighting_id: Optional[uuid.UUID] = Field(default=None, foreign_key="lighting.id") + water_heating_id: Optional[uuid.UUID] = Field(default=None, foreign_key="waterheating.id") + hot_water_cylinder_id: Optional[uuid.UUID] = Field(default=None, foreign_key="hotwatercylinder.id") + solar_water_heating_id: Optional[uuid.UUID] = Field(default=None, foreign_key="solarwaterheating.id") + shower_and_baths_id: Optional[uuid.UUID] = Field(default=None, foreign_key="showerandbaths.id") + flue_gas_heat_recovery_system_id: Optional[uuid.UUID] = Field(default=None, foreign_key="fluegasheatrecoverysystem.id") + photovoltaic_panel_id: Optional[uuid.UUID] = Field(default=None, foreign_key="photovoltaicpanel.id") + wind_turbine_id: Optional[uuid.UUID] = Field(default=None, foreign_key="windturbine.id") + other_details_id: Optional[uuid.UUID] = Field(default=None, foreign_key="otherdetails.id") + main_heating_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heating.id") + main_heating2_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heating.id") + secondary_heating_type_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingtype.id") + + +class AssessorInfo(BaseModel, table=True): + accreditation_number: str + name: str + phone_number: Optional[str] = None + email_address: Optional[EmailStr] = None + + +class SurverySummaryInfo(BaseModel, table=True): + reference_number: str + epc_language: str + uprn: Optional[str] = "" + postcode: str + region: str + address: str + town: str + county: Optional[str] = "" + property_tenure: str + transaction_type: str + inspection_date: datetime + current_sap: str + potential_sap: str + current_ei: str + potential_ei: str + current_annual_emissions: str + current_annual_emission_including_0925_multiplayer: str + current_annual_energy_costs: str + + +class CompanyInfo(BaseModel, table=True): + trading_name: str + post_code: str + fax_number: Optional[str] = None + related_party_disclosure: Optional[str] = None + + +class Insulation(BaseModel, table=True): + type: str diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index f77a2d7..7e25682 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -1,31 +1,28 @@ -from sqlmodel import Field, SQLModel, Relationship +from sqlmodel import Field, SQLModel import uuid from datetime import datetime from pydantic import field_validator, EmailStr from typing import Optional, List from sqlalchemy import Column -from sqlalchemy.orm import relationship from sqlalchemy.dialects.postgresql import UUID class BaseModel(SQLModel): id: uuid.UUID = Field( default_factory=uuid.uuid4, - primary_key=True, + sa_column=Column(UUID(as_uuid=True), primary_key=True) ) -class Dimension(BaseModel, table=True): +class Dimension(BaseModel): floor_area_m2: float room_height_m: float loss_perimeter_m: float party_wall_length_m: float -class CompanyInfo(BaseModel, table=True): +class CompanyInfo(BaseModel): trading_name: str post_code: str fax_number: Optional[str] = None related_party_disclosure: Optional[str] = None - - __table_args__ = {"extend_existing": True} @field_validator('related_party_disclosure') def set_none_if_none_of_the_above(cls, v): @@ -33,7 +30,7 @@ class CompanyInfo(BaseModel, table=True): return None return v -class PreSiteNotesSummaryInfo(BaseModel, table=True): +class SurverySummaryInfo(BaseModel): reference_number: str epc_language: str uprn: Optional[str] = "" @@ -52,14 +49,8 @@ class PreSiteNotesSummaryInfo(BaseModel, table=True): current_annual_emissions: str current_annual_emission_including_0925_multiplayer: str current_annual_energy_costs: str - - pre_site_note: Optional["PreSiteNote"] = Relationship( - back_populates="summary_info", - sa_relationship=relationship("PreSiteNote", back_populates="summary_info", uselist=False) - ) - -class Walls(BaseModel, table=True): +class Walls(BaseModel): construction: str insulation: str insulation_thickness_mm: str @@ -70,20 +61,20 @@ class Walls(BaseModel, table=True): dry_lining: bool alternative_wall_present: bool -class Roofs(BaseModel, table=True): +class Roofs(BaseModel): construction: str insulation_type: str insulation_thickness: str u_value_known: bool -class Floors(BaseModel, table=True): +class Floors(BaseModel): floor_type: str ground_floor_construction: str ground_floor_insulation_type: Optional[str] = "" floor_insulation_thickness_mm: Optional[float] = -1 u_value_known: bool -class Door(BaseModel, table=True): +class Door(BaseModel): no_of_doors: int no_of_insulated_doors: int u_value_w_m2_k: Optional[str] @@ -93,23 +84,17 @@ class AssessorInfo(BaseModel, table=True): name: str phone_number: Optional[str] = None email_address: Optional[EmailStr] = None - company_id: Optional[uuid.UUID] = Field( - foreign_key="companyinfo.id", # Referencing CompanyInfo - nullable=False - ) - - pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="assessor") -class VentilationAndCooling(BaseModel, table=True): +class VentilationAndCooling(BaseModel): no_of_open_fireplaces: int ventilation_type: str space_cooling_system_present: bool -class Lighting(BaseModel, table=True): +class Lighting(BaseModel): total_no_of_light_fittings: int total_no_of_lel_fittings: int -class HeatingSystemControls(BaseModel, table=True): +class HeatingSystemControls(BaseModel): control_type: str flue_type: str fan_assisted_flue: bool @@ -117,11 +102,7 @@ class HeatingSystemControls(BaseModel, table=True): electricity_meter_type: Optional[str] = "" mains_gas_available: Optional[bool] = False - # Reverse relationship (optional) - heating: Optional["Heating"] = Relationship( - back_populates="controls", - ) -class Heating(BaseModel, table=True): +class Heating(BaseModel): type: str heating_source: str efficiency_source: str @@ -131,48 +112,45 @@ class Heating(BaseModel, table=True): model_qualifer: str sap_2009_table: Optional[str] = "" percentage_of_heated_floor_area_served: Optional[str] = "" + controls: HeatingSystemControls - # Foregin Key to HeatingSystemControls - controls_id: Optional[uuid.UUID] = Field(foreign_key="heatingsystemcontrols.id") - controls: Optional["HeatingSystemControls"] = Relationship(back_populates="heating") - -class HeatingType(BaseModel, table=True): +class HeatingType(BaseModel): heating_type: str fuel_type: str -class WaterHeating(BaseModel, table=True): +class WaterHeating(BaseModel): heating_type: str fuel_type: str -class HotWaterCylinder(BaseModel, table=True): +class HotWaterCylinder(BaseModel): volume: str insulation_type: str insulation_thickness: str thermostat: bool -class SolarWaterHeating(BaseModel, table=True): +class SolarWaterHeating(BaseModel): solar_water_heating_details_known: bool -class ShowerAndBaths(BaseModel, table=True): +class ShowerAndBaths(BaseModel): no_of_rooms_with_baths_and_or_shower: int no_of_rooms_with_mixer_shower_and_no_baths: int no_of_rooms_with_mixer_shower_and_baths: int -class FlueGasHeatRecoverySystem(BaseModel, table=True): +class FlueGasHeatRecoverySystem(BaseModel): fghrs_present: bool -class PhotovoltaicPanel(BaseModel, table=True): +class PhotovoltaicPanel(BaseModel): pvs_are_connected_to_dwelling_electricity_meter: bool percentage_of_external_roof_area_with_pvs: str -class WindTurbine(BaseModel, table=True): +class WindTurbine(BaseModel): wind_turbine: bool -class OtherDetails(BaseModel, table=True): +class OtherDetails(BaseModel): electricity_meter_type: str main_gas_avalible: bool -class Windows(BaseModel, table=True): +class Windows(BaseModel): glazing_type: str area_m2: float roof_window: bool @@ -180,42 +158,20 @@ class Windows(BaseModel, table=True): u_value_w_m2_k: int g_value: int - property_db: Optional["PropertyDetail"] = Relationship(back_populates="windows_db") - - -class PropertyDetail(BaseModel, table=True): +class PropertyDetail(BaseModel): # change this name to build parts age_band: str - windows: Optional[List[Windows]] = [] - windows_db: List[Windows] = Relationship(back_populates="property_db") - - wall: Optional[Walls] = None - wall_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="walls.id" - ) - roof: Optional[Roofs] = None - roof_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="roofs.id" - ) - floor: Optional[Floors] = None - floor_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="floors.id" - ) dimensions: List[Dimension] = [] - dimensions_id: Optional[List[uuid.UUID]] = Field( - default=None, - foreign_key="dimensions.id" - ) + wall: Optional[Walls] = None + roof: Optional[Roofs] = None + floor: Optional[Floors] = None + windows: Optional[List[Windows]] = [] class PropertyDescription(BaseModel): built_form: str detachment_or_position: str no_of_main_property: int - conservatory: bool no_of_extension_1: Optional[int] = 0 no_of_extension_2: Optional[int] = 0 no_of_extension_3: Optional[int] = 0 @@ -227,130 +183,31 @@ class PropertyDescription(BaseModel): percentage_of_draught_proofed: int terrain_type: str main_property: PropertyDetail - - ex1_property: Optional[PropertyDetail] = None ex2_property: Optional[PropertyDetail] = None ex3_property: Optional[PropertyDetail] = None ex4_property: Optional[PropertyDetail] = None - # Do walls next, check if variable is real similar to main heating - - # Door - door_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="door.id" - ) + conservatory: bool door: Optional[Door] - - - # ventilation and cooling - vetilation_and_cooling_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="ventilationandcooling.id" - ) ventilationAndCooling: Optional[VentilationAndCooling] - - - # lighting - lighting_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="lighting.id" - ) lighting: Optional[Lighting] - - - # water heating - water_heating_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="waterheating.id" - ) waterHeating: Optional[WaterHeating] - - - #hotwatercylinder - hot_water_cylinder_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="hotwatercylinder.id" - ) hotWaterCylinder: Optional[HotWaterCylinder] - - #solarAndWaterHeating - solar_water_heating_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="solarwaterheating.id" - ) solarWaterHeating: Optional[SolarWaterHeating] - - # shower and baths - shower_and_baths_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="showerandbaths.id" - ) showerAndBaths: Optional[ShowerAndBaths] - - # flueGasHeatRecoverySystem - flue_gas_heat_recovery_system_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="fluegasheatrecoverysystem.id" - ) flueGasHeatRecoverySystem: Optional[FlueGasHeatRecoverySystem] - - # photovolaticPanel - photo_voltaic_panel_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key='photovoltaicpanel.id' - ) photovoltaicPanel: Optional[PhotovoltaicPanel] - - # windTuribe - wind_turbine_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="windturbine.id" - ) windTurbine: Optional[WindTurbine] - - - # other details - other_details_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="otherdetails.id" - ) - otherDetails: Optional["OtherDetails"] - + otherDetails: Optional[OtherDetails] mainHeating: Optional[Heating] - - # Foreign key to Main Heating 2 - main_heating_2_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="heatingtype.id" - ) mainHeating2: Optional[Heating] - - # Foreign key to HeatingType - secondary_heating_type_id: Optional[uuid.UUID] = Field( - default=None, - foreign_key="heatingtype.id" - ) secondaryHeatingType: Optional[HeatingType] +# class PropertyReport(): + # TODO: Property description + # TODO: Due consideration foregin key + # TODO: Which company did it (Osmosis, Warmfront etc) + # TODO: Links to more foreign keys per report etc + class Insulation(BaseModel): - type: str - -# One class to rule them all -class PreSiteNote(BaseModel, table=True): - # Summary Info - summary_info_id: uuid.UUID = Field( - foreign_key="presitenotessummaryinfo.id", - nullable=False - ) - - summary_info: Optional["PreSiteNotesSummaryInfo"] = Relationship(back_populates="pre_site_note") - - - # Assessor Info - assessor_id: uuid.UUID = Field( - foreign_key="assessorinfo.id", - nullable=False - ) - - assessor: Optional["AssessorInfo"] = Relationship(back_populates="pre_site_notes") \ No newline at end of file + type: str \ No newline at end of file From 4c9bcada1332d9a65bb0a754a5418b9ad5cd4bbe Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 12 May 2025 13:25:28 +0000 Subject: [PATCH 27/33] added migration scripts --- .../9f45742b4b2f_initial_migration.py | 328 ++++++++++++++++++ .../versions/e351a98aba54_init_some_tables.py | 117 ------- etl/load/preSiteNoteTypes.py | 43 ++- etl/surveyedData/surveryedData.py | 63 ++-- etl/transform/preSiteNoteTypes.py | 9 +- migration_db.sh | 3 +- 6 files changed, 397 insertions(+), 166 deletions(-) create mode 100644 alembic/versions/9f45742b4b2f_initial_migration.py delete mode 100644 alembic/versions/e351a98aba54_init_some_tables.py diff --git a/alembic/versions/9f45742b4b2f_initial_migration.py b/alembic/versions/9f45742b4b2f_initial_migration.py new file mode 100644 index 0000000..a59e894 --- /dev/null +++ b/alembic/versions/9f45742b4b2f_initial_migration.py @@ -0,0 +1,328 @@ +"""Initial migration + +Revision ID: 9f45742b4b2f +Revises: +Create Date: 2025-05-12 13:24:03.856980 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa +import sqlmodel + + +# revision identifiers, used by Alembic. +revision: str = '9f45742b4b2f' +down_revision: Union[str, None] = None +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.create_table('companyinfo', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('trading_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('post_code', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('fax_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('related_party_disclosure', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('door', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('no_of_doors', sa.Integer(), nullable=False), + sa.Column('no_of_insulated_doors', sa.Integer(), nullable=False), + sa.Column('u_value_w_m2_k', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('floors', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('floor_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('ground_floor_construction', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('ground_floor_insulation_type', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('floor_insulation_thickness_mm', sa.Float(), nullable=True), + sa.Column('u_value_known', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('fluegasheatrecoverysystem', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('fghrs_present', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('heatingsystemcontrols', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('control_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('flue_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('fan_assisted_flue', sa.Boolean(), nullable=False), + sa.Column('heat_emitter_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('electricity_meter_type', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('mains_gas_available', sa.Boolean(), nullable=True), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('heatingtype', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('heating_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('fuel_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('hotwatercylinder', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('volume', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('insulation_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('insulation_thickness', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('thermostat', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('insulation', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('lighting', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('total_no_of_light_fittings', sa.Integer(), nullable=False), + sa.Column('total_no_of_lel_fittings', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('otherdetails', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('electricity_meter_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('main_gas_avalible', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('photovoltaicpanel', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('pvs_are_connected_to_dwelling_electricity_meter', sa.Boolean(), nullable=False), + sa.Column('percentage_of_external_roof_area_with_pvs', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('presitenotessummaryinfo', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('reference_number', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('epc_language', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('uprn', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('postcode', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('region', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('address', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('town', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('county', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('property_tenure', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('transaction_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('inspection_date', sa.DateTime(), nullable=False), + sa.Column('current_sap', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('potential_sap', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('current_ei', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('potential_ei', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('current_annual_emissions', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('current_annual_emission_including_0925_multiplayer', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('current_annual_energy_costs', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('roofs', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('construction', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('insulation_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('insulation_thickness', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('u_value_known', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('showerandbaths', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('no_of_rooms_with_baths_and_or_shower', sa.Integer(), nullable=False), + sa.Column('no_of_rooms_with_mixer_shower_and_no_baths', sa.Integer(), nullable=False), + sa.Column('no_of_rooms_with_mixer_shower_and_baths', sa.Integer(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('solarwaterheating', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('solar_water_heating_details_known', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('ventilationandcooling', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('no_of_open_fireplaces', sa.Integer(), nullable=False), + sa.Column('ventilation_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('space_cooling_system_present', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('walls', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('construction', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('insulation', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('insulation_thickness_mm', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('wall_thickness_measured', sa.Boolean(), nullable=False), + sa.Column('wall_thickness_mm', sa.Integer(), nullable=True), + sa.Column('u_value_known', sa.Boolean(), nullable=False), + sa.Column('u_value_w_m2_k', sa.Float(), nullable=True), + sa.Column('dry_lining', sa.Boolean(), nullable=False), + sa.Column('alternative_wall_present', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('waterheating', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('heating_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('fuel_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('windturbine', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('wind_turbine', sa.Boolean(), nullable=False), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('assessorinfo', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('accreditation_number', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('phone_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('email_address', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('company_id', sa.Uuid(), nullable=True), + sa.ForeignKeyConstraint(['company_id'], ['companyinfo.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('heating', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('heating_source', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('efficiency_source', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('heating_fuel', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('brand_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('model_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('model_qualifer', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('sap_2009_table', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('percentage_of_heated_floor_area_served', sqlmodel.sql.sqltypes.AutoString(), nullable=True), + sa.Column('controls_id', sa.Uuid(), nullable=True), + sa.ForeignKeyConstraint(['controls_id'], ['heatingsystemcontrols.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('propertydetail', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('age_band', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('wall_id', sa.Uuid(), nullable=True), + sa.Column('roof_id', sa.Uuid(), nullable=True), + sa.Column('floor_id', sa.Uuid(), nullable=True), + sa.ForeignKeyConstraint(['floor_id'], ['floors.id'], ), + sa.ForeignKeyConstraint(['roof_id'], ['roofs.id'], ), + sa.ForeignKeyConstraint(['wall_id'], ['walls.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('dimension', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('floor_area_m2', sa.Float(), nullable=False), + sa.Column('room_height_m', sa.Float(), nullable=False), + sa.Column('loss_perimeter_m', sa.Float(), nullable=False), + sa.Column('party_wall_length_m', sa.Float(), nullable=False), + sa.Column('property_detail_id', sa.Uuid(), nullable=True), + sa.ForeignKeyConstraint(['property_detail_id'], ['propertydetail.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('presitenote', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('summary_info_id', sa.Uuid(), nullable=False), + sa.Column('assessor_id', sa.Uuid(), nullable=False), + sa.ForeignKeyConstraint(['assessor_id'], ['assessorinfo.id'], ), + sa.ForeignKeyConstraint(['summary_info_id'], ['presitenotessummaryinfo.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('propertydescription', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('built_form', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('detachment_or_position', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('no_of_main_property', sa.Integer(), nullable=False), + sa.Column('no_of_extension_1', sa.Integer(), nullable=True), + sa.Column('no_of_extension_2', sa.Integer(), nullable=True), + sa.Column('no_of_extension_3', sa.Integer(), nullable=True), + sa.Column('no_of_extension_4', sa.Integer(), nullable=True), + sa.Column('no_of_habitable_rooms', sa.Integer(), nullable=False), + sa.Column('no_of_heated_rooms', sa.Integer(), nullable=False), + sa.Column('heated_basement', sa.Boolean(), nullable=False), + sa.Column('conservatory_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('percentage_of_draught_proofed', sa.Integer(), nullable=False), + sa.Column('terrain_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('conservatory', sa.Boolean(), nullable=False), + sa.Column('main_property_id', sa.Uuid(), nullable=False), + sa.Column('ex1_property_id', sa.Uuid(), nullable=True), + sa.Column('ex2_property_id', sa.Uuid(), nullable=True), + sa.Column('ex3_property_id', sa.Uuid(), nullable=True), + sa.Column('ex4_property_id', sa.Uuid(), nullable=True), + sa.Column('door_id', sa.Uuid(), nullable=True), + sa.Column('ventilation_and_cooling_id', sa.Uuid(), nullable=True), + sa.Column('lighting_id', sa.Uuid(), nullable=True), + sa.Column('water_heating_id', sa.Uuid(), nullable=True), + sa.Column('hot_water_cylinder_id', sa.Uuid(), nullable=True), + sa.Column('solar_water_heating_id', sa.Uuid(), nullable=True), + sa.Column('shower_and_baths_id', sa.Uuid(), nullable=True), + sa.Column('flue_gas_heat_recovery_system_id', sa.Uuid(), nullable=True), + sa.Column('photovoltaic_panel_id', sa.Uuid(), nullable=True), + sa.Column('wind_turbine_id', sa.Uuid(), nullable=True), + sa.Column('other_details_id', sa.Uuid(), nullable=True), + sa.Column('main_heating_id', sa.Uuid(), nullable=True), + sa.Column('main_heating2_id', sa.Uuid(), nullable=True), + sa.Column('secondary_heating_type_id', sa.Uuid(), nullable=True), + sa.ForeignKeyConstraint(['door_id'], ['door.id'], ), + sa.ForeignKeyConstraint(['ex1_property_id'], ['propertydetail.id'], ), + sa.ForeignKeyConstraint(['ex2_property_id'], ['propertydetail.id'], ), + sa.ForeignKeyConstraint(['ex3_property_id'], ['propertydetail.id'], ), + sa.ForeignKeyConstraint(['ex4_property_id'], ['propertydetail.id'], ), + sa.ForeignKeyConstraint(['flue_gas_heat_recovery_system_id'], ['fluegasheatrecoverysystem.id'], ), + sa.ForeignKeyConstraint(['hot_water_cylinder_id'], ['hotwatercylinder.id'], ), + sa.ForeignKeyConstraint(['lighting_id'], ['lighting.id'], ), + sa.ForeignKeyConstraint(['main_heating2_id'], ['heating.id'], ), + sa.ForeignKeyConstraint(['main_heating_id'], ['heating.id'], ), + sa.ForeignKeyConstraint(['main_property_id'], ['propertydetail.id'], ), + sa.ForeignKeyConstraint(['other_details_id'], ['otherdetails.id'], ), + sa.ForeignKeyConstraint(['photovoltaic_panel_id'], ['photovoltaicpanel.id'], ), + sa.ForeignKeyConstraint(['secondary_heating_type_id'], ['heatingtype.id'], ), + sa.ForeignKeyConstraint(['shower_and_baths_id'], ['showerandbaths.id'], ), + sa.ForeignKeyConstraint(['solar_water_heating_id'], ['solarwaterheating.id'], ), + sa.ForeignKeyConstraint(['ventilation_and_cooling_id'], ['ventilationandcooling.id'], ), + sa.ForeignKeyConstraint(['water_heating_id'], ['waterheating.id'], ), + sa.ForeignKeyConstraint(['wind_turbine_id'], ['windturbine.id'], ), + sa.PrimaryKeyConstraint('id') + ) + op.create_table('windows', + sa.Column('id', sa.Uuid(), nullable=False), + sa.Column('glazing_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('area_m2', sa.Float(), nullable=False), + sa.Column('roof_window', sa.Boolean(), nullable=False), + sa.Column('orientation', sqlmodel.sql.sqltypes.AutoString(), nullable=False), + sa.Column('u_value_w_m2_k', sa.Integer(), nullable=False), + sa.Column('g_value', sa.Integer(), nullable=False), + sa.Column('property_detail_id', sa.Uuid(), nullable=True), + sa.ForeignKeyConstraint(['property_detail_id'], ['propertydetail.id'], ), + sa.PrimaryKeyConstraint('id') + ) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_table('windows') + op.drop_table('propertydescription') + op.drop_table('presitenote') + op.drop_table('dimension') + op.drop_table('propertydetail') + op.drop_table('heating') + op.drop_table('assessorinfo') + op.drop_table('windturbine') + op.drop_table('waterheating') + op.drop_table('walls') + op.drop_table('ventilationandcooling') + op.drop_table('solarwaterheating') + op.drop_table('showerandbaths') + op.drop_table('roofs') + op.drop_table('presitenotessummaryinfo') + op.drop_table('photovoltaicpanel') + op.drop_table('otherdetails') + op.drop_table('lighting') + op.drop_table('insulation') + op.drop_table('hotwatercylinder') + op.drop_table('heatingtype') + op.drop_table('heatingsystemcontrols') + op.drop_table('fluegasheatrecoverysystem') + op.drop_table('floors') + op.drop_table('door') + op.drop_table('companyinfo') + # ### end Alembic commands ### diff --git a/alembic/versions/e351a98aba54_init_some_tables.py b/alembic/versions/e351a98aba54_init_some_tables.py deleted file mode 100644 index 43be0da..0000000 --- a/alembic/versions/e351a98aba54_init_some_tables.py +++ /dev/null @@ -1,117 +0,0 @@ -"""init some tables - -Revision ID: e351a98aba54 -Revises: -Create Date: 2025-05-08 06:52:41.139547 - -""" -from typing import Sequence, Union - -from alembic import op -import sqlalchemy as sa -import sqlmodel - - -# revision identifiers, used by Alembic. -revision: str = 'e351a98aba54' -down_revision: Union[str, None] = None -branch_labels: Union[str, Sequence[str], None] = None -depends_on: Union[str, Sequence[str], None] = None - - -def upgrade() -> None: - """Upgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.create_table('companyinfo', - sa.Column('id', sa.Uuid(), nullable=False), - sa.Column('trading_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('post_code', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('fax_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column('related_party_disclosure', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('heatingsystemcontrols', - sa.Column('id', sa.Uuid(), nullable=False), - sa.Column('control_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('flue_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('fan_assisted_flue', sa.Boolean(), nullable=False), - sa.Column('heat_emitter_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('electricity_meter_type', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column('mains_gas_available', sa.Boolean(), nullable=True), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('heatingtype', - sa.Column('id', sa.Uuid(), nullable=False), - sa.Column('heating_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('fuel_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('presitenotessummaryinfo', - sa.Column('id', sa.Uuid(), nullable=False), - sa.Column('reference_number', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('epc_language', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('uprn', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column('postcode', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('region', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('address', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('town', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('county', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column('property_tenure', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('transaction_type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('inspection_date', sa.DateTime(), nullable=False), - sa.Column('current_sap', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('potential_sap', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('current_ei', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('potential_ei', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('current_annual_emissions', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('current_annual_emission_including_0925_multiplayer', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('current_annual_energy_costs', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('assessorinfo', - sa.Column('id', sa.Uuid(), nullable=False), - sa.Column('accreditation_number', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('phone_number', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column('email_address', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column('company_id', sa.Uuid(), nullable=False), - sa.ForeignKeyConstraint(['company_id'], ['companyinfo.id'], ), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('heating', - sa.Column('id', sa.Uuid(), nullable=False), - sa.Column('type', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('heating_source', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('efficiency_source', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('heating_fuel', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('brand_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('model_name', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('model_qualifer', sqlmodel.sql.sqltypes.AutoString(), nullable=False), - sa.Column('sap_2009_table', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column('percentage_of_heated_floor_area_served', sqlmodel.sql.sqltypes.AutoString(), nullable=True), - sa.Column('controls_id', sa.Uuid(), nullable=False), - sa.ForeignKeyConstraint(['controls_id'], ['heatingsystemcontrols.id'], ), - sa.PrimaryKeyConstraint('id') - ) - op.create_table('presitenote', - sa.Column('id', sa.Uuid(), nullable=False), - sa.Column('summary_info_id', sa.Uuid(), nullable=False), - sa.Column('assessor_id', sa.Uuid(), nullable=False), - sa.ForeignKeyConstraint(['assessor_id'], ['assessorinfo.id'], ), - sa.ForeignKeyConstraint(['summary_info_id'], ['presitenotessummaryinfo.id'], ), - sa.PrimaryKeyConstraint('id') - ) - # ### end Alembic commands ### - - -def downgrade() -> None: - """Downgrade schema.""" - # ### commands auto generated by Alembic - please adjust! ### - op.drop_table('presitenote') - op.drop_table('heating') - op.drop_table('assessorinfo') - op.drop_table('presitenotessummaryinfo') - op.drop_table('heatingtype') - op.drop_table('heatingsystemcontrols') - op.drop_table('companyinfo') - # ### end Alembic commands ### diff --git a/etl/load/preSiteNoteTypes.py b/etl/load/preSiteNoteTypes.py index 5ce4314..9a85916 100644 --- a/etl/load/preSiteNoteTypes.py +++ b/etl/load/preSiteNoteTypes.py @@ -8,11 +8,27 @@ from sqlalchemy.dialects.postgresql import UUID class BaseModel(SQLModel): - id: uuid.UUID = Field( - default_factory=uuid.uuid4, - sa_column=Column(UUID(as_uuid=True), primary_key=True) + id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) + +# One class to rule them all +class PreSiteNote(BaseModel, table=True): + # Summary Info + summary_info_id: uuid.UUID = Field( + foreign_key="presitenotessummaryinfo.id", + nullable=False ) + summary_info: Optional["PreSiteNotesSummaryInfo"] = Relationship(back_populates="pre_site_notes") + + + # Assessor Info + assessor_id: uuid.UUID = Field( + foreign_key="assessorinfo.id", + nullable=False + ) + + assessor: Optional["AssessorInfo"] = Relationship(back_populates="pre_site_notes") + class Dimension(BaseModel, table=True): floor_area_m2: float @@ -20,6 +36,7 @@ class Dimension(BaseModel, table=True): loss_perimeter_m: float party_wall_length_m: float property_detail_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id") + property_detail: Optional["PropertyDetail"] = Relationship(back_populates="dimensions") class Walls(BaseModel, table=True): @@ -57,6 +74,8 @@ class Windows(BaseModel, table=True): u_value_w_m2_k: int g_value: int property_detail_id: Optional[uuid.UUID] = Field(default=None, foreign_key="propertydetail.id") + property_detail: Optional["PropertyDetail"] = Relationship(back_populates="windows") + class PropertyDetail(BaseModel, table=True): @@ -69,8 +88,6 @@ class PropertyDetail(BaseModel, table=True): windows: List[Windows] = Relationship(back_populates="property_detail") -Dimension.property_detail = Relationship(back_populates="dimensions") -Windows.property_detail = Relationship(back_populates="windows") class Door(BaseModel, table=True): @@ -201,8 +218,14 @@ class AssessorInfo(BaseModel, table=True): phone_number: Optional[str] = None email_address: Optional[EmailStr] = None + company_id: Optional[uuid.UUID] = Field(default=None, foreign_key="companyinfo.id") + company: Optional["CompanyInfo"] = Relationship(back_populates="assessors") -class SurverySummaryInfo(BaseModel, table=True): + pre_site_notes: List[PreSiteNote] = Relationship(back_populates="assessor") + + + +class PreSiteNotesSummaryInfo(BaseModel, table=True): reference_number: str epc_language: str uprn: Optional[str] = "" @@ -222,6 +245,7 @@ class SurverySummaryInfo(BaseModel, table=True): current_annual_emission_including_0925_multiplayer: str current_annual_energy_costs: str + pre_site_notes: List[PreSiteNote] = Relationship(back_populates="summary_info") class CompanyInfo(BaseModel, table=True): trading_name: str @@ -229,6 +253,13 @@ class CompanyInfo(BaseModel, table=True): fax_number: Optional[str] = None related_party_disclosure: Optional[str] = None + assessors: List[AssessorInfo] = Relationship(back_populates="company") + + class Insulation(BaseModel, table=True): type: str + + + +PreSiteNote.update_forward_refs() diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 44eda12..3a54544 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -1,9 +1,10 @@ from etl.pdfReader.pdfReaderToText import pdfReaderToText from etl.pdfReader.reportType import ReportType import math -from etl.transform.preSiteNoteTypes import ( +from etl.load.preSiteNoteTypes import ( AssessorInfo, CompanyInfo, - PreSiteNotesSummaryInfo, PreSiteNote, + PreSiteNotesSummaryInfo, + PreSiteNote, PropertyDescription, Dimension, HeatingType, Heating, HeatingSystemControls, OtherDetails, WindTurbine, PhotovoltaicPanel, FlueGasHeatRecoverySystem, ShowerAndBaths, SolarWaterHeating, HotWaterCylinder, WaterHeating, Lighting, VentilationAndCooling, @@ -64,7 +65,12 @@ class surveyedDataProcessor(): property_des = self.pre_site_note.property_description.model_dump() # Seconday Heating - secondary_heating = self.get_attribute_and_load(self.pre_site_note.property_description, "secondaryHeatingType", HeatingType, db_session) + secondary_heating = self.get_attribute_and_load( + self.pre_site_note.property_description, + "secondaryHeatingType", + HeatingType, + db_session + ) # main heating 2 and main heating 2 controls mainheating2 = None @@ -198,7 +204,19 @@ class surveyedDataProcessor(): roof = self.get_attribute_and_load(obj, "roof", Roofs, db_session) floor = None if check_if_attribute_exists(obj, "floor"): - roof = self.get_attribute_and_load(obj, "floor", Floors, db_session) + floor = self.get_attribute_and_load(obj, "floor", Floors, db_session) + + property_detail = self.upsert_record( + db_session=db_session, + model_class=PropertyDetail, + data_dict={ + "age_band": obj.age_band, + "floor_id": floor.id if floor else None, + "roof_id": roof.id if roof else None, + "wall_id": wall.id if wall else None, + }, + lookup_field=None, + ) dimensions = [] if check_if_attribute_exists(obj, "dimensions"): @@ -210,6 +228,7 @@ class surveyedDataProcessor(): model_class=Dimension, data_dict=data, lookup_field=None, + additional_fields={"property_detail_id": property_detail.id}, ) dimensions.append(dimension.id) @@ -223,22 +242,9 @@ class surveyedDataProcessor(): model_class=Windows, data_dict=data, lookup_field=None, + additional_fields={"property_detail_id": property_detail.id}, ) - windows.append(windows.id) - - property_detail = self.upsert_record( - db_session=db_session, - model_class=PropertyDetail, - data = obj.model_dump(), - additional_fields={ - "windows_id": windows, - "dimensions_id": dimensions, - "floors_id": floor.id if floor else None, - "roof_id": roof.id if roof else None, - "wall_id": wall.id if wall else None, - - } - ) + windows.append(window.id) return property_detail @@ -284,20 +290,6 @@ class surveyedDataProcessor(): update_if_exists: bool = False, additional_fields: dict = None ): - """ - Upserts a SQLAlchemy model instance based on a lookup field. - - Args: - db_session: SQLAlchemy session. - model_class: SQLAlchemy model/table class. - data_dict: Dictionary of data for creating the model. - lookup_field: Unique identifier field name (str). - update_if_exists: Whether to update existing record or not. - additional_fields: Extra fields to inject (e.g., foreign keys). - - Returns: - SQLAlchemy model instance. - """ clean_data = data_dict # Merge additional fields if provided @@ -324,6 +316,7 @@ class surveyedDataProcessor(): db_session.commit() return new_record + def load_assessor_table(self, db_session): company = self.load_company_table(db_session) @@ -456,6 +449,4 @@ class surveyedDataProcessor(): def get_current_sap_score(self): score_list = self.pre_site_note.survey_information.current_sap.split(" ") score = int(score_list[1]) - return score - - + return score \ No newline at end of file diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index 7e25682..3b9df77 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -7,10 +7,7 @@ from sqlalchemy import Column from sqlalchemy.dialects.postgresql import UUID class BaseModel(SQLModel): - id: uuid.UUID = Field( - default_factory=uuid.uuid4, - sa_column=Column(UUID(as_uuid=True), primary_key=True) - ) + pass class Dimension(BaseModel): floor_area_m2: float @@ -30,7 +27,7 @@ class CompanyInfo(BaseModel): return None return v -class SurverySummaryInfo(BaseModel): +class PreSiteNotesSummaryInfo(BaseModel): reference_number: str epc_language: str uprn: Optional[str] = "" @@ -79,7 +76,7 @@ class Door(BaseModel): no_of_insulated_doors: int u_value_w_m2_k: Optional[str] -class AssessorInfo(BaseModel, table=True): +class AssessorInfo(BaseModel): accreditation_number: str name: str phone_number: Optional[str] = None diff --git a/migration_db.sh b/migration_db.sh index d029b60..deabd64 100644 --- a/migration_db.sh +++ b/migration_db.sh @@ -1,2 +1,3 @@ -#poetry run alembic revision --autogenerate -m "init some tables" +#poetry run alembic revision --autogenerate -m "Initial migration" + poetry run alembic upgrade head From 24bae95458522caaf8d068ec256002d9ee0a608c Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Mon, 12 May 2025 16:28:42 +0000 Subject: [PATCH 28/33] save work --- .vscode/settings.json | 2 +- etl/db/hubSpotLoad.py | 8 +-- etl/load/preSiteNoteTypes.py | 81 +++++++++++++++++++++++++++++-- etl/surveyedData/surveryedData.py | 72 +++++++++++++++++++++++++-- migration_db.sh | 2 +- 5 files changed, 153 insertions(+), 12 deletions(-) diff --git a/.vscode/settings.json b/.vscode/settings.json index 9158a79..ea20d57 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -7,7 +7,7 @@ "bash": { "path": "/bin/bash" } - } + }, // Hot reload setting that needs to be in user settings // "jupyter.runStartupCommands": [ diff --git a/etl/db/hubSpotLoad.py b/etl/db/hubSpotLoad.py index ed21b8a..a8f0680 100644 --- a/etl/db/hubSpotLoad.py +++ b/etl/db/hubSpotLoad.py @@ -97,7 +97,9 @@ class HubspotTodb(): # Loads the pre site summary information summary_info = surveyedData.load_pre_site_notes_summary_table(db_session) - # Creates the a final pre site note table that links all information - presitenote = surveyedData.create_pre_site_note_table(db_session, assessor, summary_info) + property_description = surveyedData.load_property_description(db_session) + + # Creates the a final pre site note table that links all information + presitenote = surveyedData.create_pre_site_note_table(db_session, assessor, summary_info, property_description) + - surveyedData.load_property_description(db_session) \ No newline at end of file diff --git a/etl/load/preSiteNoteTypes.py b/etl/load/preSiteNoteTypes.py index 9a85916..87cffe5 100644 --- a/etl/load/preSiteNoteTypes.py +++ b/etl/load/preSiteNoteTypes.py @@ -10,9 +10,9 @@ from sqlalchemy.dialects.postgresql import UUID class BaseModel(SQLModel): id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) -# One class to rule them all + class PreSiteNote(BaseModel, table=True): - # Summary Info +# One class to rule them all summary_info_id: uuid.UUID = Field( foreign_key="presitenotessummaryinfo.id", nullable=False @@ -29,6 +29,13 @@ class PreSiteNote(BaseModel, table=True): assessor: Optional["AssessorInfo"] = Relationship(back_populates="pre_site_notes") + pre_site_note_description_id: uuid.UUID = Field( + foreign_key="propertydescription.id", + nullable=True + ) + + pre_site_note_description: Optional["PropertyDescription"] = Relationship(back_populates="pre_site_notes") + class Dimension(BaseModel, table=True): floor_area_m2: float @@ -84,6 +91,7 @@ class PropertyDetail(BaseModel, table=True): roof_id: Optional[uuid.UUID] = Field(default=None, foreign_key="roofs.id") floor_id: Optional[uuid.UUID] = Field(default=None, foreign_key="floors.id") + # Relationships dimensions: List[Dimension] = Relationship(back_populates="property_detail") windows: List[Windows] = Relationship(back_populates="property_detail") @@ -95,17 +103,24 @@ class Door(BaseModel, table=True): no_of_insulated_doors: int u_value_w_m2_k: Optional[str] + property_description: Optional["PropertyDescription"] = Relationship(back_populates="door") class VentilationAndCooling(BaseModel, table=True): no_of_open_fireplaces: int ventilation_type: str space_cooling_system_present: bool + property_description: Optional["PropertyDescription"] = Relationship(back_populates="ventilation_and_cooling") + + class Lighting(BaseModel, table=True): total_no_of_light_fittings: int total_no_of_lel_fittings: int + property_description: Optional["PropertyDescription"] = Relationship(back_populates="lighting") + + class HeatingSystemControls(BaseModel, table=True): control_type: str @@ -128,16 +143,29 @@ class Heating(BaseModel, table=True): percentage_of_heated_floor_area_served: Optional[str] = "" controls_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingsystemcontrols.id") + property_description: Optional["PropertyDescription"] = Relationship( + back_populates="main_heating", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating_id]"} + ) + property_description2: Optional["PropertyDescription"] = Relationship( + back_populates="main_heating2", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating2_id]"} + ) + + class HeatingType(BaseModel, table=True): heating_type: str fuel_type: str + property_description: Optional["PropertyDescription"] = Relationship(back_populates="secondary_heating_type") + class WaterHeating(BaseModel, table=True): heating_type: str fuel_type: str + property_description: Optional["PropertyDescription"] = Relationship(back_populates="water_heating") + + class HotWaterCylinder(BaseModel, table=True): volume: str @@ -145,34 +173,55 @@ class HotWaterCylinder(BaseModel, table=True): insulation_thickness: str thermostat: bool + property_description: Optional["PropertyDescription"] = Relationship(back_populates="hot_water_cylinder") + + class SolarWaterHeating(BaseModel, table=True): solar_water_heating_details_known: bool + property_description: Optional["PropertyDescription"] = Relationship(back_populates="solar_water_heating") + + class ShowerAndBaths(BaseModel, table=True): no_of_rooms_with_baths_and_or_shower: int no_of_rooms_with_mixer_shower_and_no_baths: int no_of_rooms_with_mixer_shower_and_baths: int + property_description: Optional["PropertyDescription"] = Relationship(back_populates="shower_and_baths") + + class FlueGasHeatRecoverySystem(BaseModel, table=True): fghrs_present: bool + property_description: Optional["PropertyDescription"] = Relationship(back_populates="flue_gas_heat_recovery_system") + + class PhotovoltaicPanel(BaseModel, table=True): pvs_are_connected_to_dwelling_electricity_meter: bool percentage_of_external_roof_area_with_pvs: str + property_description: Optional["PropertyDescription"] = Relationship(back_populates="photovoltaic_panel") + + class WindTurbine(BaseModel, table=True): wind_turbine: bool + property_description: Optional["PropertyDescription"] = Relationship(back_populates="wind_turbine") + + class OtherDetails(BaseModel, table=True): electricity_meter_type: str main_gas_avalible: bool + property_description: Optional["PropertyDescription"] = Relationship(back_populates="other_details") + + class PropertyDescription(BaseModel, table=True): built_form: str @@ -211,6 +260,30 @@ class PropertyDescription(BaseModel, table=True): main_heating2_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heating.id") secondary_heating_type_id: Optional[uuid.UUID] = Field(default=None, foreign_key="heatingtype.id") + # Relationships + main_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_property_id]"}) + ex1_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex1_property_id]"}) + ex2_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex2_property_id]"}) + ex3_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex3_property_id]"}) + ex4_property: Optional["PropertyDetail"] = Relationship(sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.ex4_property_id]"}) + + # Related Models + door: Optional["Door"] = Relationship(back_populates="property_description") + ventilation_and_cooling: Optional["VentilationAndCooling"] = Relationship(back_populates="property_description") + lighting: Optional["Lighting"] = Relationship(back_populates="property_description") + water_heating: Optional["WaterHeating"] = Relationship(back_populates="property_description") + hot_water_cylinder: Optional["HotWaterCylinder"] = Relationship(back_populates="property_description") + solar_water_heating: Optional["SolarWaterHeating"] = Relationship(back_populates="property_description") + shower_and_baths: Optional["ShowerAndBaths"] = Relationship(back_populates="property_description") + flue_gas_heat_recovery_system: Optional["FlueGasHeatRecoverySystem"] = Relationship(back_populates="property_description") + photovoltaic_panel: Optional["PhotovoltaicPanel"] = Relationship(back_populates="property_description") + wind_turbine: Optional["WindTurbine"] = Relationship(back_populates="property_description") + other_details: Optional["OtherDetails"] = Relationship(back_populates="property_description") + main_heating: Optional["Heating"] = Relationship(back_populates="property_description", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating_id]"}) + main_heating2: Optional["Heating"] = Relationship(back_populates="property_description", sa_relationship_kwargs={"foreign_keys": "[PropertyDescription.main_heating2_id]"}) + secondary_heating_type: Optional["HeatingType"] = Relationship(back_populates="property_description") + + pre_site_notes: Optional["PreSiteNote"] = Relationship(back_populates="pre_site_note_description") class AssessorInfo(BaseModel, table=True): accreditation_number: str @@ -221,7 +294,7 @@ class AssessorInfo(BaseModel, table=True): company_id: Optional[uuid.UUID] = Field(default=None, foreign_key="companyinfo.id") company: Optional["CompanyInfo"] = Relationship(back_populates="assessors") - pre_site_notes: List[PreSiteNote] = Relationship(back_populates="assessor") + pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="assessor") @@ -245,7 +318,7 @@ class PreSiteNotesSummaryInfo(BaseModel, table=True): current_annual_emission_including_0925_multiplayer: str current_annual_energy_costs: str - pre_site_notes: List[PreSiteNote] = Relationship(back_populates="summary_info") + pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="summary_info") class CompanyInfo(BaseModel, table=True): trading_name: str diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 3a54544..29998ff 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -154,7 +154,7 @@ class surveyedDataProcessor(): ) # hotwatercycling - hot_water_cyclinder = self.get_attribute_and_load( + hot_water_cylinder = self.get_attribute_and_load( self.pre_site_note.property_description, "hotWaterCylinder", HotWaterCylinder, @@ -250,7 +250,57 @@ class surveyedDataProcessor(): # main_property main_property = upload_property_detail("main_property") - print(main_property.id) + ex1_property = upload_property_detail("ex1_property") + ex2_property = upload_property_detail("ex2_property") + ex3_property = upload_property_detail("ex3_property") + ex4_property = upload_property_detail("ex4_property") + + data = self.pre_site_note.property_description.model_dump() + def remove_dicts_and_lists(data): + if isinstance(data, dict): + # Create a new dict with only primitive types (ignore dicts/lists) + return { + k: remove_dicts_and_lists(v) + for k, v in data.items() + if not isinstance(v, (dict, list)) + } + elif isinstance(data, list): + # Remove lists entirely + return None + else: + return data + data = remove_dicts_and_lists(data) + property_description = self.upsert_record( + db_session=db_session, + model_class=PropertyDescription, + data_dict=data, + lookup_field=None, + additional_fields={ + "main_heating_id": mainheating.id if mainheating else None, + "main_heating_controls_id": mainheatingcontrols.id if mainheatingcontrols else None, + "main_heating2_id": mainheating2.id if mainheating2 else None, + "main_heating2_controls_id": mainheating2controls.id if mainheating2controls else None, + "secondary_heating_type_id": secondary_heating.id if secondary_heating else None, + "other_details_id": otherDetails.id if otherDetails else None, + "wind_turbine_id": windTurbine.id if windTurbine else None, + "photovoltaic_panel_id": photo_volatic_panel.id if photo_volatic_panel else None, + "flue_gas_heat_recovery_system_id": flue_gas_heat_recovery_system.id if flue_gas_heat_recovery_system else None, + "shower_and_baths_id": shower_and_baths.id if shower_and_baths else None, + "solar_water_heating_id": solar_water_heating.id if solar_water_heating else None, + "hot_water_cylinder_id": hot_water_cylinder.id if hot_water_cylinder else None, + "water_heating_id": water_heating.id if water_heating else None, + "lighting_id": lighting.id if lighting else None, + "ventilation_and_cooling_id": ventilation_and_cooling.id if ventilation_and_cooling else None, + "door_id": door.id if door else None, + "main_property_id": main_property.id if main_property else None, + "ex1_property_id": ex1_property.id if ex1_property else None, + "ex2_property_id": ex2_property.id if ex2_property else None, + "ex3_property_id": ex3_property.id if ex3_property else None, + "ex4_property_id": ex4_property.id if ex4_property else None, + } + ) + print(f"property_description {property_description}") + return property_description @@ -268,11 +318,13 @@ class surveyedDataProcessor(): self, db_session, assessor, - summary_info + summary_info, + pre_site_note_description, ): preSiteNote = PreSiteNote( summary_info_id=summary_info.id, assessor_id=assessor.id, + pre_site_note_description_id=pre_site_note_description.id, ) db_session.add(preSiteNote) db_session.commit() @@ -301,20 +353,34 @@ class surveyedDataProcessor(): if not lookup_value: raise ValueError(f"Missing lookup field '{lookup_field}' in data.") + # Try to find existing record existing_record = db_session.query(model_class).filter( getattr(model_class, lookup_field) == lookup_value ).first() if existing_record: + # Update existing record if update_if_exists is True if update_if_exists: for key, value in clean_data.items(): setattr(existing_record, key, value) db_session.commit() return existing_record + + # Filter out invalid fields that don't exist in the model class + valid_fields = [field for field in clean_data if hasattr(model_class, field)] + clean_data = {field: clean_data[field] for field in valid_fields} + + print(f'clean data is {clean_data}') + + # Handle Pydantic models (with model_validate or parse_obj) new_record = model_class(**clean_data) + + # Add the new record to the session and commit db_session.add(new_record) db_session.commit() + return new_record + diff --git a/migration_db.sh b/migration_db.sh index deabd64..059baa9 100644 --- a/migration_db.sh +++ b/migration_db.sh @@ -1,3 +1,3 @@ -#poetry run alembic revision --autogenerate -m "Initial migration" +#poetry run alembic revision --autogenerate -m "mistake on foreign key" poetry run alembic upgrade head From ff757a187b5da81fa1d96681fe81b021bf2e0c3b Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Wed, 14 May 2025 14:59:08 +0000 Subject: [PATCH 29/33] db migration and hubspot deal@ --- alembic/env.py | 1 + etl/db/hubSpotLoad.py | 15 +- etl/hubSpotClient/hubspot.py | 54 ++ etl/hubSpotClient/types.py | 5 +- etl/hubspot_to_invoice.py | 54 +- etl/load/preSiteNoteTypes.py | 10 +- etl/load/topLevel.py | 37 + etl/osmosis_google_maps_.py | 2 +- etl/pdfReader/reportType.py | 10 +- etl/pdfReader/sitenotes.py | 1 + etl/surveyPrice/surveyPrice.py | 3 + etl/surveyedData/surveryedData.py | 59 +- etl/transform/preSiteNoteTypes.py | 4 +- migration_db.sh | 3 +- poetry.lock | 1065 ++++++++++++++--------------- pyproject.toml | 2 +- 16 files changed, 724 insertions(+), 601 deletions(-) create mode 100644 etl/load/topLevel.py diff --git a/alembic/env.py b/alembic/env.py index c8174a7..fcf84b0 100644 --- a/alembic/env.py +++ b/alembic/env.py @@ -6,6 +6,7 @@ from alembic import context from sqlmodel import SQLModel from etl.load.preSiteNoteTypes import * +from etl.load.topLevel import * import os diff --git a/etl/db/hubSpotLoad.py b/etl/db/hubSpotLoad.py index a8f0680..a893245 100644 --- a/etl/db/hubSpotLoad.py +++ b/etl/db/hubSpotLoad.py @@ -81,7 +81,10 @@ class HubspotTodb(): data_loc = self.create_files_locally(sp, path, row["HUBSPOT_DEAL_ADDRESS"]) for add, file_loc in data_loc.items(): - self.data_in_sharepoint.append(surveyedDataProcessor(add, file_loc)) + sdp = surveyedDataProcessor(add, file_loc) + sdp.hubspot_deal_id = row["HUBSPOT_DEAL_ID"] + self.data_in_sharepoint.append(sdp) + def load_all(self, fast=False): if fast is False: @@ -102,4 +105,14 @@ class HubspotTodb(): # Creates the a final pre site note table that links all information presitenote = surveyedData.create_pre_site_note_table(db_session, assessor, summary_info, property_description) + df = self.deals_in_hubspot + df = df[df["HUBSPOT_DEAL_ID"] == str(surveyedData.hubspot_deal_id)] + building_table = surveyedData.create_buildings_table( + db_session, + df["HUBSPOT_LANDLORD_ID"].values[0], + df["HUBSPOT_DOMNA_ID"].values[0], + ) + documents = surveyedData.create_document_table_via_pre_site_note(db_session, presitenote, assessor, building_table) + # Create building table or find building table to add new pre_site_note + diff --git a/etl/hubSpotClient/hubspot.py b/etl/hubSpotClient/hubspot.py index eb982fa..0866d24 100644 --- a/etl/hubSpotClient/hubspot.py +++ b/etl/hubSpotClient/hubspot.py @@ -35,7 +35,45 @@ class HubSpotClient(): return deal.properties.get("dealname", "No deal name") except Exception as e: return "Unknown Deal" # Fallback if the deal name is not found + + def get_listings_from_deals_id(self, deals_id): + from hubspot.crm.objects import PublicObjectSearchRequest + found_notes = [] + after = None + while True: + # Correct filter for notes associated with the given deal ID + search_request = PublicObjectSearchRequest( + filter_groups=[{ + "filters": [{ + "propertyName": "associations.deal", # Filter by association to the deal + "operator": "EQ", + "value": deals_id, + }] + }], + properties=["domna_property_id", "owner_property_id", 'national_uprn'], # Properties of the note you need + limit=200, + after=after, + ) + # Call the search API + response = self.client.crm.objects.search_api.do_search(object_type="0-420", public_object_search_request=search_request) + time.sleep(1) + + # Add the results to the found_notes list + found_notes.extend(response.results) + + # Handle pagination if more results are available + if not response.paging or not response.paging.next: + break + after = response.paging.next.after + + if found_notes: + return found_notes[0] + return None + def get_domna_and_landlord_id(self, deals_id): + data = self.get_listings_from_deals_id(deals_id) + return data.properties['domna_property_id'], data.properties['owner_property_id'], data.properties['national_uprn'] + def get_notes_from_deals_id(self, deals_id): from hubspot.crm.objects import PublicObjectSearchRequest found_notes = [] @@ -115,6 +153,17 @@ class HubSpotClient(): "deal_owner": deal.properties.get("hubspot_owner_id"), }) return all_deals + + def get_associations_for_deal(self, deal_id, to_object_type): + """ + Returns a list of associated object IDs of type `to_object_type` + (e.g. "contacts", "companies", "notes", etc.) + """ + assoc_resp = self.client.crm.deals.associations_api.get_all( + deal_id=deal_id, + to_object_type=to_object_type + ) + return [assoc.id for assoc in assoc_resp.results] def get_deals_from_deal_stage(self, deal_stage: DealStage): found_deals = [] @@ -149,6 +198,7 @@ class HubSpotClient(): all_deals = [] for deal in found_deals: + domna_id, landlord_id, uprn = self.get_domna_and_landlord_id(deal.id) all_deals.append(SubmissionInfoFromDeal( deal_id= deal.properties["hs_object_id"], deal_name=deal.properties["dealname"], @@ -159,7 +209,11 @@ class HubSpotClient(): no_of_wet_rooms=int(deal.properties["number_of_wet_rooms_needing_ventilation"]), installer=deal.properties["installer"], submission_folder_path = deal.properties["submission_folder"], + landlord_id = landlord_id, + domna_id = domna_id, + uprn = uprn, )) + return all_deals def print_all_pipeline_ids(self): diff --git a/etl/hubSpotClient/types.py b/etl/hubSpotClient/types.py index 7d069df..f95603f 100644 --- a/etl/hubSpotClient/types.py +++ b/etl/hubSpotClient/types.py @@ -19,4 +19,7 @@ class SubmissionInfoFromDeal(BaseModel): existing_wall_insulation: str no_of_wet_rooms: int installer: str - submission_folder_path: str \ No newline at end of file + submission_folder_path: str + landlord_id: str + domna_id: str + uprn: str \ No newline at end of file diff --git a/etl/hubspot_to_invoice.py b/etl/hubspot_to_invoice.py index c243fac..6ecbd4a 100644 --- a/etl/hubspot_to_invoice.py +++ b/etl/hubspot_to_invoice.py @@ -6,9 +6,13 @@ os.environ["SHAREPOINT_CLIENT_SECRET"] = "SOf8Q~-is4wdQiqvEEm9FlJQRAY9ELGaj5Qz-a os.environ["SHAREPOINT_TENANT_ID"] = "c3f7519c-2719-4547-af04-6da6cbfd8f8f" os.environ["SOUTH_COAST_INSULATION_SERVICE_SHAREPOINT_ID"] = "b5a51507-9427-4ee0-b03e-90ec7681e2d3" os.environ["JJC_SERVICE_SHAREPOINT_ID"] = "7fdd0485-bbf3-4b29-b30f-98c81c2a6284" -os.environ["DATABASE_URL"] = "postgresql://postgres:makingwarmhomes@db:5432/postgres" +# Local development +# os.environ["DATABASE_URL"] = "postgresql://postgres:makingwarmhomes@db:5432/postgres" from etl.surveyPrice.surveyPrice import SurveyPrice +from etl.db.hubSpotLoad import HubspotTodb + + sp = SurveyPrice() @@ -32,48 +36,10 @@ sp.upload_to_sharepoint(sp.get_master_rate_card_path(), "COPY_OF_RATE_CARD_USED. deal_ids = df["HUBSPOT_DEAL_ID"].tolist() + +# Load to db +dbLoader = HubspotTodb() +dbLoader.load_all() + # Commented out as i don't want to sync up hubspot_to_db just yet sp.move_deals_to_completed(deal_ids) - -""" - TODO: - Tuesday - P1) - Get read for demo, 3 examples of solar ( JJC AND SCIS), 3 examples of cavity wall ( SCIS and JJC) 12 in total - P2) Review deem score with last weeks deem score values to ensure accuracy - - - P3) Figure out what to do if I see an address that isn't registered but surveyrod - P3) Write documentation for tech demos from Khalims demo - Handed off to cyrus -""" - -# Look for -# JJC - -# 3 examples of Solar -# No solar example in april deem scroe -# 3 examples Cavity Wall, FOAM, Empty and General ideally -# (in hubspot )111 Duddell Road General ( fibre) - 500, 2 wet rooms -# Empty -# ( in hubspot ) 29 Lower King ( empty ) - 500 - 400 -# Foam -# ( in hubspot ) 6 STOKESAY STREET (foam) - 400 - -# SCIS -# 3 examples of Solar -# ( in hubspot ) 12 short hedges - Solar 1608 -# ( in hubspot ) 18 short hedge - Solar 1608 -# ( in hubspot) 6 forety road -Solar 1608 - -# 3 examples Cavity Wall, FOAM, Empty and General ideally -# ( in hubspot ) 319 Muirfield Road, (Empty Cavity) - 1000 -# ( hubspot ) 2 queensway, (Fibre) - 500 -# ( in hubspot )56 Aughton Crescent -(foam) - To be worked out by Lewis but lets use this as an oppurtunity - - -# Compare value with what I should get and in the deem score. Keep tabs below so I can check easily - -# Change w.c. date to a weird one to speed up automation - - -# Observation: - - diff --git a/etl/load/preSiteNoteTypes.py b/etl/load/preSiteNoteTypes.py index 87cffe5..054dda4 100644 --- a/etl/load/preSiteNoteTypes.py +++ b/etl/load/preSiteNoteTypes.py @@ -5,14 +5,10 @@ from datetime import datetime from pydantic import EmailStr from sqlalchemy import Column from sqlalchemy.dialects.postgresql import UUID - - -class BaseModel(SQLModel): - id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) +from etl.load.topLevel import BaseModel, Documents class PreSiteNote(BaseModel, table=True): -# One class to rule them all summary_info_id: uuid.UUID = Field( foreign_key="presitenotessummaryinfo.id", nullable=False @@ -295,7 +291,7 @@ class AssessorInfo(BaseModel, table=True): company: Optional["CompanyInfo"] = Relationship(back_populates="assessors") pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="assessor") - + documents: List["Documents"] = Relationship(back_populates="author") class PreSiteNotesSummaryInfo(BaseModel, table=True): @@ -321,6 +317,7 @@ class PreSiteNotesSummaryInfo(BaseModel, table=True): pre_site_notes: List["PreSiteNote"] = Relationship(back_populates="summary_info") class CompanyInfo(BaseModel, table=True): + address: str trading_name: str post_code: str fax_number: Optional[str] = None @@ -336,3 +333,4 @@ class Insulation(BaseModel, table=True): PreSiteNote.update_forward_refs() +AssessorInfo.update_forward_refs() diff --git a/etl/load/topLevel.py b/etl/load/topLevel.py new file mode 100644 index 0000000..610c2e9 --- /dev/null +++ b/etl/load/topLevel.py @@ -0,0 +1,37 @@ +from sqlmodel import Field, SQLModel, Relationship +import uuid +from typing import Optional, List +from datetime import datetime +from pydantic import EmailStr +from sqlalchemy import Column +from sqlalchemy.dialects.postgresql import UUID +from etl.pdfReader.reportType import ReportType + +class BaseModel(SQLModel): + id: uuid.UUID = Field(default_factory=uuid.uuid4, primary_key=True) + +class Buildings(BaseModel, table=True): + address: str + postcode: str + UPRN: str + landlord_id: str + domna_id: str + + documents: List["Documents"] = Relationship(back_populates="building") + +class Documents(BaseModel, table=True): + assessor_id: uuid.UUID = Field( + foreign_key="assessorinfo.id", + nullable=False + ) + author: Optional["AssessorInfo"] = Relationship(back_populates="documents") + created_at: datetime + document_type: ReportType + + building_id: uuid.UUID = Field(foreign_key="buildings.id", nullable=False) + building: Optional["Buildings"] = Relationship(back_populates="documents") + + target_table: str + target_id: uuid.UUID + +Documents.update_forward_refs() \ No newline at end of file diff --git a/etl/osmosis_google_maps_.py b/etl/osmosis_google_maps_.py index c8c2654..89ba01f 100644 --- a/etl/osmosis_google_maps_.py +++ b/etl/osmosis_google_maps_.py @@ -5,7 +5,7 @@ import time from tqdm import tqdm -board_id = "8829428746" +board_id = "3584401309" monday_key = "eyJhbGciOiJIUzI1NiJ9.eyJ0aWQiOjQ5ODc2ODQxOCwiYWFpIjoxMSwidWlkIjozNjE3ODAzNCwiaWFkIjoiMjAyNS0wNC0xMVQxMToyMzoxNy40NjdaIiwicGVyIjoibWU6d3JpdGUiLCJhY3RpZCI6MTM5OTc4MjMsInJnbiI6InVzZTEifQ.-2Lit4s46ZF6AXuMW9t0TxIaFLkHqD4Yo-PyM9i2XZY" monday = MondayClient(monday_key) diff --git a/etl/pdfReader/reportType.py b/etl/pdfReader/reportType.py index 25c5b22..c77ad9f 100644 --- a/etl/pdfReader/reportType.py +++ b/etl/pdfReader/reportType.py @@ -2,8 +2,8 @@ from enum import Enum class ReportType(Enum): - QUIDOS_PRESITE_NOTE = 1 - CHARTED_SURVEYOR_REPORT = 2 - ENERGY_PERFORMANCE_REPORT = 3 - U_VALUE_CALCULATOR_REPORT = 4 - OVERWRITING_U_VALUE_DECLARATION_FORM = 5 \ No newline at end of file + QUIDOS_PRESITE_NOTE = "quidos_presite_note" + CHARTED_SURVEYOR_REPORT = "charted_surveyor_report" + ENERGY_PERFORMANCE_REPORT = "energy_performance_report" + U_VALUE_CALCULATOR_REPORT = "u_value_calculator_report" + OVERWRITING_U_VALUE_DECLARATION_FORM = "overwriting_u_value_declaration_form" \ No newline at end of file diff --git a/etl/pdfReader/sitenotes.py b/etl/pdfReader/sitenotes.py index 43b17e8..67fe4a0 100644 --- a/etl/pdfReader/sitenotes.py +++ b/etl/pdfReader/sitenotes.py @@ -145,6 +145,7 @@ class QuidosSiteNotesExtractor(SiteNotesExtractor): self.company_information = CompanyInfo( + address=self.raw_data[self.get_x_occurance(self.raw_data,'Address', 1) + 1], trading_name = get_value('Company name/trading name'), post_code = get_value('POST CODE'), fax_number = get_value('Fax number'), diff --git a/etl/surveyPrice/surveyPrice.py b/etl/surveyPrice/surveyPrice.py index 4a5ffd2..408b42e 100644 --- a/etl/surveyPrice/surveyPrice.py +++ b/etl/surveyPrice/surveyPrice.py @@ -149,6 +149,9 @@ class SurveyPrice(): "HUBSPOT_INSTALLER": deal.installer, "HUBSPOT_WETROOMS": deal.no_of_wet_rooms, "HUBSPOT_SHAREPOINT_PATH": deal.submission_folder_path, + "HUBSPOT_LANDLORD_ID": deal.landlord_id, + "HUBSPOT_DOMNA_ID": deal.domna_id, + "HUBSPOT_UPRN": deal.uprn, }) self.all_hubspot_submissions = pd.DataFrame(all_deals) diff --git a/etl/surveyedData/surveryedData.py b/etl/surveyedData/surveryedData.py index 29998ff..427a9a4 100644 --- a/etl/surveyedData/surveryedData.py +++ b/etl/surveyedData/surveryedData.py @@ -10,6 +10,9 @@ from etl.load.preSiteNoteTypes import ( SolarWaterHeating, HotWaterCylinder, WaterHeating, Lighting, VentilationAndCooling, Door, Walls, Roofs, Floors, PropertyDetail, Windows ) +from etl.load.topLevel import( + Buildings, Documents +) import uuid class surveyedDataProcessor(): @@ -19,6 +22,7 @@ class surveyedDataProcessor(): self.pre_site_note = None self.csr = None self.identify_files() + self.hubspot_deal_id = None def identify_files(self): @@ -40,6 +44,20 @@ class surveyedDataProcessor(): lookup_field="reference_number" ) + def create_building_table(self, db_session): + return self.upsert_record( + db_session=db_session, + model_class=Buildings, + data_dict={ + "address":"foo", + "potcode": "foobar", + "UPRN": self.pre_site_note.survey_information.uprn, + "landlord_id": "landlord_id", + "domna_id": "landlord_id", + }, + lookup_field="UPRN", + ) + def get_attribute_and_load(self, obj, attr_string, pydanticModel, db_session): found = getattr(obj, attr_string, None) if found: @@ -299,7 +317,6 @@ class surveyedDataProcessor(): "ex4_property_id": ex4_property.id if ex4_property else None, } ) - print(f"property_description {property_description}") return property_description @@ -313,6 +330,46 @@ class surveyedDataProcessor(): lookup_field="trading_name" ) + + def create_document_table_via_pre_site_note(self, db_session, pre_site_note, assessor, building): + data = { + "assessor_id": assessor.id, + "created_at": self.pre_site_note.survey_information.inspection_date, + "document_type": ReportType.QUIDOS_PRESITE_NOTE, + "building_id": building.id, + "target_table": "pre_site_note", + "target_id": pre_site_note.id + } + return self.upsert_record( + db_session=db_session, + model_class=Documents, + data_dict=data, + lookup_field=None, + ) + + def create_buildings_table( + self, + db_session, + landlord_id, + domna_id, + ): + + data = { + "address": self.pre_site_note.survey_information.address, + "postcode": self.pre_site_note.survey_information.postcode, + "UPRN": self.pre_site_note.survey_information.uprn, + "landlord_id": landlord_id, + "domna_id": domna_id + } + building = self.upsert_record( + db_session=db_session, + model_class=Buildings, + data_dict=data, + lookup_field="UPRN", + ) + + return building + def create_pre_site_note_table( self, diff --git a/etl/transform/preSiteNoteTypes.py b/etl/transform/preSiteNoteTypes.py index 3b9df77..4a8b8fc 100644 --- a/etl/transform/preSiteNoteTypes.py +++ b/etl/transform/preSiteNoteTypes.py @@ -1,4 +1,4 @@ -from sqlmodel import Field, SQLModel +from sqlmodel import Field, SQLModel, Relationship import uuid from datetime import datetime from pydantic import field_validator, EmailStr @@ -16,6 +16,7 @@ class Dimension(BaseModel): party_wall_length_m: float class CompanyInfo(BaseModel): + address: str trading_name: str post_code: str fax_number: Optional[str] = None @@ -82,6 +83,7 @@ class AssessorInfo(BaseModel): phone_number: Optional[str] = None email_address: Optional[EmailStr] = None + class VentilationAndCooling(BaseModel): no_of_open_fireplaces: int ventilation_type: str diff --git a/migration_db.sh b/migration_db.sh index 059baa9..799701e 100644 --- a/migration_db.sh +++ b/migration_db.sh @@ -1,3 +1,4 @@ -#poetry run alembic revision --autogenerate -m "mistake on foreign key" +#poetry run alembic revision --autogenerate -m "Add address in company" + poetry run alembic upgrade head diff --git a/poetry.lock b/poetry.lock index 0f1cdd6..62d14e9 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,15 +1,15 @@ -# This file is automatically @generated by Poetry 2.1.2 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.1.3 and should not be changed by hand. [[package]] name = "alembic" -version = "1.15.1" +version = "1.15.2" description = "A database migration tool for SQLAlchemy." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "alembic-1.15.1-py3-none-any.whl", hash = "sha256:197de710da4b3e91cf66a826a5b31b5d59a127ab41bd0fc42863e2902ce2bbbe"}, - {file = "alembic-1.15.1.tar.gz", hash = "sha256:e1a1c738577bca1f27e68728c910cd389b9a92152ff91d902da649c192e30c49"}, + {file = "alembic-1.15.2-py3-none-any.whl", hash = "sha256:2e76bd916d547f6900ec4bb5a90aeac1485d2c92536923d0b138c02b126edc53"}, + {file = "alembic-1.15.2.tar.gz", hash = "sha256:1c72391bbdeffccfe317eefba686cb9a3c078005478885413b95c3b26c57a8a7"}, ] [package.dependencies] @@ -86,14 +86,14 @@ lxml = ["lxml"] [[package]] name = "certifi" -version = "2025.1.31" +version = "2025.4.26" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" groups = ["main"] files = [ - {file = "certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe"}, - {file = "certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651"}, + {file = "certifi-2025.4.26-py3-none-any.whl", hash = "sha256:30350364dfe371162649852c63336a15c70c6510c2ad5015b21c2345311805f3"}, + {file = "certifi-2025.4.26.tar.gz", hash = "sha256:0a816057ea3cdefcef70270d2c515e4506bbc954f417fa5ade2021213bb8f0c6"}, ] [[package]] @@ -179,104 +179,104 @@ pycparser = "*" [[package]] name = "charset-normalizer" -version = "3.4.1" +version = "3.4.2" description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "charset_normalizer-3.4.1-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:91b36a978b5ae0ee86c394f5a54d6ef44db1de0815eb43de826d41d21e4af3de"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7461baadb4dc00fd9e0acbe254e3d7d2112e7f92ced2adc96e54ef6501c5f176"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e218488cd232553829be0664c2292d3af2eeeb94b32bea483cf79ac6a694e037"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80ed5e856eb7f30115aaf94e4a08114ccc8813e6ed1b5efa74f9f82e8509858f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b010a7a4fd316c3c484d482922d13044979e78d1861f0e0650423144c616a46a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4532bff1b8421fd0a320463030c7520f56a79c9024a4e88f01c537316019005a"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:d973f03c0cb71c5ed99037b870f2be986c3c05e63622c017ea9816881d2dd247"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:3a3bd0dcd373514dcec91c411ddb9632c0d7d92aed7093b8c3bbb6d69ca74408"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:d9c3cdf5390dcd29aa8056d13e8e99526cda0305acc038b96b30352aff5ff2bb"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:2bdfe3ac2e1bbe5b59a1a63721eb3b95fc9b6817ae4a46debbb4e11f6232428d"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:eab677309cdb30d047996b36d34caeda1dc91149e4fdca0b1a039b3f79d9a807"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win32.whl", hash = "sha256:c0429126cf75e16c4f0ad00ee0eae4242dc652290f940152ca8c75c3a4b6ee8f"}, - {file = "charset_normalizer-3.4.1-cp310-cp310-win_amd64.whl", hash = "sha256:9f0b8b1c6d84c8034a44893aba5e767bf9c7a211e313a9605d9c617d7083829f"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:8bfa33f4f2672964266e940dd22a195989ba31669bd84629f05fab3ef4e2d125"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:28bf57629c75e810b6ae989f03c0828d64d6b26a5e205535585f96093e405ed1"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f08ff5e948271dc7e18a35641d2f11a4cd8dfd5634f55228b691e62b37125eb3"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:234ac59ea147c59ee4da87a0c0f098e9c8d169f4dc2a159ef720f1a61bbe27cd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fd4ec41f914fa74ad1b8304bbc634b3de73d2a0889bd32076342a573e0779e00"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:eea6ee1db730b3483adf394ea72f808b6e18cf3cb6454b4d86e04fa8c4327a12"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:c96836c97b1238e9c9e3fe90844c947d5afbf4f4c92762679acfe19927d81d77"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:4d86f7aff21ee58f26dcf5ae81a9addbd914115cdebcbb2217e4f0ed8982e146"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:09b5e6733cbd160dcc09589227187e242a30a49ca5cefa5a7edd3f9d19ed53fd"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:5777ee0881f9499ed0f71cc82cf873d9a0ca8af166dfa0af8ec4e675b7df48e6"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:237bdbe6159cff53b4f24f397d43c6336c6b0b42affbe857970cefbb620911c8"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win32.whl", hash = "sha256:8417cb1f36cc0bc7eaba8ccb0e04d55f0ee52df06df3ad55259b9a323555fc8b"}, - {file = "charset_normalizer-3.4.1-cp311-cp311-win_amd64.whl", hash = "sha256:d7f50a1f8c450f3925cb367d011448c39239bb3eb4117c36a6d354794de4ce76"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:73d94b58ec7fecbc7366247d3b0b10a21681004153238750bb67bd9012414545"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dad3e487649f498dd991eeb901125411559b22e8d7ab25d3aeb1af367df5efd7"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c30197aa96e8eed02200a83fba2657b4c3acd0f0aa4bdc9f6c1af8e8962e0757"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2369eea1ee4a7610a860d88f268eb39b95cb588acd7235e02fd5a5601773d4fa"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc2722592d8998c870fa4e290c2eec2c1569b87fe58618e67d38b4665dfa680d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ffc9202a29ab3920fa812879e95a9e78b2465fd10be7fcbd042899695d75e616"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:804a4d582ba6e5b747c625bf1255e6b1507465494a40a2130978bda7b932c90b"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:0f55e69f030f7163dffe9fd0752b32f070566451afe180f99dbeeb81f511ad8d"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:c4c3e6da02df6fa1410a7680bd3f63d4f710232d3139089536310d027950696a"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:5df196eb874dae23dcfb968c83d4f8fdccb333330fe1fc278ac5ceeb101003a9"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:e358e64305fe12299a08e08978f51fc21fac060dcfcddd95453eabe5b93ed0e1"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win32.whl", hash = "sha256:9b23ca7ef998bc739bf6ffc077c2116917eabcc901f88da1b9856b210ef63f35"}, - {file = "charset_normalizer-3.4.1-cp312-cp312-win_amd64.whl", hash = "sha256:6ff8a4a60c227ad87030d76e99cd1698345d4491638dfa6673027c48b3cd395f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:aabfa34badd18f1da5ec1bc2715cadc8dca465868a4e73a0173466b688f29dda"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22e14b5d70560b8dd51ec22863f370d1e595ac3d024cb8ad7d308b4cd95f8313"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8436c508b408b82d87dc5f62496973a1805cd46727c34440b0d29d8a2f50a6c9"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2d074908e1aecee37a7635990b2c6d504cd4766c7bc9fc86d63f9c09af3fa11b"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:955f8851919303c92343d2f66165294848d57e9bba6cf6e3625485a70a038d11"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:44ecbf16649486d4aebafeaa7ec4c9fed8b88101f4dd612dcaf65d5e815f837f"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:0924e81d3d5e70f8126529951dac65c1010cdf117bb75eb02dd12339b57749dd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:2967f74ad52c3b98de4c3b32e1a44e32975e008a9cd2a8cc8966d6a5218c5cb2"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:c75cb2a3e389853835e84a2d8fb2b81a10645b503eca9bcb98df6b5a43eb8886"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:09b26ae6b1abf0d27570633b2b078a2a20419c99d66fb2823173d73f188ce601"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:fa88b843d6e211393a37219e6a1c1df99d35e8fd90446f1118f4216e307e48cd"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win32.whl", hash = "sha256:eb8178fe3dba6450a3e024e95ac49ed3400e506fd4e9e5c32d30adda88cbd407"}, - {file = "charset_normalizer-3.4.1-cp313-cp313-win_amd64.whl", hash = "sha256:b1ac5992a838106edb89654e0aebfc24f5848ae2547d22c2c3f66454daa11971"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f30bf9fd9be89ecb2360c7d94a711f00c09b976258846efe40db3d05828e8089"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:97f68b8d6831127e4787ad15e6757232e14e12060bec17091b85eb1486b91d8d"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7974a0b5ecd505609e3b19742b60cee7aa2aa2fb3151bc917e6e2646d7667dcf"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fc54db6c8593ef7d4b2a331b58653356cf04f67c960f584edb7c3d8c97e8f39e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:311f30128d7d333eebd7896965bfcfbd0065f1716ec92bd5638d7748eb6f936a"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:7d053096f67cd1241601111b698f5cad775f97ab25d81567d3f59219b5f1adbd"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:807f52c1f798eef6cf26beb819eeb8819b1622ddfeef9d0977a8502d4db6d534"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:dccbe65bd2f7f7ec22c4ff99ed56faa1e9f785482b9bbd7c717e26fd723a1d1e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:2fb9bd477fdea8684f78791a6de97a953c51831ee2981f8e4f583ff3b9d9687e"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:01732659ba9b5b873fc117534143e4feefecf3b2078b0a6a2e925271bb6f4cfa"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win32.whl", hash = "sha256:7a4f97a081603d2050bfaffdefa5b02a9ec823f8348a572e39032caa8404a487"}, - {file = "charset_normalizer-3.4.1-cp37-cp37m-win_amd64.whl", hash = "sha256:7b1bef6280950ee6c177b326508f86cad7ad4dff12454483b51d8b7d673a2c5d"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:ecddf25bee22fe4fe3737a399d0d177d72bc22be6913acfab364b40bce1ba83c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8c60ca7339acd497a55b0ea5d506b2a2612afb2826560416f6894e8b5770d4a9"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b7b2d86dd06bfc2ade3312a83a5c364c7ec2e3498f8734282c6c3d4b07b346b8"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dd78cfcda14a1ef52584dbb008f7ac81c1328c0f58184bf9a84c49c605002da6"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6e27f48bcd0957c6d4cb9d6fa6b61d192d0b13d5ef563e5f2ae35feafc0d179c"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:01ad647cdd609225c5350561d084b42ddf732f4eeefe6e678765636791e78b9a"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:619a609aa74ae43d90ed2e89bdd784765de0a25ca761b93e196d938b8fd1dbbd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:89149166622f4db9b4b6a449256291dc87a99ee53151c74cbd82a53c8c2f6ccd"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:7709f51f5f7c853f0fb938bcd3bc59cdfdc5203635ffd18bf354f6967ea0f824"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:345b0426edd4e18138d6528aed636de7a9ed169b4aaf9d61a8c19e39d26838ca"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:0907f11d019260cdc3f94fbdb23ff9125f6b5d1039b76003b5b0ac9d6a6c9d5b"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win32.whl", hash = "sha256:ea0d8d539afa5eb2728aa1932a988a9a7af94f18582ffae4bc10b3fbdad0626e"}, - {file = "charset_normalizer-3.4.1-cp38-cp38-win_amd64.whl", hash = "sha256:329ce159e82018d646c7ac45b01a430369d526569ec08516081727a20e9e4af4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:b97e690a2118911e39b4042088092771b4ae3fc3aa86518f84b8cf6888dbdb41"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:78baa6d91634dfb69ec52a463534bc0df05dbd546209b79a3880a34487f4b84f"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1a2bc9f351a75ef49d664206d51f8e5ede9da246602dc2d2726837620ea034b2"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75832c08354f595c760a804588b9357d34ec00ba1c940c15e31e96d902093770"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0af291f4fe114be0280cdd29d533696a77b5b49cfde5467176ecab32353395c4"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0167ddc8ab6508fe81860a57dd472b2ef4060e8d378f0cc555707126830f2537"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:2a75d49014d118e4198bcee5ee0a6f25856b29b12dbf7cd012791f8a6cc5c496"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:363e2f92b0f0174b2f8238240a1a30142e3db7b957a5dd5689b0e75fb717cc78"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:ab36c8eb7e454e34e60eb55ca5d241a5d18b2c6244f6827a30e451c42410b5f7"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:4c0907b1928a36d5a998d72d64d8eaa7244989f7aaaf947500d3a800c83a3fd6"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:04432ad9479fa40ec0f387795ddad4437a2b50417c69fa275e212933519ff294"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win32.whl", hash = "sha256:3bed14e9c89dcb10e8f3a29f9ccac4955aebe93c71ae803af79265c9ca5644c5"}, - {file = "charset_normalizer-3.4.1-cp39-cp39-win_amd64.whl", hash = "sha256:49402233c892a461407c512a19435d1ce275543138294f7ef013f0b63d5d3765"}, - {file = "charset_normalizer-3.4.1-py3-none-any.whl", hash = "sha256:d98b1668f06378c6dbefec3b92299716b931cd4e6061f3c875a71ced1780ab85"}, - {file = "charset_normalizer-3.4.1.tar.gz", hash = "sha256:44251f18cd68a75b56585dd00dae26183e102cd5e0f9f1466e6df5da2ed64ea3"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:7c48ed483eb946e6c04ccbe02c6b4d1d48e51944b6db70f697e089c193404941"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b2d318c11350e10662026ad0eb71bb51c7812fc8590825304ae0bdd4ac283acd"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9cbfacf36cb0ec2897ce0ebc5d08ca44213af24265bd56eca54bee7923c48fd6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18dd2e350387c87dabe711b86f83c9c78af772c748904d372ade190b5c7c9d4d"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8075c35cd58273fee266c58c0c9b670947c19df5fb98e7b66710e04ad4e9ff86"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5bf4545e3b962767e5c06fe1738f951f77d27967cb2caa64c28be7c4563e162c"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a6ab32f7210554a96cd9e33abe3ddd86732beeafc7a28e9955cdf22ffadbab0"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_i686.whl", hash = "sha256:b33de11b92e9f75a2b545d6e9b6f37e398d86c3e9e9653c4864eb7e89c5773ef"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_ppc64le.whl", hash = "sha256:8755483f3c00d6c9a77f490c17e6ab0c8729e39e6390328e42521ef175380ae6"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_s390x.whl", hash = "sha256:68a328e5f55ec37c57f19ebb1fdc56a248db2e3e9ad769919a58672958e8f366"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:21b2899062867b0e1fde9b724f8aecb1af14f2778d69aacd1a5a1853a597a5db"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win32.whl", hash = "sha256:e8082b26888e2f8b36a042a58307d5b917ef2b1cacab921ad3323ef91901c71a"}, + {file = "charset_normalizer-3.4.2-cp310-cp310-win_amd64.whl", hash = "sha256:f69a27e45c43520f5487f27627059b64aaf160415589230992cec34c5e18a509"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:be1e352acbe3c78727a16a455126d9ff83ea2dfdcbc83148d2982305a04714c2"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa88ca0b1932e93f2d961bf3addbb2db902198dca337d88c89e1559e066e7645"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d524ba3f1581b35c03cb42beebab4a13e6cdad7b36246bd22541fa585a56cccd"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28a1005facc94196e1fb3e82a3d442a9d9110b8434fc1ded7a24a2983c9888d8"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fdb20a30fe1175ecabed17cbf7812f7b804b8a315a25f24678bcdf120a90077f"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:0f5d9ed7f254402c9e7d35d2f5972c9bbea9040e99cd2861bd77dc68263277c7"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:efd387a49825780ff861998cd959767800d54f8308936b21025326de4b5a42b9"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:f0aa37f3c979cf2546b73e8222bbfa3dc07a641585340179d768068e3455e544"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:e70e990b2137b29dc5564715de1e12701815dacc1d056308e2b17e9095372a82"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:0c8c57f84ccfc871a48a47321cfa49ae1df56cd1d965a09abe84066f6853b9c0"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:6b66f92b17849b85cad91259efc341dce9c1af48e2173bf38a85c6329f1033e5"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win32.whl", hash = "sha256:daac4765328a919a805fa5e2720f3e94767abd632ae410a9062dff5412bae65a"}, + {file = "charset_normalizer-3.4.2-cp311-cp311-win_amd64.whl", hash = "sha256:e53efc7c7cee4c1e70661e2e112ca46a575f90ed9ae3fef200f2a25e954f4b28"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:0c29de6a1a95f24b9a1aa7aefd27d2487263f00dfd55a77719b530788f75cff7"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cddf7bd982eaa998934a91f69d182aec997c6c468898efe6679af88283b498d3"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fcbe676a55d7445b22c10967bceaaf0ee69407fbe0ece4d032b6eb8d4565982a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:d41c4d287cfc69060fa91cae9683eacffad989f1a10811995fa309df656ec214"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4e594135de17ab3866138f496755f302b72157d115086d100c3f19370839dd3a"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:cf713fe9a71ef6fd5adf7a79670135081cd4431c2943864757f0fa3a65b1fafd"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:a370b3e078e418187da8c3674eddb9d983ec09445c99a3a263c2011993522981"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:a955b438e62efdf7e0b7b52a64dc5c3396e2634baa62471768a64bc2adb73d5c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:7222ffd5e4de8e57e03ce2cef95a4c43c98fcb72ad86909abdfc2c17d227fc1b"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:bee093bf902e1d8fc0ac143c88902c3dfc8941f7ea1d6a8dd2bcb786d33db03d"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:dedb8adb91d11846ee08bec4c8236c8549ac721c245678282dcb06b221aab59f"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win32.whl", hash = "sha256:db4c7bf0e07fc3b7d89ac2a5880a6a8062056801b83ff56d8464b70f65482b6c"}, + {file = "charset_normalizer-3.4.2-cp312-cp312-win_amd64.whl", hash = "sha256:5a9979887252a82fefd3d3ed2a8e3b937a7a809f65dcb1e068b090e165bbe99e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:926ca93accd5d36ccdabd803392ddc3e03e6d4cd1cf17deff3b989ab8e9dbcf0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:eba9904b0f38a143592d9fc0e19e2df0fa2e41c3c3745554761c5f6447eedabf"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:3fddb7e2c84ac87ac3a947cb4e66d143ca5863ef48e4a5ecb83bd48619e4634e"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:98f862da73774290f251b9df8d11161b6cf25b599a66baf087c1ffe340e9bfd1"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6c9379d65defcab82d07b2a9dfbfc2e95bc8fe0ebb1b176a3190230a3ef0e07c"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e635b87f01ebc977342e2697d05b56632f5f879a4f15955dfe8cef2448b51691"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:1c95a1e2902a8b722868587c0e1184ad5c55631de5afc0eb96bc4b0d738092c0"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_i686.whl", hash = "sha256:ef8de666d6179b009dce7bcb2ad4c4a779f113f12caf8dc77f0162c29d20490b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:32fc0341d72e0f73f80acb0a2c94216bd704f4f0bce10aedea38f30502b271ff"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:289200a18fa698949d2b39c671c2cc7a24d44096784e76614899a7ccf2574b7b"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:4a476b06fbcf359ad25d34a057b7219281286ae2477cc5ff5e3f70a246971148"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win32.whl", hash = "sha256:aaeeb6a479c7667fbe1099af9617c83aaca22182d6cf8c53966491a0f1b7ffb7"}, + {file = "charset_normalizer-3.4.2-cp313-cp313-win_amd64.whl", hash = "sha256:aa6af9e7d59f9c12b33ae4e9450619cf2488e2bbe9b44030905877f0b2324980"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1cad5f45b3146325bb38d6855642f6fd609c3f7cad4dbaf75549bf3b904d3184"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b2680962a4848b3c4f155dc2ee64505a9c57186d0d56b43123b17ca3de18f0fa"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:36b31da18b8890a76ec181c3cf44326bf2c48e36d393ca1b72b3f484113ea344"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4074c5a429281bf056ddd4c5d3b740ebca4d43ffffe2ef4bf4d2d05114299da"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c9e36a97bee9b86ef9a1cf7bb96747eb7a15c2f22bdb5b516434b00f2a599f02"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:1b1bde144d98e446b056ef98e59c256e9294f6b74d7af6846bf5ffdafd687a7d"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_i686.whl", hash = "sha256:915f3849a011c1f593ab99092f3cecfcb4d65d8feb4a64cf1bf2d22074dc0ec4"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_ppc64le.whl", hash = "sha256:fb707f3e15060adf5b7ada797624a6c6e0138e2a26baa089df64c68ee98e040f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_s390x.whl", hash = "sha256:25a23ea5c7edc53e0f29bae2c44fcb5a1aa10591aae107f2a2b2583a9c5cbc64"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:770cab594ecf99ae64c236bc9ee3439c3f46be49796e265ce0cc8bc17b10294f"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win32.whl", hash = "sha256:6a0289e4589e8bdfef02a80478f1dfcb14f0ab696b5a00e1f4b8a14a307a3c58"}, + {file = "charset_normalizer-3.4.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6fc1f5b51fa4cecaa18f2bd7a003f3dd039dd615cd69a2afd6d3b19aed6775f2"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:76af085e67e56c8816c3ccf256ebd136def2ed9654525348cfa744b6802b69eb"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e45ba65510e2647721e35323d6ef54c7974959f6081b58d4ef5d87c60c84919a"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:046595208aae0120559a67693ecc65dd75d46f7bf687f159127046628178dc45"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:75d10d37a47afee94919c4fab4c22b9bc2a8bf7d4f46f87363bcf0573f3ff4f5"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6333b3aa5a12c26b2a4d4e7335a28f1475e0e5e17d69d55141ee3cab736f66d1"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8323a9b031aa0393768b87f04b4164a40037fb2a3c11ac06a03ffecd3618027"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:24498ba8ed6c2e0b56d4acbf83f2d989720a93b41d712ebd4f4979660db4417b"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_i686.whl", hash = "sha256:844da2b5728b5ce0e32d863af26f32b5ce61bc4273a9c720a9f3aa9df73b1455"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_ppc64le.whl", hash = "sha256:65c981bdbd3f57670af8b59777cbfae75364b483fa8a9f420f08094531d54a01"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_s390x.whl", hash = "sha256:3c21d4fca343c805a52c0c78edc01e3477f6dd1ad7c47653241cf2a206d4fc58"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:dc7039885fa1baf9be153a0626e337aa7ec8bf96b0128605fb0d77788ddc1681"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win32.whl", hash = "sha256:8272b73e1c5603666618805fe821edba66892e2870058c94c53147602eab29c7"}, + {file = "charset_normalizer-3.4.2-cp38-cp38-win_amd64.whl", hash = "sha256:70f7172939fdf8790425ba31915bfbe8335030f05b9913d7ae00a87d4395620a"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:005fa3432484527f9732ebd315da8da8001593e2cf46a3d817669f062c3d9ed4"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e92fca20c46e9f5e1bb485887d074918b13543b1c2a1185e69bb8d17ab6236a7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:50bf98d5e563b83cc29471fa114366e6806bc06bc7a25fd59641e41445327836"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:721c76e84fe669be19c5791da68232ca2e05ba5185575086e384352e2c309597"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:82d8fd25b7f4675d0c47cf95b594d4e7b158aca33b76aa63d07186e13c0e0ab7"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3daeac64d5b371dea99714f08ffc2c208522ec6b06fbc7866a450dd446f5c0f"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:dccab8d5fa1ef9bfba0590ecf4d46df048d18ffe3eec01eeb73a42e0d9e7a8ba"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_i686.whl", hash = "sha256:aaf27faa992bfee0264dc1f03f4c75e9fcdda66a519db6b957a3f826e285cf12"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_ppc64le.whl", hash = "sha256:eb30abc20df9ab0814b5a2524f23d75dcf83cde762c161917a2b4b7b55b1e518"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_s390x.whl", hash = "sha256:c72fbbe68c6f32f251bdc08b8611c7b3060612236e960ef848e0a517ddbe76c5"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:982bb1e8b4ffda883b3d0a521e23abcd6fd17418f6d2c4118d257a10199c0ce3"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win32.whl", hash = "sha256:43e0933a0eff183ee85833f341ec567c0980dae57c464d8a508e1b2ceb336471"}, + {file = "charset_normalizer-3.4.2-cp39-cp39-win_amd64.whl", hash = "sha256:d11b54acf878eef558599658b0ffca78138c8c3655cf4f3a4a673c437e67732e"}, + {file = "charset_normalizer-3.4.2-py3-none-any.whl", hash = "sha256:7f56930ab0abd1c45cd15be65cc741c28b1c9a34876ce8c17a2fa107810c0af0"}, + {file = "charset_normalizer-3.4.2.tar.gz", hash = "sha256:5baececa9ecba31eff645232d59845c07aa030f0c81ee70184a90d35099a0e63"}, ] [[package]] @@ -312,47 +312,49 @@ test = ["pytest"] [[package]] name = "cryptography" -version = "44.0.2" +version = "44.0.3" description = "cryptography is a package which provides cryptographic recipes and primitives to Python developers." optional = false python-versions = "!=3.9.0,!=3.9.1,>=3.7" groups = ["main"] files = [ - {file = "cryptography-44.0.2-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:efcfe97d1b3c79e486554efddeb8f6f53a4cdd4cf6086642784fa31fc384e1d7"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29ecec49f3ba3f3849362854b7253a9f59799e3763b0c9d0826259a88efa02f1"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bc821e161ae88bfe8088d11bb39caf2916562e0a2dc7b6d56714a48b784ef0bb"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:3c00b6b757b32ce0f62c574b78b939afab9eecaf597c4d624caca4f9e71e7843"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:7bdcd82189759aba3816d1f729ce42ffded1ac304c151d0a8e89b9996ab863d5"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:4973da6ca3db4405c54cd0b26d328be54c7747e89e284fcff166132eb7bccc9c"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:4e389622b6927d8133f314949a9812972711a111d577a5d1f4bee5e58736b80a"}, - {file = "cryptography-44.0.2-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:f514ef4cd14bb6fb484b4a60203e912cfcb64f2ab139e88c2274511514bf7308"}, - {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:1bc312dfb7a6e5d66082c87c34c8a62176e684b6fe3d90fcfe1568de675e6688"}, - {file = "cryptography-44.0.2-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:3b721b8b4d948b218c88cb8c45a01793483821e709afe5f622861fc6182b20a7"}, - {file = "cryptography-44.0.2-cp37-abi3-win32.whl", hash = "sha256:51e4de3af4ec3899d6d178a8c005226491c27c4ba84101bfb59c901e10ca9f79"}, - {file = "cryptography-44.0.2-cp37-abi3-win_amd64.whl", hash = "sha256:c505d61b6176aaf982c5717ce04e87da5abc9a36a5b39ac03905c4aafe8de7aa"}, - {file = "cryptography-44.0.2-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:8e0ddd63e6bf1161800592c71ac794d3fb8001f2caebe0966e77c5234fa9efc3"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:81276f0ea79a208d961c433a947029e1a15948966658cf6710bbabb60fcc2639"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9a1e657c0f4ea2a23304ee3f964db058c9e9e635cc7019c4aa21c330755ef6fd"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:6210c05941994290f3f7f175a4a57dbbb2afd9273657614c506d5976db061181"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:d1c3572526997b36f245a96a2b1713bf79ce99b271bbcf084beb6b9b075f29ea"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:b042d2a275c8cee83a4b7ae30c45a15e6a4baa65a179a0ec2d78ebb90e4f6699"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:d03806036b4f89e3b13b6218fefea8d5312e450935b1a2d55f0524e2ed7c59d9"}, - {file = "cryptography-44.0.2-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c7362add18b416b69d58c910caa217f980c5ef39b23a38a0880dfd87bdf8cd23"}, - {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:8cadc6e3b5a1f144a039ea08a0bdb03a2a92e19c46be3285123d32029f40a922"}, - {file = "cryptography-44.0.2-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6f101b1f780f7fc613d040ca4bdf835c6ef3b00e9bd7125a4255ec574c7916e4"}, - {file = "cryptography-44.0.2-cp39-abi3-win32.whl", hash = "sha256:3dc62975e31617badc19a906481deacdeb80b4bb454394b4098e3f2525a488c5"}, - {file = "cryptography-44.0.2-cp39-abi3-win_amd64.whl", hash = "sha256:5f6f90b72d8ccadb9c6e311c775c8305381db88374c65fa1a68250aa8a9cb3a6"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:af4ff3e388f2fa7bff9f7f2b31b87d5651c45731d3e8cfa0944be43dff5cfbdb"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:0529b1d5a0105dd3731fa65680b45ce49da4d8115ea76e9da77a875396727b41"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:7ca25849404be2f8e4b3c59483d9d3c51298a22c1c61a0e84415104dacaf5562"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:268e4e9b177c76d569e8a145a6939eca9a5fec658c932348598818acf31ae9a5"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:9eb9d22b0a5d8fd9925a7764a054dca914000607dff201a24c791ff5c799e1fa"}, - {file = "cryptography-44.0.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:2bf7bf75f7df9715f810d1b038870309342bff3069c5bd8c6b96128cb158668d"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:909c97ab43a9c0c0b0ada7a1281430e4e5ec0458e6d9244c0e821bbf152f061d"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:96e7a5e9d6e71f9f4fca8eebfd603f8e86c5225bb18eb621b2c1e50b290a9471"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:d1b3031093a366ac767b3feb8bcddb596671b3aaff82d4050f984da0c248b615"}, - {file = "cryptography-44.0.2-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:04abd71114848aa25edb28e225ab5f268096f44cf0127f3d36975bdf1bdf3390"}, - {file = "cryptography-44.0.2.tar.gz", hash = "sha256:c63454aa261a0cf0c5b4718349629793e9e634993538db841165b3df74f37ec0"}, + {file = "cryptography-44.0.3-cp37-abi3-macosx_10_9_universal2.whl", hash = "sha256:962bc30480a08d133e631e8dfd4783ab71cc9e33d5d7c1e192f0b7c06397bb88"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4ffc61e8f3bf5b60346d89cd3d37231019c17a081208dfbbd6e1605ba03fa137"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:58968d331425a6f9eedcee087f77fd3c927c88f55368f43ff7e0a19891f2642c"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:e28d62e59a4dbd1d22e747f57d4f00c459af22181f0b2f787ea83f5a876d7c76"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:af653022a0c25ef2e3ffb2c673a50e5a0d02fecc41608f4954176f1933b12359"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:157f1f3b8d941c2bd8f3ffee0af9b049c9665c39d3da9db2dc338feca5e98a43"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:c6cd67722619e4d55fdb42ead64ed8843d64638e9c07f4011163e46bc512cf01"}, + {file = "cryptography-44.0.3-cp37-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:b424563394c369a804ecbee9b06dfb34997f19d00b3518e39f83a5642618397d"}, + {file = "cryptography-44.0.3-cp37-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:c91fc8e8fd78af553f98bc7f2a1d8db977334e4eea302a4bfd75b9461c2d8904"}, + {file = "cryptography-44.0.3-cp37-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:25cd194c39fa5a0aa4169125ee27d1172097857b27109a45fadc59653ec06f44"}, + {file = "cryptography-44.0.3-cp37-abi3-win32.whl", hash = "sha256:3be3f649d91cb182c3a6bd336de8b61a0a71965bd13d1a04a0e15b39c3d5809d"}, + {file = "cryptography-44.0.3-cp37-abi3-win_amd64.whl", hash = "sha256:3883076d5c4cc56dbef0b898a74eb6992fdac29a7b9013870b34efe4ddb39a0d"}, + {file = "cryptography-44.0.3-cp39-abi3-macosx_10_9_universal2.whl", hash = "sha256:5639c2b16764c6f76eedf722dbad9a0914960d3489c0cc38694ddf9464f1bb2f"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ffef566ac88f75967d7abd852ed5f182da252d23fac11b4766da3957766759"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:192ed30fac1728f7587c6f4613c29c584abdc565d7417c13904708db10206645"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:7d5fe7195c27c32a64955740b949070f21cba664604291c298518d2e255931d2"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_28_armv7l.manylinux_2_31_armv7l.whl", hash = "sha256:3f07943aa4d7dad689e3bb1638ddc4944cc5e0921e3c227486daae0e31a05e54"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:cb90f60e03d563ca2445099edf605c16ed1d5b15182d21831f58460c48bffb93"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ab0b005721cc0039e885ac3503825661bd9810b15d4f374e473f8c89b7d5460c"}, + {file = "cryptography-44.0.3-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:3bb0847e6363c037df8f6ede57d88eaf3410ca2267fb12275370a76f85786a6f"}, + {file = "cryptography-44.0.3-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b0cc66c74c797e1db750aaa842ad5b8b78e14805a9b5d1348dc603612d3e3ff5"}, + {file = "cryptography-44.0.3-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6866df152b581f9429020320e5eb9794c8780e90f7ccb021940d7f50ee00ae0b"}, + {file = "cryptography-44.0.3-cp39-abi3-win32.whl", hash = "sha256:c138abae3a12a94c75c10499f1cbae81294a6f983b3af066390adee73f433028"}, + {file = "cryptography-44.0.3-cp39-abi3-win_amd64.whl", hash = "sha256:5d186f32e52e66994dce4f766884bcb9c68b8da62d61d9d215bfe5fb56d21334"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-macosx_10_9_x86_64.whl", hash = "sha256:cad399780053fb383dc067475135e41c9fe7d901a97dd5d9c5dfb5611afc0d7d"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:21a83f6f35b9cc656d71b5de8d519f566df01e660ac2578805ab245ffd8523f8"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:fc3c9babc1e1faefd62704bb46a69f359a9819eb0292e40df3fb6e3574715cd4"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:e909df4053064a97f1e6565153ff8bb389af12c5c8d29c343308760890560aff"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:dad80b45c22e05b259e33ddd458e9e2ba099c86ccf4e88db7bbab4b747b18d06"}, + {file = "cryptography-44.0.3-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:479d92908277bed6e1a1c69b277734a7771c2b78633c224445b5c60a9f4bc1d9"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-macosx_10_9_x86_64.whl", hash = "sha256:896530bc9107b226f265effa7ef3f21270f18a2026bc09fed1ebd7b66ddf6375"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:9b4d4a5dbee05a2c390bf212e78b99434efec37b17a4bff42f50285c5c8c9647"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:02f55fb4f8b79c1221b0961488eaae21015b69b210e18c386b69de182ebb1259"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:dd3db61b8fe5be220eee484a17233287d0be6932d056cf5738225b9c05ef4fff"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:978631ec51a6bbc0b7e58f23b68a8ce9e5f09721940933e9c217068388789fe5"}, + {file = "cryptography-44.0.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:5d20cc348cca3a8aa7312f42ab953a56e15323800ca3ab0706b8cd452a3a056c"}, + {file = "cryptography-44.0.3.tar.gz", hash = "sha256:fe19d8bc5536a91a24a8133328880a41831b6c5df54599a8417b62fe015d3053"}, ] [package.dependencies] @@ -365,43 +367,43 @@ nox = ["nox (>=2024.4.15)", "nox[uv] (>=2024.3.2) ; python_version >= \"3.8\""] pep8test = ["check-sdist ; python_version >= \"3.8\"", "click (>=8.0.1)", "mypy (>=1.4)", "ruff (>=0.3.6)"] sdist = ["build (>=1.0.0)"] ssh = ["bcrypt (>=3.1.5)"] -test = ["certifi (>=2024)", "cryptography-vectors (==44.0.2)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] +test = ["certifi (>=2024)", "cryptography-vectors (==44.0.3)", "pretend (>=0.7)", "pytest (>=7.4.0)", "pytest-benchmark (>=4.0)", "pytest-cov (>=2.10.1)", "pytest-xdist (>=3.5.0)"] test-randomorder = ["pytest-randomly"] [[package]] name = "debugpy" -version = "1.8.13" +version = "1.8.14" description = "An implementation of the Debug Adapter Protocol for Python" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "debugpy-1.8.13-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:06859f68e817966723ffe046b896b1bd75c665996a77313370336ee9e1de3e90"}, - {file = "debugpy-1.8.13-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cb56c2db69fb8df3168bc857d7b7d2494fed295dfdbde9a45f27b4b152f37520"}, - {file = "debugpy-1.8.13-cp310-cp310-win32.whl", hash = "sha256:46abe0b821cad751fc1fb9f860fb2e68d75e2c5d360986d0136cd1db8cad4428"}, - {file = "debugpy-1.8.13-cp310-cp310-win_amd64.whl", hash = "sha256:dc7b77f5d32674686a5f06955e4b18c0e41fb5a605f5b33cf225790f114cfeec"}, - {file = "debugpy-1.8.13-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:eee02b2ed52a563126c97bf04194af48f2fe1f68bb522a312b05935798e922ff"}, - {file = "debugpy-1.8.13-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4caca674206e97c85c034c1efab4483f33971d4e02e73081265ecb612af65377"}, - {file = "debugpy-1.8.13-cp311-cp311-win32.whl", hash = "sha256:7d9a05efc6973b5aaf076d779cf3a6bbb1199e059a17738a2aa9d27a53bcc888"}, - {file = "debugpy-1.8.13-cp311-cp311-win_amd64.whl", hash = "sha256:62f9b4a861c256f37e163ada8cf5a81f4c8d5148fc17ee31fb46813bd658cdcc"}, - {file = "debugpy-1.8.13-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:2b8de94c5c78aa0d0ed79023eb27c7c56a64c68217d881bee2ffbcb13951d0c1"}, - {file = "debugpy-1.8.13-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:887d54276cefbe7290a754424b077e41efa405a3e07122d8897de54709dbe522"}, - {file = "debugpy-1.8.13-cp312-cp312-win32.whl", hash = "sha256:3872ce5453b17837ef47fb9f3edc25085ff998ce63543f45ba7af41e7f7d370f"}, - {file = "debugpy-1.8.13-cp312-cp312-win_amd64.whl", hash = "sha256:63ca7670563c320503fea26ac688988d9d6b9c6a12abc8a8cf2e7dd8e5f6b6ea"}, - {file = "debugpy-1.8.13-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:31abc9618be4edad0b3e3a85277bc9ab51a2d9f708ead0d99ffb5bb750e18503"}, - {file = "debugpy-1.8.13-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a0bd87557f97bced5513a74088af0b84982b6ccb2e254b9312e29e8a5c4270eb"}, - {file = "debugpy-1.8.13-cp313-cp313-win32.whl", hash = "sha256:5268ae7fdca75f526d04465931cb0bd24577477ff50e8bb03dab90983f4ebd02"}, - {file = "debugpy-1.8.13-cp313-cp313-win_amd64.whl", hash = "sha256:79ce4ed40966c4c1631d0131606b055a5a2f8e430e3f7bf8fd3744b09943e8e8"}, - {file = "debugpy-1.8.13-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:acf39a6e98630959763f9669feddee540745dfc45ad28dbc9bd1f9cd60639391"}, - {file = "debugpy-1.8.13-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:924464d87e7d905eb0d79fb70846558910e906d9ee309b60c4fe597a2e802590"}, - {file = "debugpy-1.8.13-cp38-cp38-win32.whl", hash = "sha256:3dae443739c6b604802da9f3e09b0f45ddf1cf23c99161f3a1a8039f61a8bb89"}, - {file = "debugpy-1.8.13-cp38-cp38-win_amd64.whl", hash = "sha256:ed93c3155fc1f888ab2b43626182174e457fc31b7781cd1845629303790b8ad1"}, - {file = "debugpy-1.8.13-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:6fab771639332bd8ceb769aacf454a30d14d7a964f2012bf9c4e04c60f16e85b"}, - {file = "debugpy-1.8.13-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32b6857f8263a969ce2ca098f228e5cc0604d277447ec05911a8c46cf3e7e307"}, - {file = "debugpy-1.8.13-cp39-cp39-win32.whl", hash = "sha256:f14d2c4efa1809da125ca62df41050d9c7cd9cb9e380a2685d1e453c4d450ccb"}, - {file = "debugpy-1.8.13-cp39-cp39-win_amd64.whl", hash = "sha256:ea869fe405880327497e6945c09365922c79d2a1eed4c3ae04d77ac7ae34b2b5"}, - {file = "debugpy-1.8.13-py2.py3-none-any.whl", hash = "sha256:d4ba115cdd0e3a70942bd562adba9ec8c651fe69ddde2298a1be296fc331906f"}, - {file = "debugpy-1.8.13.tar.gz", hash = "sha256:837e7bef95bdefba426ae38b9a94821ebdc5bea55627879cd48165c90b9e50ce"}, + {file = "debugpy-1.8.14-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:93fee753097e85623cab1c0e6a68c76308cd9f13ffdf44127e6fab4fbf024339"}, + {file = "debugpy-1.8.14-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3d937d93ae4fa51cdc94d3e865f535f185d5f9748efb41d0d49e33bf3365bd79"}, + {file = "debugpy-1.8.14-cp310-cp310-win32.whl", hash = "sha256:c442f20577b38cc7a9aafecffe1094f78f07fb8423c3dddb384e6b8f49fd2987"}, + {file = "debugpy-1.8.14-cp310-cp310-win_amd64.whl", hash = "sha256:f117dedda6d969c5c9483e23f573b38f4e39412845c7bc487b6f2648df30fe84"}, + {file = "debugpy-1.8.14-cp311-cp311-macosx_14_0_universal2.whl", hash = "sha256:1b2ac8c13b2645e0b1eaf30e816404990fbdb168e193322be8f545e8c01644a9"}, + {file = "debugpy-1.8.14-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:cf431c343a99384ac7eab2f763980724834f933a271e90496944195318c619e2"}, + {file = "debugpy-1.8.14-cp311-cp311-win32.whl", hash = "sha256:c99295c76161ad8d507b413cd33422d7c542889fbb73035889420ac1fad354f2"}, + {file = "debugpy-1.8.14-cp311-cp311-win_amd64.whl", hash = "sha256:7816acea4a46d7e4e50ad8d09d963a680ecc814ae31cdef3622eb05ccacf7b01"}, + {file = "debugpy-1.8.14-cp312-cp312-macosx_14_0_universal2.whl", hash = "sha256:8899c17920d089cfa23e6005ad9f22582fd86f144b23acb9feeda59e84405b84"}, + {file = "debugpy-1.8.14-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f6bb5c0dcf80ad5dbc7b7d6eac484e2af34bdacdf81df09b6a3e62792b722826"}, + {file = "debugpy-1.8.14-cp312-cp312-win32.whl", hash = "sha256:281d44d248a0e1791ad0eafdbbd2912ff0de9eec48022a5bfbc332957487ed3f"}, + {file = "debugpy-1.8.14-cp312-cp312-win_amd64.whl", hash = "sha256:5aa56ef8538893e4502a7d79047fe39b1dae08d9ae257074c6464a7b290b806f"}, + {file = "debugpy-1.8.14-cp313-cp313-macosx_14_0_universal2.whl", hash = "sha256:329a15d0660ee09fec6786acdb6e0443d595f64f5d096fc3e3ccf09a4259033f"}, + {file = "debugpy-1.8.14-cp313-cp313-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0f920c7f9af409d90f5fd26e313e119d908b0dd2952c2393cd3247a462331f15"}, + {file = "debugpy-1.8.14-cp313-cp313-win32.whl", hash = "sha256:3784ec6e8600c66cbdd4ca2726c72d8ca781e94bce2f396cc606d458146f8f4e"}, + {file = "debugpy-1.8.14-cp313-cp313-win_amd64.whl", hash = "sha256:684eaf43c95a3ec39a96f1f5195a7ff3d4144e4a18d69bb66beeb1a6de605d6e"}, + {file = "debugpy-1.8.14-cp38-cp38-macosx_14_0_x86_64.whl", hash = "sha256:d5582bcbe42917bc6bbe5c12db1bffdf21f6bfc28d4554b738bf08d50dc0c8c3"}, + {file = "debugpy-1.8.14-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5349b7c3735b766a281873fbe32ca9cca343d4cc11ba4a743f84cb854339ff35"}, + {file = "debugpy-1.8.14-cp38-cp38-win32.whl", hash = "sha256:7118d462fe9724c887d355eef395fae68bc764fd862cdca94e70dcb9ade8a23d"}, + {file = "debugpy-1.8.14-cp38-cp38-win_amd64.whl", hash = "sha256:d235e4fa78af2de4e5609073972700523e372cf5601742449970110d565ca28c"}, + {file = "debugpy-1.8.14-cp39-cp39-macosx_14_0_x86_64.whl", hash = "sha256:413512d35ff52c2fb0fd2d65e69f373ffd24f0ecb1fac514c04a668599c5ce7f"}, + {file = "debugpy-1.8.14-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4c9156f7524a0d70b7a7e22b2e311d8ba76a15496fb00730e46dcdeedb9e1eea"}, + {file = "debugpy-1.8.14-cp39-cp39-win32.whl", hash = "sha256:b44985f97cc3dd9d52c42eb59ee9d7ee0c4e7ecd62bca704891f997de4cef23d"}, + {file = "debugpy-1.8.14-cp39-cp39-win_amd64.whl", hash = "sha256:b1528cfee6c1b1c698eb10b6b096c598738a8238822d218173d21c3086de8123"}, + {file = "debugpy-1.8.14-py2.py3-none-any.whl", hash = "sha256:5cd9a579d553b6cb9759a7908a41988ee6280b961f24f63336835d9418216a20"}, + {file = "debugpy-1.8.14.tar.gz", hash = "sha256:7cd287184318416850aa8b60ac90105837bb1e59531898c07569d197d2ed5322"}, ] [[package]] @@ -497,86 +499,68 @@ speedup = ["python-levenshtein (>=0.12)"] [[package]] name = "greenlet" -version = "3.1.1" +version = "3.2.2" description = "Lightweight in-process concurrent programming" optional = false -python-versions = ">=3.7" +python-versions = ">=3.9" groups = ["main"] markers = "python_version < \"3.14\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")" files = [ - {file = "greenlet-3.1.1-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:0bbae94a29c9e5c7e4a2b7f0aae5c17e8e90acbfd3bf6270eeba60c39fce3563"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0fde093fb93f35ca72a556cf72c92ea3ebfda3d79fc35bb19fbe685853869a83"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36b89d13c49216cadb828db8dfa6ce86bbbc476a82d3a6c397f0efae0525bdd0"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94b6150a85e1b33b40b1464a3f9988dcc5251d6ed06842abff82e42632fac120"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:93147c513fac16385d1036b7e5b102c7fbbdb163d556b791f0f11eada7ba65dc"}, - {file = "greenlet-3.1.1-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:da7a9bff22ce038e19bf62c4dd1ec8391062878710ded0a845bcf47cc0200617"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:b2795058c23988728eec1f36a4e5e4ebad22f8320c85f3587b539b9ac84128d7"}, - {file = "greenlet-3.1.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:ed10eac5830befbdd0c32f83e8aa6288361597550ba669b04c48f0f9a2c843c6"}, - {file = "greenlet-3.1.1-cp310-cp310-win_amd64.whl", hash = "sha256:77c386de38a60d1dfb8e55b8c1101d68c79dfdd25c7095d51fec2dd800892b80"}, - {file = "greenlet-3.1.1-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:e4d333e558953648ca09d64f13e6d8f0523fa705f51cae3f03b5983489958c70"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:09fc016b73c94e98e29af67ab7b9a879c307c6731a2c9da0db5a7d9b7edd1159"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d5e975ca70269d66d17dd995dafc06f1b06e8cb1ec1e9ed54c1d1e4a7c4cf26e"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3b2813dc3de8c1ee3f924e4d4227999285fd335d1bcc0d2be6dc3f1f6a318ec1"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e347b3bfcf985a05e8c0b7d462ba6f15b1ee1c909e2dcad795e49e91b152c383"}, - {file = "greenlet-3.1.1-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:9e8f8c9cb53cdac7ba9793c276acd90168f416b9ce36799b9b885790f8ad6c0a"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:62ee94988d6b4722ce0028644418d93a52429e977d742ca2ccbe1c4f4a792511"}, - {file = "greenlet-3.1.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1776fd7f989fc6b8d8c8cb8da1f6b82c5814957264d1f6cf818d475ec2bf6395"}, - {file = "greenlet-3.1.1-cp311-cp311-win_amd64.whl", hash = "sha256:48ca08c771c268a768087b408658e216133aecd835c0ded47ce955381105ba39"}, - {file = "greenlet-3.1.1-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:4afe7ea89de619adc868e087b4d2359282058479d7cfb94970adf4b55284574d"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f406b22b7c9a9b4f8aa9d2ab13d6ae0ac3e85c9a809bd590ad53fed2bf70dc79"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c3a701fe5a9695b238503ce5bbe8218e03c3bcccf7e204e455e7462d770268aa"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2846930c65b47d70b9d178e89c7e1a69c95c1f68ea5aa0a58646b7a96df12441"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:99cfaa2110534e2cf3ba31a7abcac9d328d1d9f1b95beede58294a60348fba36"}, - {file = "greenlet-3.1.1-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1443279c19fca463fc33e65ef2a935a5b09bb90f978beab37729e1c3c6c25fe9"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b7cede291382a78f7bb5f04a529cb18e068dd29e0fb27376074b6d0317bf4dd0"}, - {file = "greenlet-3.1.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:23f20bb60ae298d7d8656c6ec6db134bca379ecefadb0b19ce6f19d1f232a942"}, - {file = "greenlet-3.1.1-cp312-cp312-win_amd64.whl", hash = "sha256:7124e16b4c55d417577c2077be379514321916d5790fa287c9ed6f23bd2ffd01"}, - {file = "greenlet-3.1.1-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:05175c27cb459dcfc05d026c4232f9de8913ed006d42713cb8a5137bd49375f1"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:935e943ec47c4afab8965954bf49bfa639c05d4ccf9ef6e924188f762145c0ff"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:667a9706c970cb552ede35aee17339a18e8f2a87a51fba2ed39ceeeb1004798a"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b8a678974d1f3aa55f6cc34dc480169d58f2e6d8958895d68845fa4ab566509e"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:efc0f674aa41b92da8c49e0346318c6075d734994c3c4e4430b1c3f853e498e4"}, - {file = "greenlet-3.1.1-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0153404a4bb921f0ff1abeb5ce8a5131da56b953eda6e14b88dc6bbc04d2049e"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:275f72decf9932639c1c6dd1013a1bc266438eb32710016a1c742df5da6e60a1"}, - {file = "greenlet-3.1.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:c4aab7f6381f38a4b42f269057aee279ab0fc7bf2e929e3d4abfae97b682a12c"}, - {file = "greenlet-3.1.1-cp313-cp313-win_amd64.whl", hash = "sha256:b42703b1cf69f2aa1df7d1030b9d77d3e584a70755674d60e710f0af570f3761"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f1695e76146579f8c06c1509c7ce4dfe0706f49c6831a817ac04eebb2fd02011"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7876452af029456b3f3549b696bb36a06db7c90747740c5302f74a9e9fa14b13"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4ead44c85f8ab905852d3de8d86f6f8baf77109f9da589cb4fa142bd3b57b475"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8320f64b777d00dd7ccdade271eaf0cad6636343293a25074cc5566160e4de7b"}, - {file = "greenlet-3.1.1-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6510bf84a6b643dabba74d3049ead221257603a253d0a9873f55f6a59a65f822"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:04b013dc07c96f83134b1e99888e7a79979f1a247e2a9f59697fa14b5862ed01"}, - {file = "greenlet-3.1.1-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:411f015496fec93c1c8cd4e5238da364e1da7a124bcb293f085bf2860c32c6f6"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:47da355d8687fd65240c364c90a31569a133b7b60de111c255ef5b606f2ae291"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:98884ecf2ffb7d7fe6bd517e8eb99d31ff7855a840fa6d0d63cd07c037f6a981"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f1d4aeb8891338e60d1ab6127af1fe45def5259def8094b9c7e34690c8858803"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:db32b5348615a04b82240cc67983cb315309e88d444a288934ee6ceaebcad6cc"}, - {file = "greenlet-3.1.1-cp37-cp37m-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dcc62f31eae24de7f8dce72134c8651c58000d3b1868e01392baea7c32c247de"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:1d3755bcb2e02de341c55b4fca7a745a24a9e7212ac953f6b3a48d117d7257aa"}, - {file = "greenlet-3.1.1-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:b8da394b34370874b4572676f36acabac172602abf054cbc4ac910219f3340af"}, - {file = "greenlet-3.1.1-cp37-cp37m-win32.whl", hash = "sha256:a0dfc6c143b519113354e780a50381508139b07d2177cb6ad6a08278ec655798"}, - {file = "greenlet-3.1.1-cp37-cp37m-win_amd64.whl", hash = "sha256:54558ea205654b50c438029505def3834e80f0869a70fb15b871c29b4575ddef"}, - {file = "greenlet-3.1.1-cp38-cp38-macosx_11_0_universal2.whl", hash = "sha256:346bed03fe47414091be4ad44786d1bd8bef0c3fcad6ed3dee074a032ab408a9"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dfc59d69fc48664bc693842bd57acfdd490acafda1ab52c7836e3fc75c90a111"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d21e10da6ec19b457b82636209cbe2331ff4306b54d06fa04b7c138ba18c8a81"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:37b9de5a96111fc15418819ab4c4432e4f3c2ede61e660b1e33971eba26ef9ba"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6ef9ea3f137e5711f0dbe5f9263e8c009b7069d8a1acea822bd5e9dae0ae49c8"}, - {file = "greenlet-3.1.1-cp38-cp38-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:85f3ff71e2e60bd4b4932a043fbbe0f499e263c628390b285cb599154a3b03b1"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:95ffcf719966dd7c453f908e208e14cde192e09fde6c7186c8f1896ef778d8cd"}, - {file = "greenlet-3.1.1-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:03a088b9de532cbfe2ba2034b2b85e82df37874681e8c470d6fb2f8c04d7e4b7"}, - {file = "greenlet-3.1.1-cp38-cp38-win32.whl", hash = "sha256:8b8b36671f10ba80e159378df9c4f15c14098c4fd73a36b9ad715f057272fbef"}, - {file = "greenlet-3.1.1-cp38-cp38-win_amd64.whl", hash = "sha256:7017b2be767b9d43cc31416aba48aab0d2309ee31b4dbf10a1d38fb7972bdf9d"}, - {file = "greenlet-3.1.1-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:396979749bd95f018296af156201d6211240e7a23090f50a8d5d18c370084dc3"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ca9d0ff5ad43e785350894d97e13633a66e2b50000e8a183a50a88d834752d42"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f6ff3b14f2df4c41660a7dec01045a045653998784bf8cfcb5a525bdffffbc8f"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:94ebba31df2aa506d7b14866fed00ac141a867e63143fe5bca82a8e503b36437"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:73aaad12ac0ff500f62cebed98d8789198ea0e6f233421059fa68a5aa7220145"}, - {file = "greenlet-3.1.1-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:63e4844797b975b9af3a3fb8f7866ff08775f5426925e1e0bbcfe7932059a12c"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:7939aa3ca7d2a1593596e7ac6d59391ff30281ef280d8632fa03d81f7c5f955e"}, - {file = "greenlet-3.1.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d0028e725ee18175c6e422797c407874da24381ce0690d6b9396c204c7f7276e"}, - {file = "greenlet-3.1.1-cp39-cp39-win32.whl", hash = "sha256:5e06afd14cbaf9e00899fae69b24a32f2196c19de08fcb9f4779dd4f004e5e7c"}, - {file = "greenlet-3.1.1-cp39-cp39-win_amd64.whl", hash = "sha256:3319aa75e0e0639bc15ff54ca327e8dc7a6fe404003496e3c6925cd3142e0e22"}, - {file = "greenlet-3.1.1.tar.gz", hash = "sha256:4ce3ac6cdb6adf7946475d7ef31777c26d94bccc377e070a7986bd2d5c515467"}, + {file = "greenlet-3.2.2-cp310-cp310-macosx_11_0_universal2.whl", hash = "sha256:c49e9f7c6f625507ed83a7485366b46cbe325717c60837f7244fc99ba16ba9d6"}, + {file = "greenlet-3.2.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3cc1a3ed00ecfea8932477f729a9f616ad7347a5e55d50929efa50a86cb7be7"}, + {file = "greenlet-3.2.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:7c9896249fbef2c615853b890ee854f22c671560226c9221cfd27c995db97e5c"}, + {file = "greenlet-3.2.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:7409796591d879425997a518138889d8d17e63ada7c99edc0d7a1c22007d4907"}, + {file = "greenlet-3.2.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7791dcb496ec53d60c7f1c78eaa156c21f402dda38542a00afc3e20cae0f480f"}, + {file = "greenlet-3.2.2-cp310-cp310-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d8009ae46259e31bc73dc183e402f548e980c96f33a6ef58cc2e7865db012e13"}, + {file = "greenlet-3.2.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fd9fb7c941280e2c837b603850efc93c999ae58aae2b40765ed682a6907ebbc5"}, + {file = "greenlet-3.2.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:00cd814b8959b95a546e47e8d589610534cfb71f19802ea8a2ad99d95d702057"}, + {file = "greenlet-3.2.2-cp310-cp310-win_amd64.whl", hash = "sha256:d0cb7d47199001de7658c213419358aa8937df767936506db0db7ce1a71f4a2f"}, + {file = "greenlet-3.2.2-cp311-cp311-macosx_11_0_universal2.whl", hash = "sha256:dcb9cebbf3f62cb1e5afacae90761ccce0effb3adaa32339a0670fe7805d8068"}, + {file = "greenlet-3.2.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf3fc9145141250907730886b031681dfcc0de1c158f3cc51c092223c0f381ce"}, + {file = "greenlet-3.2.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:efcdfb9df109e8a3b475c016f60438fcd4be68cd13a365d42b35914cdab4bb2b"}, + {file = "greenlet-3.2.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4bd139e4943547ce3a56ef4b8b1b9479f9e40bb47e72cc906f0f66b9d0d5cab3"}, + {file = "greenlet-3.2.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:71566302219b17ca354eb274dfd29b8da3c268e41b646f330e324e3967546a74"}, + {file = "greenlet-3.2.2-cp311-cp311-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3091bc45e6b0c73f225374fefa1536cd91b1e987377b12ef5b19129b07d93ebe"}, + {file = "greenlet-3.2.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:44671c29da26539a5f142257eaba5110f71887c24d40df3ac87f1117df589e0e"}, + {file = "greenlet-3.2.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c23ea227847c9dbe0b3910f5c0dd95658b607137614eb821e6cbaecd60d81cc6"}, + {file = "greenlet-3.2.2-cp311-cp311-win_amd64.whl", hash = "sha256:0a16fb934fcabfdfacf21d79e6fed81809d8cd97bc1be9d9c89f0e4567143d7b"}, + {file = "greenlet-3.2.2-cp312-cp312-macosx_11_0_universal2.whl", hash = "sha256:df4d1509efd4977e6a844ac96d8be0b9e5aa5d5c77aa27ca9f4d3f92d3fcf330"}, + {file = "greenlet-3.2.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:da956d534a6d1b9841f95ad0f18ace637668f680b1339ca4dcfb2c1837880a0b"}, + {file = "greenlet-3.2.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c7b15fb9b88d9ee07e076f5a683027bc3befd5bb5d25954bb633c385d8b737e"}, + {file = "greenlet-3.2.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:752f0e79785e11180ebd2e726c8a88109ded3e2301d40abced2543aa5d164275"}, + {file = "greenlet-3.2.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ae572c996ae4b5e122331e12bbb971ea49c08cc7c232d1bd43150800a2d6c65"}, + {file = "greenlet-3.2.2-cp312-cp312-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02f5972ff02c9cf615357c17ab713737cccfd0eaf69b951084a9fd43f39833d3"}, + {file = "greenlet-3.2.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:4fefc7aa68b34b9224490dfda2e70ccf2131368493add64b4ef2d372955c207e"}, + {file = "greenlet-3.2.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:a31ead8411a027c2c4759113cf2bd473690517494f3d6e4bf67064589afcd3c5"}, + {file = "greenlet-3.2.2-cp312-cp312-win_amd64.whl", hash = "sha256:b24c7844c0a0afc3ccbeb0b807adeefb7eff2b5599229ecedddcfeb0ef333bec"}, + {file = "greenlet-3.2.2-cp313-cp313-macosx_11_0_universal2.whl", hash = "sha256:3ab7194ee290302ca15449f601036007873028712e92ca15fc76597a0aeb4c59"}, + {file = "greenlet-3.2.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2dc5c43bb65ec3669452af0ab10729e8fdc17f87a1f2ad7ec65d4aaaefabf6bf"}, + {file = "greenlet-3.2.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:decb0658ec19e5c1f519faa9a160c0fc85a41a7e6654b3ce1b44b939f8bf1325"}, + {file = "greenlet-3.2.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6fadd183186db360b61cb34e81117a096bff91c072929cd1b529eb20dd46e6c5"}, + {file = "greenlet-3.2.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1919cbdc1c53ef739c94cf2985056bcc0838c1f217b57647cbf4578576c63825"}, + {file = "greenlet-3.2.2-cp313-cp313-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:3885f85b61798f4192d544aac7b25a04ece5fe2704670b4ab73c2d2c14ab740d"}, + {file = "greenlet-3.2.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:85f3e248507125bf4af607a26fd6cb8578776197bd4b66e35229cdf5acf1dfbf"}, + {file = "greenlet-3.2.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1e76106b6fc55fa3d6fe1c527f95ee65e324a13b62e243f77b48317346559708"}, + {file = "greenlet-3.2.2-cp313-cp313-win_amd64.whl", hash = "sha256:fe46d4f8e94e637634d54477b0cfabcf93c53f29eedcbdeecaf2af32029b4421"}, + {file = "greenlet-3.2.2-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba30e88607fb6990544d84caf3c706c4b48f629e18853fc6a646f82db9629418"}, + {file = "greenlet-3.2.2-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:055916fafad3e3388d27dd68517478933a97edc2fc54ae79d3bec827de2c64c4"}, + {file = "greenlet-3.2.2-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2593283bf81ca37d27d110956b79e8723f9aa50c4bcdc29d3c0543d4743d2763"}, + {file = "greenlet-3.2.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:89c69e9a10670eb7a66b8cef6354c24671ba241f46152dd3eed447f79c29fb5b"}, + {file = "greenlet-3.2.2-cp313-cp313t-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02a98600899ca1ca5d3a2590974c9e3ec259503b2d6ba6527605fcd74e08e207"}, + {file = "greenlet-3.2.2-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:b50a8c5c162469c3209e5ec92ee4f95c8231b11db6a04db09bbe338176723bb8"}, + {file = "greenlet-3.2.2-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:45f9f4853fb4cc46783085261c9ec4706628f3b57de3e68bae03e8f8b3c0de51"}, + {file = "greenlet-3.2.2-cp314-cp314-macosx_11_0_universal2.whl", hash = "sha256:9ea5231428af34226c05f927e16fc7f6fa5e39e3ad3cd24ffa48ba53a47f4240"}, + {file = "greenlet-3.2.2-cp39-cp39-macosx_11_0_universal2.whl", hash = "sha256:1e4747712c4365ef6765708f948acc9c10350719ca0545e362c24ab973017370"}, + {file = "greenlet-3.2.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:782743700ab75716650b5238a4759f840bb2dcf7bff56917e9ffdf9f1f23ec59"}, + {file = "greenlet-3.2.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:354f67445f5bed6604e493a06a9a49ad65675d3d03477d38a4db4a427e9aad0e"}, + {file = "greenlet-3.2.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3aeca9848d08ce5eb653cf16e15bb25beeab36e53eb71cc32569f5f3afb2a3aa"}, + {file = "greenlet-3.2.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8cb8553ee954536500d88a1a2f58fcb867e45125e600e80f586ade399b3f8819"}, + {file = "greenlet-3.2.2-cp39-cp39-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1592a615b598643dbfd566bac8467f06c8c8ab6e56f069e573832ed1d5d528cc"}, + {file = "greenlet-3.2.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1f72667cc341c95184f1c68f957cb2d4fc31eef81646e8e59358a10ce6689457"}, + {file = "greenlet-3.2.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:a8fa80665b1a29faf76800173ff5325095f3e66a78e62999929809907aca5659"}, + {file = "greenlet-3.2.2-cp39-cp39-win32.whl", hash = "sha256:6629311595e3fe7304039c67f00d145cd1d38cf723bb5b99cc987b23c1433d61"}, + {file = "greenlet-3.2.2-cp39-cp39-win_amd64.whl", hash = "sha256:eeb27bece45c0c2a5842ac4c5a1b5c2ceaefe5711078eed4e8043159fa05c834"}, + {file = "greenlet-3.2.2.tar.gz", hash = "sha256:ad053d34421a2debba45aa3cc39acf454acbcd025b3fc1a9f8a0dee237abd485"}, ] [package.extras] @@ -585,14 +569,14 @@ test = ["objgraph", "psutil"] [[package]] name = "hubspot-api-client" -version = "11.1.0" +version = "12.0.0" description = "HubSpot API client" optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "hubspot_api_client-11.1.0-py3-none-any.whl", hash = "sha256:0c49e2c2f511a56d249c6890d2dfdd62afd04f66edc69a591e8348e28804634b"}, - {file = "hubspot_api_client-11.1.0.tar.gz", hash = "sha256:93ed914f1cd4dad67bacf26b8a1ec8506847483fa801cb2bddd4b2d887d0f825"}, + {file = "hubspot_api_client-12.0.0-py3-none-any.whl", hash = "sha256:5426627ff808fdf259d5b5e4791667a323a2a82d36a834e7e43ec8e8f021aa08"}, + {file = "hubspot_api_client-12.0.0.tar.gz", hash = "sha256:ddfc60f3a80565dd5bf7fc7554d0d2dabad37926f547436158258772f1a04e69"}, ] [package.dependencies] @@ -668,14 +652,14 @@ test = ["flaky", "ipyparallel", "pre-commit", "pytest (>=7.0)", "pytest-asyncio [[package]] name = "ipython" -version = "9.0.2" +version = "9.2.0" description = "IPython: Productive Interactive Computing" optional = false python-versions = ">=3.11" groups = ["dev"] files = [ - {file = "ipython-9.0.2-py3-none-any.whl", hash = "sha256:143ef3ea6fb1e1bffb4c74b114051de653ffb7737a3f7ab1670e657ca6ae8c44"}, - {file = "ipython-9.0.2.tar.gz", hash = "sha256:ec7b479e3e5656bf4f58c652c120494df1820f4f28f522fb7ca09e213c2aab52"}, + {file = "ipython-9.2.0-py3-none-any.whl", hash = "sha256:fef5e33c4a1ae0759e0bba5917c9db4eb8c53fee917b6a526bd973e1ca5159f6"}, + {file = "ipython-9.2.0.tar.gz", hash = "sha256:62a9373dbc12f28f9feaf4700d052195bf89806279fc8ca11f3f54017d04751b"}, ] [package.dependencies] @@ -696,7 +680,7 @@ black = ["black"] doc = ["docrepr", "exceptiongroup", "intersphinx_registry", "ipykernel", "ipython[test]", "matplotlib", "setuptools (>=18.5)", "sphinx (>=1.3)", "sphinx-rtd-theme", "sphinx_toml (==0.0.4)", "typing_extensions"] matplotlib = ["matplotlib"] test = ["packaging", "pytest", "pytest-asyncio (<0.22)", "testpath"] -test-extra = ["curio", "ipython[test]", "matplotlib (!=3.2.0)", "nbformat", "numpy (>=1.23)", "pandas", "trio"] +test-extra = ["curio", "ipykernel", "ipython[test]", "jupyter_ai", "matplotlib (!=3.2.0)", "nbclient", "nbformat", "numpy (>=1.23)", "pandas", "trio"] [[package]] name = "ipython-pygments-lexers" @@ -779,14 +763,14 @@ test = ["ipykernel", "pre-commit", "pytest (<8)", "pytest-cov", "pytest-timeout" [[package]] name = "mako" -version = "1.3.9" +version = "1.3.10" description = "A super-fast templating language that borrows the best ideas from the existing templating languages." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "Mako-1.3.9-py3-none-any.whl", hash = "sha256:95920acccb578427a9aa38e37a186b1e43156c87260d7ba18ca63aa4c7cbd3a1"}, - {file = "mako-1.3.9.tar.gz", hash = "sha256:b5d65ff3462870feec922dbccf38f6efb44e5714d7b593a656be86663d8600ac"}, + {file = "mako-1.3.10-py3-none-any.whl", hash = "sha256:baef24a52fc4fc514a0887ac600f9f1cff3d82c61d4d700a1fa84d597b88db59"}, + {file = "mako-1.3.10.tar.gz", hash = "sha256:99579a6f39583fa7e5630a28c3c1f440e4e97a414b80372649c0ce338da2ea28"}, ] [package.dependencies] @@ -897,14 +881,14 @@ files = [ [[package]] name = "msal" -version = "1.32.0" +version = "1.32.3" description = "The Microsoft Authentication Library (MSAL) for Python library enables your app to access the Microsoft Cloud by supporting authentication of users with Microsoft Azure Active Directory accounts (AAD) and Microsoft Accounts (MSA) using industry standard OAuth2 and OpenID Connect." optional = false python-versions = ">=3.7" groups = ["main"] files = [ - {file = "msal-1.32.0-py3-none-any.whl", hash = "sha256:9dbac5384a10bbbf4dae5c7ea0d707d14e087b92c5aa4954b3feaa2d1aa0bcb7"}, - {file = "msal-1.32.0.tar.gz", hash = "sha256:5445fe3af1da6be484991a7ab32eaa82461dc2347de105b76af92c610c3335c2"}, + {file = "msal-1.32.3-py3-none-any.whl", hash = "sha256:b2798db57760b1961b142f027ffb7c8169536bf77316e99a0df5c4aaebb11569"}, + {file = "msal-1.32.3.tar.gz", hash = "sha256:5eea038689c78a5a70ca8ecbe1245458b55a857bd096efb6989c69ba15985d35"}, ] [package.dependencies] @@ -929,67 +913,67 @@ files = [ [[package]] name = "numpy" -version = "2.2.4" +version = "2.2.5" description = "Fundamental package for array computing in Python" optional = false python-versions = ">=3.10" groups = ["main"] files = [ - {file = "numpy-2.2.4-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:8146f3550d627252269ac42ae660281d673eb6f8b32f113538e0cc2a9aed42b9"}, - {file = "numpy-2.2.4-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e642d86b8f956098b564a45e6f6ce68a22c2c97a04f5acd3f221f57b8cb850ae"}, - {file = "numpy-2.2.4-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:a84eda42bd12edc36eb5b53bbcc9b406820d3353f1994b6cfe453a33ff101775"}, - {file = "numpy-2.2.4-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:4ba5054787e89c59c593a4169830ab362ac2bee8a969249dc56e5d7d20ff8df9"}, - {file = "numpy-2.2.4-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7716e4a9b7af82c06a2543c53ca476fa0b57e4d760481273e09da04b74ee6ee2"}, - {file = "numpy-2.2.4-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:adf8c1d66f432ce577d0197dceaac2ac00c0759f573f28516246351c58a85020"}, - {file = "numpy-2.2.4-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:218f061d2faa73621fa23d6359442b0fc658d5b9a70801373625d958259eaca3"}, - {file = "numpy-2.2.4-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:df2f57871a96bbc1b69733cd4c51dc33bea66146b8c63cacbfed73eec0883017"}, - {file = "numpy-2.2.4-cp310-cp310-win32.whl", hash = "sha256:a0258ad1f44f138b791327961caedffbf9612bfa504ab9597157806faa95194a"}, - {file = "numpy-2.2.4-cp310-cp310-win_amd64.whl", hash = "sha256:0d54974f9cf14acf49c60f0f7f4084b6579d24d439453d5fc5805d46a165b542"}, - {file = "numpy-2.2.4-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:e9e0a277bb2eb5d8a7407e14688b85fd8ad628ee4e0c7930415687b6564207a4"}, - {file = "numpy-2.2.4-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9eeea959168ea555e556b8188da5fa7831e21d91ce031e95ce23747b7609f8a4"}, - {file = "numpy-2.2.4-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:bd3ad3b0a40e713fc68f99ecfd07124195333f1e689387c180813f0e94309d6f"}, - {file = "numpy-2.2.4-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:cf28633d64294969c019c6df4ff37f5698e8326db68cc2b66576a51fad634880"}, - {file = "numpy-2.2.4-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fa8fa7697ad1646b5c93de1719965844e004fcad23c91228aca1cf0800044a1"}, - {file = "numpy-2.2.4-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f4162988a360a29af158aeb4a2f4f09ffed6a969c9776f8f3bdee9b06a8ab7e5"}, - {file = "numpy-2.2.4-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:892c10d6a73e0f14935c31229e03325a7b3093fafd6ce0af704be7f894d95687"}, - {file = "numpy-2.2.4-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:db1f1c22173ac1c58db249ae48aa7ead29f534b9a948bc56828337aa84a32ed6"}, - {file = "numpy-2.2.4-cp311-cp311-win32.whl", hash = "sha256:ea2bb7e2ae9e37d96835b3576a4fa4b3a97592fbea8ef7c3587078b0068b8f09"}, - {file = "numpy-2.2.4-cp311-cp311-win_amd64.whl", hash = "sha256:f7de08cbe5551911886d1ab60de58448c6df0f67d9feb7d1fb21e9875ef95e91"}, - {file = "numpy-2.2.4-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a7b9084668aa0f64e64bd00d27ba5146ef1c3a8835f3bd912e7a9e01326804c4"}, - {file = "numpy-2.2.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:dbe512c511956b893d2dacd007d955a3f03d555ae05cfa3ff1c1ff6df8851854"}, - {file = "numpy-2.2.4-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:bb649f8b207ab07caebba230d851b579a3c8711a851d29efe15008e31bb4de24"}, - {file = "numpy-2.2.4-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:f34dc300df798742b3d06515aa2a0aee20941c13579d7a2f2e10af01ae4901ee"}, - {file = "numpy-2.2.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c3f7ac96b16955634e223b579a3e5798df59007ca43e8d451a0e6a50f6bfdfba"}, - {file = "numpy-2.2.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f92084defa704deadd4e0a5ab1dc52d8ac9e8a8ef617f3fbb853e79b0ea3592"}, - {file = "numpy-2.2.4-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7a4e84a6283b36632e2a5b56e121961f6542ab886bc9e12f8f9818b3c266bfbb"}, - {file = "numpy-2.2.4-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:11c43995255eb4127115956495f43e9343736edb7fcdb0d973defd9de14cd84f"}, - {file = "numpy-2.2.4-cp312-cp312-win32.whl", hash = "sha256:65ef3468b53269eb5fdb3a5c09508c032b793da03251d5f8722b1194f1790c00"}, - {file = "numpy-2.2.4-cp312-cp312-win_amd64.whl", hash = "sha256:2aad3c17ed2ff455b8eaafe06bcdae0062a1db77cb99f4b9cbb5f4ecb13c5146"}, - {file = "numpy-2.2.4-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1cf4e5c6a278d620dee9ddeb487dc6a860f9b199eadeecc567f777daace1e9e7"}, - {file = "numpy-2.2.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1974afec0b479e50438fc3648974268f972e2d908ddb6d7fb634598cdb8260a0"}, - {file = "numpy-2.2.4-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:79bd5f0a02aa16808fcbc79a9a376a147cc1045f7dfe44c6e7d53fa8b8a79392"}, - {file = "numpy-2.2.4-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:3387dd7232804b341165cedcb90694565a6015433ee076c6754775e85d86f1fc"}, - {file = "numpy-2.2.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6f527d8fdb0286fd2fd97a2a96c6be17ba4232da346931d967a0630050dfd298"}, - {file = "numpy-2.2.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bce43e386c16898b91e162e5baaad90c4b06f9dcbe36282490032cec98dc8ae7"}, - {file = "numpy-2.2.4-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:31504f970f563d99f71a3512d0c01a645b692b12a63630d6aafa0939e52361e6"}, - {file = "numpy-2.2.4-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:81413336ef121a6ba746892fad881a83351ee3e1e4011f52e97fba79233611fd"}, - {file = "numpy-2.2.4-cp313-cp313-win32.whl", hash = "sha256:f486038e44caa08dbd97275a9a35a283a8f1d2f0ee60ac260a1790e76660833c"}, - {file = "numpy-2.2.4-cp313-cp313-win_amd64.whl", hash = "sha256:207a2b8441cc8b6a2a78c9ddc64d00d20c303d79fba08c577752f080c4007ee3"}, - {file = "numpy-2.2.4-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:8120575cb4882318c791f839a4fd66161a6fa46f3f0a5e613071aae35b5dd8f8"}, - {file = "numpy-2.2.4-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:a761ba0fa886a7bb33c6c8f6f20213735cb19642c580a931c625ee377ee8bd39"}, - {file = "numpy-2.2.4-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:ac0280f1ba4a4bfff363a99a6aceed4f8e123f8a9b234c89140f5e894e452ecd"}, - {file = "numpy-2.2.4-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:879cf3a9a2b53a4672a168c21375166171bc3932b7e21f622201811c43cdd3b0"}, - {file = "numpy-2.2.4-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f05d4198c1bacc9124018109c5fba2f3201dbe7ab6e92ff100494f236209c960"}, - {file = "numpy-2.2.4-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e2f085ce2e813a50dfd0e01fbfc0c12bbe5d2063d99f8b29da30e544fb6483b8"}, - {file = "numpy-2.2.4-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:92bda934a791c01d6d9d8e038363c50918ef7c40601552a58ac84c9613a665bc"}, - {file = "numpy-2.2.4-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:ee4d528022f4c5ff67332469e10efe06a267e32f4067dc76bb7e2cddf3cd25ff"}, - {file = "numpy-2.2.4-cp313-cp313t-win32.whl", hash = "sha256:05c076d531e9998e7e694c36e8b349969c56eadd2cdcd07242958489d79a7286"}, - {file = "numpy-2.2.4-cp313-cp313t-win_amd64.whl", hash = "sha256:188dcbca89834cc2e14eb2f106c96d6d46f200fe0200310fc29089657379c58d"}, - {file = "numpy-2.2.4-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:7051ee569db5fbac144335e0f3b9c2337e0c8d5c9fee015f259a5bd70772b7e8"}, - {file = "numpy-2.2.4-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:ab2939cd5bec30a7430cbdb2287b63151b77cf9624de0532d629c9a1c59b1d5c"}, - {file = "numpy-2.2.4-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d0f35b19894a9e08639fd60a1ec1978cb7f5f7f1eace62f38dd36be8aecdef4d"}, - {file = "numpy-2.2.4-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:b4adfbbc64014976d2f91084915ca4e626fbf2057fb81af209c1a6d776d23e3d"}, - {file = "numpy-2.2.4.tar.gz", hash = "sha256:9ba03692a45d3eef66559efe1d1096c4b9b75c0986b5dff5530c378fb8331d4f"}, + {file = "numpy-2.2.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:1f4a922da1729f4c40932b2af4fe84909c7a6e167e6e99f71838ce3a29f3fe26"}, + {file = "numpy-2.2.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:b6f91524d31b34f4a5fee24f5bc16dcd1491b668798b6d85585d836c1e633a6a"}, + {file = "numpy-2.2.5-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:19f4718c9012e3baea91a7dba661dcab2451cda2550678dc30d53acb91a7290f"}, + {file = "numpy-2.2.5-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:eb7fd5b184e5d277afa9ec0ad5e4eb562ecff541e7f60e69ee69c8d59e9aeaba"}, + {file = "numpy-2.2.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6413d48a9be53e183eb06495d8e3b006ef8f87c324af68241bbe7a39e8ff54c3"}, + {file = "numpy-2.2.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7451f92eddf8503c9b8aa4fe6aa7e87fd51a29c2cfc5f7dbd72efde6c65acf57"}, + {file = "numpy-2.2.5-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:0bcb1d057b7571334139129b7f941588f69ce7c4ed15a9d6162b2ea54ded700c"}, + {file = "numpy-2.2.5-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:36ab5b23915887543441efd0417e6a3baa08634308894316f446027611b53bf1"}, + {file = "numpy-2.2.5-cp310-cp310-win32.whl", hash = "sha256:422cc684f17bc963da5f59a31530b3936f57c95a29743056ef7a7903a5dbdf88"}, + {file = "numpy-2.2.5-cp310-cp310-win_amd64.whl", hash = "sha256:e4f0b035d9d0ed519c813ee23e0a733db81ec37d2e9503afbb6e54ccfdee0fa7"}, + {file = "numpy-2.2.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c42365005c7a6c42436a54d28c43fe0e01ca11eb2ac3cefe796c25a5f98e5e9b"}, + {file = "numpy-2.2.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:498815b96f67dc347e03b719ef49c772589fb74b8ee9ea2c37feae915ad6ebda"}, + {file = "numpy-2.2.5-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:6411f744f7f20081b1b4e7112e0f4c9c5b08f94b9f086e6f0adf3645f85d3a4d"}, + {file = "numpy-2.2.5-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:9de6832228f617c9ef45d948ec1cd8949c482238d68b2477e6f642c33a7b0a54"}, + {file = "numpy-2.2.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:369e0d4647c17c9363244f3468f2227d557a74b6781cb62ce57cf3ef5cc7c610"}, + {file = "numpy-2.2.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:262d23f383170f99cd9191a7c85b9a50970fe9069b2f8ab5d786eca8a675d60b"}, + {file = "numpy-2.2.5-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:aa70fdbdc3b169d69e8c59e65c07a1c9351ceb438e627f0fdcd471015cd956be"}, + {file = "numpy-2.2.5-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:37e32e985f03c06206582a7323ef926b4e78bdaa6915095ef08070471865b906"}, + {file = "numpy-2.2.5-cp311-cp311-win32.whl", hash = "sha256:f5045039100ed58fa817a6227a356240ea1b9a1bc141018864c306c1a16d4175"}, + {file = "numpy-2.2.5-cp311-cp311-win_amd64.whl", hash = "sha256:b13f04968b46ad705f7c8a80122a42ae8f620536ea38cf4bdd374302926424dd"}, + {file = "numpy-2.2.5-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:ee461a4eaab4f165b68780a6a1af95fb23a29932be7569b9fab666c407969051"}, + {file = "numpy-2.2.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ec31367fd6a255dc8de4772bd1658c3e926d8e860a0b6e922b615e532d320ddc"}, + {file = "numpy-2.2.5-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:47834cde750d3c9f4e52c6ca28a7361859fcaf52695c7dc3cc1a720b8922683e"}, + {file = "numpy-2.2.5-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:2c1a1c6ccce4022383583a6ded7bbcda22fc635eb4eb1e0a053336425ed36dfa"}, + {file = "numpy-2.2.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9d75f338f5f79ee23548b03d801d28a505198297534f62416391857ea0479571"}, + {file = "numpy-2.2.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a801fef99668f309b88640e28d261991bfad9617c27beda4a3aec4f217ea073"}, + {file = "numpy-2.2.5-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:abe38cd8381245a7f49967a6010e77dbf3680bd3627c0fe4362dd693b404c7f8"}, + {file = "numpy-2.2.5-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:5a0ac90e46fdb5649ab6369d1ab6104bfe5854ab19b645bf5cda0127a13034ae"}, + {file = "numpy-2.2.5-cp312-cp312-win32.whl", hash = "sha256:0cd48122a6b7eab8f06404805b1bd5856200e3ed6f8a1b9a194f9d9054631beb"}, + {file = "numpy-2.2.5-cp312-cp312-win_amd64.whl", hash = "sha256:ced69262a8278547e63409b2653b372bf4baff0870c57efa76c5703fd6543282"}, + {file = "numpy-2.2.5-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:059b51b658f4414fff78c6d7b1b4e18283ab5fa56d270ff212d5ba0c561846f4"}, + {file = "numpy-2.2.5-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:47f9ed103af0bc63182609044b0490747e03bd20a67e391192dde119bf43d52f"}, + {file = "numpy-2.2.5-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:261a1ef047751bb02f29dfe337230b5882b54521ca121fc7f62668133cb119c9"}, + {file = "numpy-2.2.5-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:4520caa3807c1ceb005d125a75e715567806fed67e315cea619d5ec6e75a4191"}, + {file = "numpy-2.2.5-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3d14b17b9be5f9c9301f43d2e2a4886a33b53f4e6fdf9ca2f4cc60aeeee76372"}, + {file = "numpy-2.2.5-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2ba321813a00e508d5421104464510cc962a6f791aa2fca1c97b1e65027da80d"}, + {file = "numpy-2.2.5-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:a4cbdef3ddf777423060c6f81b5694bad2dc9675f110c4b2a60dc0181543fac7"}, + {file = "numpy-2.2.5-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:54088a5a147ab71a8e7fdfd8c3601972751ded0739c6b696ad9cb0343e21ab73"}, + {file = "numpy-2.2.5-cp313-cp313-win32.whl", hash = "sha256:c8b82a55ef86a2d8e81b63da85e55f5537d2157165be1cb2ce7cfa57b6aef38b"}, + {file = "numpy-2.2.5-cp313-cp313-win_amd64.whl", hash = "sha256:d8882a829fd779f0f43998e931c466802a77ca1ee0fe25a3abe50278616b1471"}, + {file = "numpy-2.2.5-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:e8b025c351b9f0e8b5436cf28a07fa4ac0204d67b38f01433ac7f9b870fa38c6"}, + {file = "numpy-2.2.5-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:8dfa94b6a4374e7851bbb6f35e6ded2120b752b063e6acdd3157e4d2bb922eba"}, + {file = "numpy-2.2.5-cp313-cp313t-macosx_14_0_arm64.whl", hash = "sha256:97c8425d4e26437e65e1d189d22dff4a079b747ff9c2788057bfb8114ce1e133"}, + {file = "numpy-2.2.5-cp313-cp313t-macosx_14_0_x86_64.whl", hash = "sha256:352d330048c055ea6db701130abc48a21bec690a8d38f8284e00fab256dc1376"}, + {file = "numpy-2.2.5-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8b4c0773b6ada798f51f0f8e30c054d32304ccc6e9c5d93d46cb26f3d385ab19"}, + {file = "numpy-2.2.5-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:55f09e00d4dccd76b179c0f18a44f041e5332fd0e022886ba1c0bbf3ea4a18d0"}, + {file = "numpy-2.2.5-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:02f226baeefa68f7d579e213d0f3493496397d8f1cff5e2b222af274c86a552a"}, + {file = "numpy-2.2.5-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:c26843fd58f65da9491165072da2cccc372530681de481ef670dcc8e27cfb066"}, + {file = "numpy-2.2.5-cp313-cp313t-win32.whl", hash = "sha256:1a161c2c79ab30fe4501d5a2bbfe8b162490757cf90b7f05be8b80bc02f7bb8e"}, + {file = "numpy-2.2.5-cp313-cp313t-win_amd64.whl", hash = "sha256:d403c84991b5ad291d3809bace5e85f4bbf44a04bdc9a88ed2bb1807b3360bb8"}, + {file = "numpy-2.2.5-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b4ea7e1cff6784e58fe281ce7e7f05036b3e1c89c6f922a6bfbc0a7e8768adbe"}, + {file = "numpy-2.2.5-pp310-pypy310_pp73-macosx_14_0_x86_64.whl", hash = "sha256:d7543263084a85fbc09c704b515395398d31d6395518446237eac219eab9e55e"}, + {file = "numpy-2.2.5-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0255732338c4fdd00996c0421884ea8a3651eea555c3a56b84892b66f696eb70"}, + {file = "numpy-2.2.5-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:d2e3bdadaba0e040d1e7ab39db73e0afe2c74ae277f5614dad53eadbecbbb169"}, + {file = "numpy-2.2.5.tar.gz", hash = "sha256:a9c0d994680cd991b1cb772e8b297340085466a6fe964bc9d4e80f5e2f43c291"}, ] [[package]] @@ -1009,14 +993,14 @@ et-xmlfile = "*" [[package]] name = "packaging" -version = "24.2" +version = "25.0" description = "Core utilities for Python packages" optional = false python-versions = ">=3.8" groups = ["main", "dev"] files = [ - {file = "packaging-24.2-py3-none-any.whl", hash = "sha256:09abb1bccd265c01f4a3aa3f7a7db064b36514d2cba19a2f694fe6150451a759"}, - {file = "packaging-24.2.tar.gz", hash = "sha256:c228a6dc5e932d346bc5739379109d49e8853dd8223571c7c5b55260edc0b97f"}, + {file = "packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484"}, + {file = "packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f"}, ] [[package]] @@ -1136,14 +1120,14 @@ ptyprocess = ">=0.5" [[package]] name = "platformdirs" -version = "4.3.7" +version = "4.3.8" description = "A small Python package for determining appropriate platform-specific dirs, e.g. a `user data dir`." optional = false python-versions = ">=3.9" groups = ["dev"] files = [ - {file = "platformdirs-4.3.7-py3-none-any.whl", hash = "sha256:a03875334331946f13c549dbd8f4bac7a13a50a895a0eb1e8c6a8ace80d40a94"}, - {file = "platformdirs-4.3.7.tar.gz", hash = "sha256:eb437d586b6a0986388f0d6f74aa0cde27b48d0e3d66843640bfb6bdcdb6e351"}, + {file = "platformdirs-4.3.8-py3-none-any.whl", hash = "sha256:ff7059bb7eb1179e2685604f4aaf157cfd9535242bd23742eadc3c13542139b4"}, + {file = "platformdirs-4.3.8.tar.gz", hash = "sha256:3d512d96e16bcb959a814c9f348431070822a6496326a4be0911c40b5a74c2bc"}, ] [package.extras] @@ -1169,14 +1153,14 @@ testing = ["pytest", "pytest-benchmark"] [[package]] name = "prompt-toolkit" -version = "3.0.50" +version = "3.0.51" description = "Library for building powerful interactive command lines in Python" optional = false -python-versions = ">=3.8.0" +python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "prompt_toolkit-3.0.50-py3-none-any.whl", hash = "sha256:9b6427eb19e479d98acff65196a307c555eb567989e6d88ebbb1b509d9779198"}, - {file = "prompt_toolkit-3.0.50.tar.gz", hash = "sha256:544748f3860a2623ca5cd6d2795e7a14f3d0e1c3c9728359013f79877fc89bab"}, + {file = "prompt_toolkit-3.0.51-py3-none-any.whl", hash = "sha256:52742911fde84e2d423e2f9a4cf1de7d7ac4e51958f648d9540e0fb8db077b07"}, + {file = "prompt_toolkit-3.0.51.tar.gz", hash = "sha256:931a162e3b27fc90c86f1b48bb1fb2c528c2761475e57c9c06de13311c7b54ed"}, ] [package.dependencies] @@ -1269,20 +1253,20 @@ markers = {main = "platform_python_implementation != \"PyPy\"", dev = "implement [[package]] name = "pydantic" -version = "2.11.0" +version = "2.11.4" description = "Data validation using Python type hints" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic-2.11.0-py3-none-any.whl", hash = "sha256:d52535bb7aba33c2af820eaefd866f3322daf39319d03374921cd17fbbdf28f9"}, - {file = "pydantic-2.11.0.tar.gz", hash = "sha256:d6a287cd6037dee72f0597229256dfa246c4d61567a250e99f86b7b4626e2f41"}, + {file = "pydantic-2.11.4-py3-none-any.whl", hash = "sha256:d9615eaa9ac5a063471da949c8fc16376a84afb5024688b3ff885693506764eb"}, + {file = "pydantic-2.11.4.tar.gz", hash = "sha256:32738d19d63a226a52eed76645a98ee07c1f410ee41d93b4afbfa85ed8111c2d"}, ] [package.dependencies] annotated-types = ">=0.6.0" email-validator = {version = ">=2.0.0", optional = true, markers = "extra == \"email\""} -pydantic-core = "2.33.0" +pydantic-core = "2.33.2" typing-extensions = ">=4.12.2" typing-inspection = ">=0.4.0" @@ -1292,111 +1276,111 @@ timezone = ["tzdata ; python_version >= \"3.9\" and platform_system == \"Windows [[package]] name = "pydantic-core" -version = "2.33.0" +version = "2.33.2" description = "Core functionality for Pydantic validation and serialization" optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic_core-2.33.0-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:71dffba8fe9ddff628c68f3abd845e91b028361d43c5f8e7b3f8b91d7d85413e"}, - {file = "pydantic_core-2.33.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:abaeec1be6ed535a5d7ffc2e6c390083c425832b20efd621562fbb5bff6dc518"}, - {file = "pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:759871f00e26ad3709efc773ac37b4d571de065f9dfb1778012908bcc36b3a73"}, - {file = "pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:dcfebee69cd5e1c0b76a17e17e347c84b00acebb8dd8edb22d4a03e88e82a207"}, - {file = "pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1b1262b912435a501fa04cd213720609e2cefa723a07c92017d18693e69bf00b"}, - {file = "pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4726f1f3f42d6a25678c67da3f0b10f148f5655813c5aca54b0d1742ba821b8f"}, - {file = "pydantic_core-2.33.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e790954b5093dff1e3a9a2523fddc4e79722d6f07993b4cd5547825c3cbf97b5"}, - {file = "pydantic_core-2.33.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:34e7fb3abe375b5c4e64fab75733d605dda0f59827752debc99c17cb2d5f3276"}, - {file = "pydantic_core-2.33.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:ecb158fb9b9091b515213bed3061eb7deb1d3b4e02327c27a0ea714ff46b0760"}, - {file = "pydantic_core-2.33.0-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:4d9149e7528af8bbd76cc055967e6e04617dcb2a2afdaa3dea899406c5521faa"}, - {file = "pydantic_core-2.33.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:e81a295adccf73477220e15ff79235ca9dcbcee4be459eb9d4ce9a2763b8386c"}, - {file = "pydantic_core-2.33.0-cp310-cp310-win32.whl", hash = "sha256:f22dab23cdbce2005f26a8f0c71698457861f97fc6318c75814a50c75e87d025"}, - {file = "pydantic_core-2.33.0-cp310-cp310-win_amd64.whl", hash = "sha256:9cb2390355ba084c1ad49485d18449b4242da344dea3e0fe10babd1f0db7dcfc"}, - {file = "pydantic_core-2.33.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:a608a75846804271cf9c83e40bbb4dab2ac614d33c6fd5b0c6187f53f5c593ef"}, - {file = "pydantic_core-2.33.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e1c69aa459f5609dec2fa0652d495353accf3eda5bdb18782bc5a2ae45c9273a"}, - {file = "pydantic_core-2.33.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b9ec80eb5a5f45a2211793f1c4aeddff0c3761d1c70d684965c1807e923a588b"}, - {file = "pydantic_core-2.33.0-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e925819a98318d17251776bd3d6aa9f3ff77b965762155bdad15d1a9265c4cfd"}, - {file = "pydantic_core-2.33.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5bf68bb859799e9cec3d9dd8323c40c00a254aabb56fe08f907e437005932f2b"}, - {file = "pydantic_core-2.33.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1b2ea72dea0825949a045fa4071f6d5b3d7620d2a208335207793cf29c5a182d"}, - {file = "pydantic_core-2.33.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1583539533160186ac546b49f5cde9ffc928062c96920f58bd95de32ffd7bffd"}, - {file = "pydantic_core-2.33.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:23c3e77bf8a7317612e5c26a3b084c7edeb9552d645742a54a5867635b4f2453"}, - {file = "pydantic_core-2.33.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:a7a7f2a3f628d2f7ef11cb6188bcf0b9e1558151d511b974dfea10a49afe192b"}, - {file = "pydantic_core-2.33.0-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:f1fb026c575e16f673c61c7b86144517705865173f3d0907040ac30c4f9f5915"}, - {file = "pydantic_core-2.33.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:635702b2fed997e0ac256b2cfbdb4dd0bf7c56b5d8fba8ef03489c03b3eb40e2"}, - {file = "pydantic_core-2.33.0-cp311-cp311-win32.whl", hash = "sha256:07b4ced28fccae3f00626eaa0c4001aa9ec140a29501770a88dbbb0966019a86"}, - {file = "pydantic_core-2.33.0-cp311-cp311-win_amd64.whl", hash = "sha256:4927564be53239a87770a5f86bdc272b8d1fbb87ab7783ad70255b4ab01aa25b"}, - {file = "pydantic_core-2.33.0-cp311-cp311-win_arm64.whl", hash = "sha256:69297418ad644d521ea3e1aa2e14a2a422726167e9ad22b89e8f1130d68e1e9a"}, - {file = "pydantic_core-2.33.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:6c32a40712e3662bebe524abe8abb757f2fa2000028d64cc5a1006016c06af43"}, - {file = "pydantic_core-2.33.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:8ec86b5baa36f0a0bfb37db86c7d52652f8e8aa076ab745ef7725784183c3fdd"}, - {file = "pydantic_core-2.33.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4deac83a8cc1d09e40683be0bc6d1fa4cde8df0a9bf0cda5693f9b0569ac01b6"}, - {file = "pydantic_core-2.33.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:175ab598fb457a9aee63206a1993874badf3ed9a456e0654273e56f00747bbd6"}, - {file = "pydantic_core-2.33.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5f36afd0d56a6c42cf4e8465b6441cf546ed69d3a4ec92724cc9c8c61bd6ecf4"}, - {file = "pydantic_core-2.33.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0a98257451164666afafc7cbf5fb00d613e33f7e7ebb322fbcd99345695a9a61"}, - {file = "pydantic_core-2.33.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ecc6d02d69b54a2eb83ebcc6f29df04957f734bcf309d346b4f83354d8376862"}, - {file = "pydantic_core-2.33.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a69b7596c6603afd049ce7f3835bcf57dd3892fc7279f0ddf987bebed8caa5a"}, - {file = "pydantic_core-2.33.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ea30239c148b6ef41364c6f51d103c2988965b643d62e10b233b5efdca8c0099"}, - {file = "pydantic_core-2.33.0-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:abfa44cf2f7f7d7a199be6c6ec141c9024063205545aa09304349781b9a125e6"}, - {file = "pydantic_core-2.33.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:20d4275f3c4659d92048c70797e5fdc396c6e4446caf517ba5cad2db60cd39d3"}, - {file = "pydantic_core-2.33.0-cp312-cp312-win32.whl", hash = "sha256:918f2013d7eadea1d88d1a35fd4a1e16aaf90343eb446f91cb091ce7f9b431a2"}, - {file = "pydantic_core-2.33.0-cp312-cp312-win_amd64.whl", hash = "sha256:aec79acc183865bad120b0190afac467c20b15289050648b876b07777e67ea48"}, - {file = "pydantic_core-2.33.0-cp312-cp312-win_arm64.whl", hash = "sha256:5461934e895968655225dfa8b3be79e7e927e95d4bd6c2d40edd2fa7052e71b6"}, - {file = "pydantic_core-2.33.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:f00e8b59e1fc8f09d05594aa7d2b726f1b277ca6155fc84c0396db1b373c4555"}, - {file = "pydantic_core-2.33.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:1a73be93ecef45786d7d95b0c5e9b294faf35629d03d5b145b09b81258c7cd6d"}, - {file = "pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff48a55be9da6930254565ff5238d71d5e9cd8c5487a191cb85df3bdb8c77365"}, - {file = "pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:26a4ea04195638dcd8c53dadb545d70badba51735b1594810e9768c2c0b4a5da"}, - {file = "pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:41d698dcbe12b60661f0632b543dbb119e6ba088103b364ff65e951610cb7ce0"}, - {file = "pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ae62032ef513fe6281ef0009e30838a01057b832dc265da32c10469622613885"}, - {file = "pydantic_core-2.33.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f225f3a3995dbbc26affc191d0443c6c4aa71b83358fd4c2b7d63e2f6f0336f9"}, - {file = "pydantic_core-2.33.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5bdd36b362f419c78d09630cbaebc64913f66f62bda6d42d5fbb08da8cc4f181"}, - {file = "pydantic_core-2.33.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:2a0147c0bef783fd9abc9f016d66edb6cac466dc54a17ec5f5ada08ff65caf5d"}, - {file = "pydantic_core-2.33.0-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:c860773a0f205926172c6644c394e02c25421dc9a456deff16f64c0e299487d3"}, - {file = "pydantic_core-2.33.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:138d31e3f90087f42aa6286fb640f3c7a8eb7bdae829418265e7e7474bd2574b"}, - {file = "pydantic_core-2.33.0-cp313-cp313-win32.whl", hash = "sha256:d20cbb9d3e95114325780f3cfe990f3ecae24de7a2d75f978783878cce2ad585"}, - {file = "pydantic_core-2.33.0-cp313-cp313-win_amd64.whl", hash = "sha256:ca1103d70306489e3d006b0f79db8ca5dd3c977f6f13b2c59ff745249431a606"}, - {file = "pydantic_core-2.33.0-cp313-cp313-win_arm64.whl", hash = "sha256:6291797cad239285275558e0a27872da735b05c75d5237bbade8736f80e4c225"}, - {file = "pydantic_core-2.33.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:7b79af799630af263eca9ec87db519426d8c9b3be35016eddad1832bac812d87"}, - {file = "pydantic_core-2.33.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eabf946a4739b5237f4f56d77fa6668263bc466d06a8036c055587c130a46f7b"}, - {file = "pydantic_core-2.33.0-cp313-cp313t-win_amd64.whl", hash = "sha256:8a1d581e8cdbb857b0e0e81df98603376c1a5c34dc5e54039dcc00f043df81e7"}, - {file = "pydantic_core-2.33.0-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:7c9c84749f5787781c1c45bb99f433402e484e515b40675a5d121ea14711cf61"}, - {file = "pydantic_core-2.33.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:64672fa888595a959cfeff957a654e947e65bbe1d7d82f550417cbd6898a1d6b"}, - {file = "pydantic_core-2.33.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:26bc7367c0961dec292244ef2549afa396e72e28cc24706210bd44d947582c59"}, - {file = "pydantic_core-2.33.0-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ce72d46eb201ca43994303025bd54d8a35a3fc2a3495fac653d6eb7205ce04f4"}, - {file = "pydantic_core-2.33.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:14229c1504287533dbf6b1fc56f752ce2b4e9694022ae7509631ce346158de11"}, - {file = "pydantic_core-2.33.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:085d8985b1c1e48ef271e98a658f562f29d89bda98bf120502283efbc87313eb"}, - {file = "pydantic_core-2.33.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31860fbda80d8f6828e84b4a4d129fd9c4535996b8249cfb8c720dc2a1a00bb8"}, - {file = "pydantic_core-2.33.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:f200b2f20856b5a6c3a35f0d4e344019f805e363416e609e9b47c552d35fd5ea"}, - {file = "pydantic_core-2.33.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f72914cfd1d0176e58ddc05c7a47674ef4222c8253bf70322923e73e14a4ac3"}, - {file = "pydantic_core-2.33.0-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:91301a0980a1d4530d4ba7e6a739ca1a6b31341252cb709948e0aca0860ce0ae"}, - {file = "pydantic_core-2.33.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:7419241e17c7fbe5074ba79143d5523270e04f86f1b3a0dff8df490f84c8273a"}, - {file = "pydantic_core-2.33.0-cp39-cp39-win32.whl", hash = "sha256:7a25493320203005d2a4dac76d1b7d953cb49bce6d459d9ae38e30dd9f29bc9c"}, - {file = "pydantic_core-2.33.0-cp39-cp39-win_amd64.whl", hash = "sha256:82a4eba92b7ca8af1b7d5ef5f3d9647eee94d1f74d21ca7c21e3a2b92e008358"}, - {file = "pydantic_core-2.33.0-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:e2762c568596332fdab56b07060c8ab8362c56cf2a339ee54e491cd503612c50"}, - {file = "pydantic_core-2.33.0-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:5bf637300ff35d4f59c006fff201c510b2b5e745b07125458a5389af3c0dff8c"}, - {file = "pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:62c151ce3d59ed56ebd7ce9ce5986a409a85db697d25fc232f8e81f195aa39a1"}, - {file = "pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ee65f0cc652261744fd07f2c6e6901c914aa6c5ff4dcfaf1136bc394d0dd26b"}, - {file = "pydantic_core-2.33.0-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:024d136ae44d233e6322027bbf356712b3940bee816e6c948ce4b90f18471b3d"}, - {file = "pydantic_core-2.33.0-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:e37f10f6d4bc67c58fbd727108ae1d8b92b397355e68519f1e4a7babb1473442"}, - {file = "pydantic_core-2.33.0-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:502ed542e0d958bd12e7c3e9a015bce57deaf50eaa8c2e1c439b512cb9db1e3a"}, - {file = "pydantic_core-2.33.0-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:715c62af74c236bf386825c0fdfa08d092ab0f191eb5b4580d11c3189af9d330"}, - {file = "pydantic_core-2.33.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:bccc06fa0372151f37f6b69834181aa9eb57cf8665ed36405fb45fbf6cac3bae"}, - {file = "pydantic_core-2.33.0-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5d8dc9f63a26f7259b57f46a7aab5af86b2ad6fbe48487500bb1f4b27e051e4c"}, - {file = "pydantic_core-2.33.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:30369e54d6d0113d2aa5aee7a90d17f225c13d87902ace8fcd7bbf99b19124db"}, - {file = "pydantic_core-2.33.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3eb479354c62067afa62f53bb387827bee2f75c9c79ef25eef6ab84d4b1ae3b"}, - {file = "pydantic_core-2.33.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0310524c833d91403c960b8a3cf9f46c282eadd6afd276c8c5edc617bd705dc9"}, - {file = "pydantic_core-2.33.0-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:eddb18a00bbb855325db27b4c2a89a4ba491cd6a0bd6d852b225172a1f54b36c"}, - {file = "pydantic_core-2.33.0-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:ade5dbcf8d9ef8f4b28e682d0b29f3008df9842bb5ac48ac2c17bc55771cc976"}, - {file = "pydantic_core-2.33.0-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:2c0afd34f928383e3fd25740f2050dbac9d077e7ba5adbaa2227f4d4f3c8da5c"}, - {file = "pydantic_core-2.33.0-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:7da333f21cd9df51d5731513a6d39319892947604924ddf2e24a4612975fb936"}, - {file = "pydantic_core-2.33.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:4b6d77c75a57f041c5ee915ff0b0bb58eabb78728b69ed967bc5b780e8f701b8"}, - {file = "pydantic_core-2.33.0-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:ba95691cf25f63df53c1d342413b41bd7762d9acb425df8858d7efa616c0870e"}, - {file = "pydantic_core-2.33.0-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:4f1ab031feb8676f6bd7c85abec86e2935850bf19b84432c64e3e239bffeb1ec"}, - {file = "pydantic_core-2.33.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:58c1151827eef98b83d49b6ca6065575876a02d2211f259fb1a6b7757bd24dd8"}, - {file = "pydantic_core-2.33.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a66d931ea2c1464b738ace44b7334ab32a2fd50be023d863935eb00f42be1778"}, - {file = "pydantic_core-2.33.0-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:0bcf0bab28995d483f6c8d7db25e0d05c3efa5cebfd7f56474359e7137f39856"}, - {file = "pydantic_core-2.33.0-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:89670d7a0045acb52be0566df5bc8b114ac967c662c06cf5e0c606e4aadc964b"}, - {file = "pydantic_core-2.33.0-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:b716294e721d8060908dbebe32639b01bfe61b15f9f57bcc18ca9a0e00d9520b"}, - {file = "pydantic_core-2.33.0-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fc53e05c16697ff0c1c7c2b98e45e131d4bfb78068fffff92a82d169cbb4c7b7"}, - {file = "pydantic_core-2.33.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:68504959253303d3ae9406b634997a2123a0b0c1da86459abbd0ffc921695eac"}, - {file = "pydantic_core-2.33.0.tar.gz", hash = "sha256:40eb8af662ba409c3cbf4a8150ad32ae73514cd7cb1f1a2113af39763dd616b3"}, + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_10_12_x86_64.whl", hash = "sha256:2b3d326aaef0c0399d9afffeb6367d5e26ddc24d351dbc9c636840ac355dc5d8"}, + {file = "pydantic_core-2.33.2-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:0e5b2671f05ba48b94cb90ce55d8bdcaaedb8ba00cc5359f6810fc918713983d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0069c9acc3f3981b9ff4cdfaf088e98d83440a4c7ea1bc07460af3d4dc22e72d"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d53b22f2032c42eaaf025f7c40c2e3b94568ae077a606f006d206a463bc69572"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0405262705a123b7ce9f0b92f123334d67b70fd1f20a9372b907ce1080c7ba02"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4b25d91e288e2c4e0662b8038a28c6a07eaac3e196cfc4ff69de4ea3db992a1b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6bdfe4b3789761f3bcb4b1ddf33355a71079858958e3a552f16d5af19768fef2"}, + {file = "pydantic_core-2.33.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:efec8db3266b76ef9607c2c4c419bdb06bf335ae433b80816089ea7585816f6a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:031c57d67ca86902726e0fae2214ce6770bbe2f710dc33063187a68744a5ecac"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_armv7l.whl", hash = "sha256:f8de619080e944347f5f20de29a975c2d815d9ddd8be9b9b7268e2e3ef68605a"}, + {file = "pydantic_core-2.33.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:73662edf539e72a9440129f231ed3757faab89630d291b784ca99237fb94db2b"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win32.whl", hash = "sha256:0a39979dcbb70998b0e505fb1556a1d550a0781463ce84ebf915ba293ccb7e22"}, + {file = "pydantic_core-2.33.2-cp310-cp310-win_amd64.whl", hash = "sha256:b0379a2b24882fef529ec3b4987cb5d003b9cda32256024e6fe1586ac45fc640"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:4c5b0a576fb381edd6d27f0a85915c6daf2f8138dc5c267a57c08a62900758c7"}, + {file = "pydantic_core-2.33.2-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:e799c050df38a639db758c617ec771fd8fb7a5f8eaaa4b27b101f266b216a246"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dc46a01bf8d62f227d5ecee74178ffc448ff4e5197c756331f71efcc66dc980f"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:a144d4f717285c6d9234a66778059f33a89096dfb9b39117663fd8413d582dcc"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:73cf6373c21bc80b2e0dc88444f41ae60b2f070ed02095754eb5a01df12256de"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:3dc625f4aa79713512d1976fe9f0bc99f706a9dee21dfd1810b4bbbf228d0e8a"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:881b21b5549499972441da4758d662aeea93f1923f953e9cbaff14b8b9565aef"}, + {file = "pydantic_core-2.33.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:bdc25f3681f7b78572699569514036afe3c243bc3059d3942624e936ec93450e"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:fe5b32187cbc0c862ee201ad66c30cf218e5ed468ec8dc1cf49dec66e160cc4d"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_armv7l.whl", hash = "sha256:bc7aee6f634a6f4a95676fcb5d6559a2c2a390330098dba5e5a5f28a2e4ada30"}, + {file = "pydantic_core-2.33.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:235f45e5dbcccf6bd99f9f472858849f73d11120d76ea8707115415f8e5ebebf"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win32.whl", hash = "sha256:6368900c2d3ef09b69cb0b913f9f8263b03786e5b2a387706c5afb66800efd51"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_amd64.whl", hash = "sha256:1e063337ef9e9820c77acc768546325ebe04ee38b08703244c1309cccc4f1bab"}, + {file = "pydantic_core-2.33.2-cp311-cp311-win_arm64.whl", hash = "sha256:6b99022f1d19bc32a4c2a0d544fc9a76e3be90f0b3f4af413f87d38749300e65"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:a7ec89dc587667f22b6a0b6579c249fca9026ce7c333fc142ba42411fa243cdc"}, + {file = "pydantic_core-2.33.2-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:3c6db6e52c6d70aa0d00d45cdb9b40f0433b96380071ea80b09277dba021ddf7"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e61206137cbc65e6d5256e1166f88331d3b6238e082d9f74613b9b765fb9025"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:eb8c529b2819c37140eb51b914153063d27ed88e3bdc31b71198a198e921e011"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:c52b02ad8b4e2cf14ca7b3d918f3eb0ee91e63b3167c32591e57c4317e134f8f"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:96081f1605125ba0855dfda83f6f3df5ec90c61195421ba72223de35ccfb2f88"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f57a69461af2a5fa6e6bbd7a5f60d3b7e6cebb687f55106933188e79ad155c1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:572c7e6c8bb4774d2ac88929e3d1f12bc45714ae5ee6d9a788a9fb35e60bb04b"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:db4b41f9bd95fbe5acd76d89920336ba96f03e149097365afe1cb092fceb89a1"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:fa854f5cf7e33842a892e5c73f45327760bc7bc516339fda888c75ae60edaeb6"}, + {file = "pydantic_core-2.33.2-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:5f483cfb75ff703095c59e365360cb73e00185e01aaea067cd19acffd2ab20ea"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win32.whl", hash = "sha256:9cb1da0f5a471435a7bc7e439b8a728e8b61e59784b2af70d7c169f8dd8ae290"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_amd64.whl", hash = "sha256:f941635f2a3d96b2973e867144fde513665c87f13fe0e193c158ac51bfaaa7b2"}, + {file = "pydantic_core-2.33.2-cp312-cp312-win_arm64.whl", hash = "sha256:cca3868ddfaccfbc4bfb1d608e2ccaaebe0ae628e1416aeb9c4d88c001bb45ab"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:1082dd3e2d7109ad8b7da48e1d4710c8d06c253cbc4a27c1cff4fbcaa97a9e3f"}, + {file = "pydantic_core-2.33.2-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:f517ca031dfc037a9c07e748cefd8d96235088b83b4f4ba8939105d20fa1dcd6"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a9f2c9dd19656823cb8250b0724ee9c60a82f3cdf68a080979d13092a3b0fef"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:2b0a451c263b01acebe51895bfb0e1cc842a5c666efe06cdf13846c7418caa9a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1ea40a64d23faa25e62a70ad163571c0b342b8bf66d5fa612ac0dec4f069d916"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0fb2d542b4d66f9470e8065c5469ec676978d625a8b7a363f07d9a501a9cb36a"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fdac5d6ffa1b5a83bca06ffe7583f5576555e6c8b3a91fbd25ea7780f825f7d"}, + {file = "pydantic_core-2.33.2-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:04a1a413977ab517154eebb2d326da71638271477d6ad87a769102f7c2488c56"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:c8e7af2f4e0194c22b5b37205bfb293d166a7344a5b0d0eaccebc376546d77d5"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:5c92edd15cd58b3c2d34873597a1e20f13094f59cf88068adb18947df5455b4e"}, + {file = "pydantic_core-2.33.2-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:65132b7b4a1c0beded5e057324b7e16e10910c106d43675d9bd87d4f38dde162"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win32.whl", hash = "sha256:52fb90784e0a242bb96ec53f42196a17278855b0f31ac7c3cc6f5c1ec4811849"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_amd64.whl", hash = "sha256:c083a3bdd5a93dfe480f1125926afcdbf2917ae714bdb80b36d34318b2bec5d9"}, + {file = "pydantic_core-2.33.2-cp313-cp313-win_arm64.whl", hash = "sha256:e80b087132752f6b3d714f041ccf74403799d3b23a72722ea2e6ba2e892555b9"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:61c18fba8e5e9db3ab908620af374db0ac1baa69f0f32df4f61ae23f15e586ac"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95237e53bb015f67b63c91af7518a62a8660376a6a0db19b89acc77a4d6199f5"}, + {file = "pydantic_core-2.33.2-cp313-cp313t-win_amd64.whl", hash = "sha256:c2fc0a768ef76c15ab9238afa6da7f69895bb5d1ee83aeea2e3509af4472d0b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_10_12_x86_64.whl", hash = "sha256:a2b911a5b90e0374d03813674bf0a5fbbb7741570dcd4b4e85a2e48d17def29d"}, + {file = "pydantic_core-2.33.2-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:6fa6dfc3e4d1f734a34710f391ae822e0a8eb8559a85c6979e14e65ee6ba2954"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c54c939ee22dc8e2d545da79fc5381f1c020d6d3141d3bd747eab59164dc89fb"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:53a57d2ed685940a504248187d5685e49eb5eef0f696853647bf37c418c538f7"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:09fb9dd6571aacd023fe6aaca316bd01cf60ab27240d7eb39ebd66a3a15293b4"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:0e6116757f7959a712db11f3e9c0a99ade00a5bbedae83cb801985aa154f071b"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8d55ab81c57b8ff8548c3e4947f119551253f4e3787a7bbc0b6b3ca47498a9d3"}, + {file = "pydantic_core-2.33.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c20c462aa4434b33a2661701b861604913f912254e441ab8d78d30485736115a"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:44857c3227d3fb5e753d5fe4a3420d6376fa594b07b621e220cd93703fe21782"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_armv7l.whl", hash = "sha256:eb9b459ca4df0e5c87deb59d37377461a538852765293f9e6ee834f0435a93b9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:9fcd347d2cc5c23b06de6d3b7b8275be558a0c90549495c699e379a80bf8379e"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win32.whl", hash = "sha256:83aa99b1285bc8f038941ddf598501a86f1536789740991d7d8756e34f1e74d9"}, + {file = "pydantic_core-2.33.2-cp39-cp39-win_amd64.whl", hash = "sha256:f481959862f57f29601ccced557cc2e817bce7533ab8e01a797a48b49c9692b3"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_10_12_x86_64.whl", hash = "sha256:5c4aa4e82353f65e548c476b37e64189783aa5384903bfea4f41580f255fddfa"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-macosx_11_0_arm64.whl", hash = "sha256:d946c8bf0d5c24bf4fe333af284c59a19358aa3ec18cb3dc4370080da1e8ad29"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:87b31b6846e361ef83fedb187bb5b4372d0da3f7e28d85415efa92d6125d6e6d"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aa9d91b338f2df0508606f7009fde642391425189bba6d8c653afd80fd6bb64e"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:2058a32994f1fde4ca0480ab9d1e75a0e8c87c22b53a3ae66554f9af78f2fe8c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:0e03262ab796d986f978f79c943fc5f620381be7287148b8010b4097f79a39ec"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:1a8695a8d00c73e50bff9dfda4d540b7dee29ff9b8053e38380426a85ef10052"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:fa754d1850735a0b0e03bcffd9d4b4343eb417e47196e4485d9cca326073a42c"}, + {file = "pydantic_core-2.33.2-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:a11c8d26a50bfab49002947d3d237abe4d9e4b5bdc8846a63537b6488e197808"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:dd14041875d09cc0f9308e37a6f8b65f5585cf2598a53aa0123df8b129d481f8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:d87c561733f66531dced0da6e864f44ebf89a8fba55f31407b00c2f7f9449593"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2f82865531efd18d6e07a04a17331af02cb7a651583c418df8266f17a63c6612"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2bfb5112df54209d820d7bf9317c7a6c9025ea52e49f46b6a2060104bba37de7"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:64632ff9d614e5eecfb495796ad51b0ed98c453e447a76bcbeeb69615079fc7e"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:f889f7a40498cc077332c7ab6b4608d296d852182211787d4f3ee377aaae66e8"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:de4b83bb311557e439b9e186f733f6c645b9417c84e2eb8203f3f820a4b988bf"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:82f68293f055f51b51ea42fafc74b6aad03e70e191799430b90c13d643059ebb"}, + {file = "pydantic_core-2.33.2-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:329467cecfb529c925cf2bbd4d60d2c509bc2fb52a20c1045bf09bb70971a9c1"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_10_12_x86_64.whl", hash = "sha256:87acbfcf8e90ca885206e98359d7dca4bcbb35abdc0ff66672a293e1d7a19101"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-macosx_11_0_arm64.whl", hash = "sha256:7f92c15cd1e97d4b12acd1cc9004fa092578acfa57b67ad5e43a197175d01a64"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d3f26877a748dc4251cfcfda9dfb5f13fcb034f5308388066bcfe9031b63ae7d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dac89aea9af8cd672fa7b510e7b8c33b0bba9a43186680550ccf23020f32d535"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:970919794d126ba8645f3837ab6046fb4e72bbc057b3709144066204c19a455d"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_aarch64.whl", hash = "sha256:3eb3fe62804e8f859c49ed20a8451342de53ed764150cb14ca71357c765dc2a6"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_armv7l.whl", hash = "sha256:3abcd9392a36025e3bd55f9bd38d908bd17962cc49bc6da8e7e96285336e2bca"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-musllinux_1_1_x86_64.whl", hash = "sha256:3a1c81334778f9e3af2f8aeb7a960736e5cab1dfebfb26aabca09afd2906c039"}, + {file = "pydantic_core-2.33.2-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:2807668ba86cb38c6817ad9bc66215ab8584d1d304030ce4f0887336f28a5e27"}, + {file = "pydantic_core-2.33.2.tar.gz", hash = "sha256:7cb8bc3605c29176e1b105350d2e6474142d7c1bd1d9327c4a9bdb46bf827acc"}, ] [package.dependencies] @@ -1404,22 +1388,25 @@ typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" [[package]] name = "pydantic-settings" -version = "2.8.1" +version = "2.9.1" description = "Settings management using Pydantic" optional = false -python-versions = ">=3.8" +python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pydantic_settings-2.8.1-py3-none-any.whl", hash = "sha256:81942d5ac3d905f7f3ee1a70df5dfb62d5569c12f51a5a647defc1c3d9ee2e9c"}, - {file = "pydantic_settings-2.8.1.tar.gz", hash = "sha256:d5c663dfbe9db9d5e1c646b2e161da12f0d734d422ee56f567d0ea2cee4e8585"}, + {file = "pydantic_settings-2.9.1-py3-none-any.whl", hash = "sha256:59b4f431b1defb26fe620c71a7d3968a710d719f5f4cdbbdb7926edeb770f6ef"}, + {file = "pydantic_settings-2.9.1.tar.gz", hash = "sha256:c509bf79d27563add44e8446233359004ed85066cd096d8b510f715e6ef5d268"}, ] [package.dependencies] pydantic = ">=2.7.0" python-dotenv = ">=0.21.0" +typing-inspection = ">=0.4.0" [package.extras] +aws-secrets-manager = ["boto3 (>=1.35.0)", "boto3-stubs[secretsmanager]"] azure-key-vault = ["azure-identity (>=1.16.0)", "azure-keyvault-secrets (>=4.8.0)"] +gcp-secret-manager = ["google-cloud-secret-manager (>=2.23.1)"] toml = ["tomli (>=2.0.1)"] yaml = ["pyyaml (>=6.0.1)"] @@ -1461,20 +1448,20 @@ tests = ["coverage[toml] (==5.0.4)", "pytest (>=6.0.0,<7.0.0)"] [[package]] name = "pymupdf" -version = "1.25.4" +version = "1.25.5" description = "A high performance Python library for data extraction, analysis, conversion & manipulation of PDF (and other) documents." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "pymupdf-1.25.4-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:b39ab5fc0cc793f2a93908cb4dc13182e61e9ace3476850ebf0e505f65355475"}, - {file = "pymupdf-1.25.4-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:72ac2a460e15a4b75fb22d0bdeb9eb9b3bd6ab44d5288e56f85ff9e4241d0830"}, - {file = "pymupdf-1.25.4-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bd16e264438b01bf6f6b8722deb49df20c0a9a7be2b4a8e29dcb7b5183d8b42a"}, - {file = "pymupdf-1.25.4-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:72817aa5f5023632346eee4aad0c32018d7879cf9a72e1820054dc9d8c48150f"}, - {file = "pymupdf-1.25.4-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:a8c23242d209512437187dc728001fc7c65c2dd9d37b50071f34c5cd745194a3"}, - {file = "pymupdf-1.25.4-cp39-abi3-win32.whl", hash = "sha256:d90e6087f9b153999fa9f2c4709488fb0355de0302ec502a76c4fc0c0a0c535e"}, - {file = "pymupdf-1.25.4-cp39-abi3-win_amd64.whl", hash = "sha256:af8fa1f7920b3b27343b2511cb9fc44baf2bad8b948db0b0f9d5b2fa589cb305"}, - {file = "pymupdf-1.25.4.tar.gz", hash = "sha256:5f189466b68901055a9ddc77dc1c91cba081a60964f0caa6ff5b9b87001a0194"}, + {file = "pymupdf-1.25.5-cp39-abi3-macosx_10_9_x86_64.whl", hash = "sha256:cde4e1c9cfb09c0e1e9c2b7f4b787dd6bb34a32cfe141a4675e24af7c0c25dd3"}, + {file = "pymupdf-1.25.5-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:5a35e2725fae0ab57f058dff77615c15eb5961eac50ba04f41ebc792cd8facad"}, + {file = "pymupdf-1.25.5-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d94b800e9501929c42283d39bc241001dd87fdeea297b5cb40d5b5714534452f"}, + {file = "pymupdf-1.25.5-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ee22155d3a634642d76553204867d862ae1bdd9f7cf70c0797d8127ebee6bed5"}, + {file = "pymupdf-1.25.5-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6ed7fc25271004d6d3279c20a80cb2bb4cda3efa9f9088dcc07cd790eca0bc63"}, + {file = "pymupdf-1.25.5-cp39-abi3-win32.whl", hash = "sha256:65e18ddb37fe8ec4edcdbebe9be3a8486b6a2f42609d0a142677e42f3a0614f8"}, + {file = "pymupdf-1.25.5-cp39-abi3-win_amd64.whl", hash = "sha256:7f44bc3d03ea45b2f68c96464f96105e8c7908896f2fb5e8c04f1fb8dae7981e"}, + {file = "pymupdf-1.25.5.tar.gz", hash = "sha256:5f96311cacd13254c905f6654a004a0a2025b71cabc04fda667f5472f72c15a0"}, ] [[package]] @@ -1569,105 +1556,105 @@ files = [ [[package]] name = "pyzmq" -version = "26.3.0" +version = "26.4.0" description = "Python bindings for 0MQ" optional = false python-versions = ">=3.8" groups = ["dev"] files = [ - {file = "pyzmq-26.3.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:1586944f4736515af5c6d3a5b150c7e8ca2a2d6e46b23057320584d6f2438f4a"}, - {file = "pyzmq-26.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:aa7efc695d1fc9f72d91bf9b6c6fe2d7e1b4193836ec530a98faf7d7a7577a58"}, - {file = "pyzmq-26.3.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:bd84441e4021cec6e4dd040550386cd9c9ea1d9418ea1a8002dbb7b576026b2b"}, - {file = "pyzmq-26.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9176856f36c34a8aa5c0b35ddf52a5d5cd8abeece57c2cd904cfddae3fd9acd3"}, - {file = "pyzmq-26.3.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:49334faa749d55b77f084389a80654bf2e68ab5191c0235066f0140c1b670d64"}, - {file = "pyzmq-26.3.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:fd30fc80fe96efb06bea21667c5793bbd65c0dc793187feb39b8f96990680b00"}, - {file = "pyzmq-26.3.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:b2eddfbbfb473a62c3a251bb737a6d58d91907f6e1d95791431ebe556f47d916"}, - {file = "pyzmq-26.3.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:70b3acb9ad729a53d4e751dace35404a024f188aad406013454216aba5485b4e"}, - {file = "pyzmq-26.3.0-cp310-cp310-win32.whl", hash = "sha256:c1bd75d692cd7c6d862a98013bfdf06702783b75cffbf5dae06d718fecefe8f2"}, - {file = "pyzmq-26.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:d7165bcda0dbf203e5ad04d79955d223d84b2263df4db92f525ba370b03a12ab"}, - {file = "pyzmq-26.3.0-cp310-cp310-win_arm64.whl", hash = "sha256:e34a63f71d2ecffb3c643909ad2d488251afeb5ef3635602b3448e609611a7ed"}, - {file = "pyzmq-26.3.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:2833602d9d42c94b9d0d2a44d2b382d3d3a4485be018ba19dddc401a464c617a"}, - {file = "pyzmq-26.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d8270d104ec7caa0bdac246d31d48d94472033ceab5ba142881704350b28159c"}, - {file = "pyzmq-26.3.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:c208a977843d18d3bd185f323e4eaa912eb4869cb230947dc6edd8a27a4e558a"}, - {file = "pyzmq-26.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eddc2be28a379c218e0d92e4a432805dcb0ca5870156a90b54c03cd9799f9f8a"}, - {file = "pyzmq-26.3.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:c0b519fa2159c42272f8a244354a0e110d65175647e5185b04008ec00df9f079"}, - {file = "pyzmq-26.3.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:1595533de3a80bf8363372c20bafa963ec4bf9f2b8f539b1d9a5017f430b84c9"}, - {file = "pyzmq-26.3.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:bbef99eb8d18ba9a40f00e8836b8040cdcf0f2fa649684cf7a66339599919d21"}, - {file = "pyzmq-26.3.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:979486d444ca3c469cd1c7f6a619ce48ff08b3b595d451937db543754bfacb65"}, - {file = "pyzmq-26.3.0-cp311-cp311-win32.whl", hash = "sha256:4b127cfe10b4c56e4285b69fd4b38ea1d368099ea4273d8fb349163fce3cd598"}, - {file = "pyzmq-26.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:cf736cc1298ef15280d9fcf7a25c09b05af016656856dc6fe5626fd8912658dd"}, - {file = "pyzmq-26.3.0-cp311-cp311-win_arm64.whl", hash = "sha256:2dc46ec09f5d36f606ac8393303149e69d17121beee13c8dac25e2a2078e31c4"}, - {file = "pyzmq-26.3.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:c80653332c6136da7f4d4e143975e74ac0fa14f851f716d90583bc19e8945cea"}, - {file = "pyzmq-26.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e317ee1d4528a03506cb1c282cd9db73660a35b3564096de37de7350e7d87a7"}, - {file = "pyzmq-26.3.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:943a22ebb3daacb45f76a9bcca9a7b74e7d94608c0c0505da30af900b998ca8d"}, - {file = "pyzmq-26.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3fc9e71490d989144981ea21ef4fdfaa7b6aa84aff9632d91c736441ce2f6b00"}, - {file = "pyzmq-26.3.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:e281a8071a06888575a4eb523c4deeefdcd2f5fe4a2d47e02ac8bf3a5b49f695"}, - {file = "pyzmq-26.3.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:be77efd735bb1064605be8dec6e721141c1421ef0b115ef54e493a64e50e9a52"}, - {file = "pyzmq-26.3.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:7a4ac2ffa34f1212dd586af90f4ba894e424f0cabb3a49cdcff944925640f6ac"}, - {file = "pyzmq-26.3.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ba698c7c252af83b6bba9775035263f0df5f807f0404019916d4b71af8161f66"}, - {file = "pyzmq-26.3.0-cp312-cp312-win32.whl", hash = "sha256:214038aaa88e801e54c2ef0cfdb2e6df27eb05f67b477380a452b595c5ecfa37"}, - {file = "pyzmq-26.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:bad7fe0372e505442482ca3ccbc0d6f38dae81b1650f57a0aa6bbee18e7df495"}, - {file = "pyzmq-26.3.0-cp312-cp312-win_arm64.whl", hash = "sha256:b7b578d604e79e99aa39495becea013fd043fa9f36e4b490efa951f3d847a24d"}, - {file = "pyzmq-26.3.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:fa85953df84beb7b8b73cb3ec3f5d92b62687a09a8e71525c6734e020edf56fd"}, - {file = "pyzmq-26.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:209d09f0ab6ddbcebe64630d1e6ca940687e736f443c265ae15bc4bfad833597"}, - {file = "pyzmq-26.3.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d35cc1086f1d4f907df85c6cceb2245cb39a04f69c3f375993363216134d76d4"}, - {file = "pyzmq-26.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b380e9087078ba91e45fb18cdd0c25275ffaa045cf63c947be0ddae6186bc9d9"}, - {file = "pyzmq-26.3.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6d64e74143587efe7c9522bb74d1448128fdf9897cc9b6d8b9927490922fd558"}, - {file = "pyzmq-26.3.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:efba4f53ac7752eea6d8ca38a4ddac579e6e742fba78d1e99c12c95cd2acfc64"}, - {file = "pyzmq-26.3.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:9b0137a1c40da3b7989839f9b78a44de642cdd1ce20dcef341de174c8d04aa53"}, - {file = "pyzmq-26.3.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:a995404bd3982c089e57b428c74edd5bfc3b0616b3dbcd6a8e270f1ee2110f36"}, - {file = "pyzmq-26.3.0-cp313-cp313-win32.whl", hash = "sha256:240b1634b9e530ef6a277d95cbca1a6922f44dfddc5f0a3cd6c722a8de867f14"}, - {file = "pyzmq-26.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:fe67291775ea4c2883764ba467eb389c29c308c56b86c1e19e49c9e1ed0cbeca"}, - {file = "pyzmq-26.3.0-cp313-cp313-win_arm64.whl", hash = "sha256:73ca9ae9a9011b714cf7650450cd9c8b61a135180b708904f1f0a05004543dce"}, - {file = "pyzmq-26.3.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:fea7efbd7e49af9d7e5ed6c506dfc7de3d1a628790bd3a35fd0e3c904dc7d464"}, - {file = "pyzmq-26.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c4430c7cba23bb0e2ee203eee7851c1654167d956fc6d4b3a87909ccaf3c5825"}, - {file = "pyzmq-26.3.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:016d89bee8c7d566fad75516b4e53ec7c81018c062d4c51cd061badf9539be52"}, - {file = "pyzmq-26.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:04bfe59852d76d56736bfd10ac1d49d421ab8ed11030b4a0332900691507f557"}, - {file = "pyzmq-26.3.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:1fe05bd0d633a0f672bb28cb8b4743358d196792e1caf04973b7898a0d70b046"}, - {file = "pyzmq-26.3.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:2aa1a9f236d5b835fb8642f27de95f9edcfd276c4bc1b6ffc84f27c6fb2e2981"}, - {file = "pyzmq-26.3.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:21399b31753bf321043ea60c360ed5052cc7be20739785b1dff1820f819e35b3"}, - {file = "pyzmq-26.3.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:d015efcd96aca8882057e7e6f06224f79eecd22cad193d3e6a0a91ec67590d1f"}, - {file = "pyzmq-26.3.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:18183cc3851b995fdc7e5f03d03b8a4e1b12b0f79dff1ec1da75069af6357a05"}, - {file = "pyzmq-26.3.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:da87e977f92d930a3683e10ba2b38bcc59adfc25896827e0b9d78b208b7757a6"}, - {file = "pyzmq-26.3.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:cf6db401f4957afbf372a4730c6d5b2a234393af723983cbf4bcd13d54c71e1a"}, - {file = "pyzmq-26.3.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:03caa2ffd64252122139d50ec92987f89616b9b92c9ba72920b40e92709d5e26"}, - {file = "pyzmq-26.3.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:fbf206e5329e20937fa19bd41cf3af06d5967f8f7e86b59d783b26b40ced755c"}, - {file = "pyzmq-26.3.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:6fb539a6382a048308b409d8c66d79bf636eda1b24f70c78f2a1fd16e92b037b"}, - {file = "pyzmq-26.3.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:7897b8c8bbbb2bd8cad887bffcb07aede71ef1e45383bd4d6ac049bf0af312a4"}, - {file = "pyzmq-26.3.0-cp38-cp38-win32.whl", hash = "sha256:91dead2daca698ae52ce70ee2adbb94ddd9b5f96877565fd40aa4efd18ecc6a3"}, - {file = "pyzmq-26.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:8c088e009a6d6b9f563336adb906e3a8d3fd64db129acc8d8fd0e9fe22b2dac8"}, - {file = "pyzmq-26.3.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:2eaed0d911fb3280981d5495978152fab6afd9fe217fd16f411523665089cef1"}, - {file = "pyzmq-26.3.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:7998b60ef1c105846fb3bfca494769fde3bba6160902e7cd27a8df8257890ee9"}, - {file = "pyzmq-26.3.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:96c0006a8d1d00e46cb44c8e8d7316d4a232f3d8f2ed43179d4578dbcb0829b6"}, - {file = "pyzmq-26.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5e17cc198dc50a25a0f245e6b1e56f692df2acec3ccae82d1f60c34bfb72bbec"}, - {file = "pyzmq-26.3.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:92a30840f4f2a31f7049d0a7de5fc69dd03b19bd5d8e7fed8d0bde49ce49b589"}, - {file = "pyzmq-26.3.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f52eba83272a26b444f4b8fc79f2e2c83f91d706d693836c9f7ccb16e6713c31"}, - {file = "pyzmq-26.3.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:952085a09ff32115794629ba47f8940896d7842afdef1283332109d38222479d"}, - {file = "pyzmq-26.3.0-cp39-cp39-win32.whl", hash = "sha256:0240289e33e3fbae44a5db73e54e955399179332a6b1d47c764a4983ec1524c3"}, - {file = "pyzmq-26.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:b2db7c82f08b8ce44c0b9d1153ce63907491972a7581e8b6adea71817f119df8"}, - {file = "pyzmq-26.3.0-cp39-cp39-win_arm64.whl", hash = "sha256:2d3459b6311463c96abcb97808ee0a1abb0d932833edb6aa81c30d622fd4a12d"}, - {file = "pyzmq-26.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ad03f4252d9041b0635c37528dfa3f44b39f46024ae28c8567f7423676ee409b"}, - {file = "pyzmq-26.3.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f3dfb68cf7bf4cfdf34283a75848e077c5defa4907506327282afe92780084d"}, - {file = "pyzmq-26.3.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:356ec0e39c5a9cda872b65aca1fd8a5d296ffdadf8e2442b70ff32e73ef597b1"}, - {file = "pyzmq-26.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:749d671b0eec8e738bbf0b361168369d8c682b94fcd458c20741dc4d69ef5278"}, - {file = "pyzmq-26.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:f950f17ae608e0786298340163cac25a4c5543ef25362dd5ddb6dcb10b547be9"}, - {file = "pyzmq-26.3.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:b4fc9903a73c25be9d5fe45c87faababcf3879445efa16140146b08fccfac017"}, - {file = "pyzmq-26.3.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c15b69af22030960ac63567e98ad8221cddf5d720d9cf03d85021dfd452324ef"}, - {file = "pyzmq-26.3.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2cf9ab0dff4dbaa2e893eb608373c97eb908e53b7d9793ad00ccbd082c0ee12f"}, - {file = "pyzmq-26.3.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ec332675f6a138db57aad93ae6387953763f85419bdbd18e914cb279ee1c451"}, - {file = "pyzmq-26.3.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:eb96568a22fe070590942cd4780950e2172e00fb033a8b76e47692583b1bd97c"}, - {file = "pyzmq-26.3.0-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:009a38241c76184cb004c869e82a99f0aee32eda412c1eb44df5820324a01d25"}, - {file = "pyzmq-26.3.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:4c22a12713707467abedc6d75529dd365180c4c2a1511268972c6e1d472bd63e"}, - {file = "pyzmq-26.3.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:1614fcd116275d24f2346ffca4047a741c546ad9d561cbf7813f11226ca4ed2c"}, - {file = "pyzmq-26.3.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4e2cafe7e9c7fed690e8ecf65af119f9c482923b5075a78f6f7629c63e1b4b1d"}, - {file = "pyzmq-26.3.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:14e0b81753424bd374075df6cc30b87f2c99e5f022501d97eff66544ca578941"}, - {file = "pyzmq-26.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:21c6ddb98557a77cfe3366af0c5600fb222a1b2de5f90d9cd052b324e0c295e8"}, - {file = "pyzmq-26.3.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1fc81d5d60c9d40e692de14b8d884d43cf67562402b931681f0ccb3ce6b19875"}, - {file = "pyzmq-26.3.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:52b064fafef772d0f5dbf52d4c39f092be7bc62d9a602fe6e82082e001326de3"}, - {file = "pyzmq-26.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b72206eb041f780451c61e1e89dbc3705f3d66aaaa14ee320d4f55864b13358a"}, - {file = "pyzmq-26.3.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:8ab78dc21c7b1e13053086bcf0b4246440b43b5409904b73bfd1156654ece8a1"}, - {file = "pyzmq-26.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:0b42403ad7d1194dca9574cd3c56691c345f4601fa2d0a33434f35142baec7ac"}, - {file = "pyzmq-26.3.0.tar.gz", hash = "sha256:f1cd68b8236faab78138a8fc703f7ca0ad431b17a3fcac696358600d4e6243b3"}, + {file = "pyzmq-26.4.0-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:0329bdf83e170ac133f44a233fc651f6ed66ef8e66693b5af7d54f45d1ef5918"}, + {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:398a825d2dea96227cf6460ce0a174cf7657d6f6827807d4d1ae9d0f9ae64315"}, + {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6d52d62edc96787f5c1dfa6c6ccff9b581cfae5a70d94ec4c8da157656c73b5b"}, + {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1410c3a3705db68d11eb2424d75894d41cff2f64d948ffe245dd97a9debfebf4"}, + {file = "pyzmq-26.4.0-cp310-cp310-manylinux_2_28_x86_64.whl", hash = "sha256:7dacb06a9c83b007cc01e8e5277f94c95c453c5851aac5e83efe93e72226353f"}, + {file = "pyzmq-26.4.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:6bab961c8c9b3a4dc94d26e9b2cdf84de9918931d01d6ff38c721a83ab3c0ef5"}, + {file = "pyzmq-26.4.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7a5c09413b924d96af2aa8b57e76b9b0058284d60e2fc3730ce0f979031d162a"}, + {file = "pyzmq-26.4.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7d489ac234d38e57f458fdbd12a996bfe990ac028feaf6f3c1e81ff766513d3b"}, + {file = "pyzmq-26.4.0-cp310-cp310-win32.whl", hash = "sha256:dea1c8db78fb1b4b7dc9f8e213d0af3fc8ecd2c51a1d5a3ca1cde1bda034a980"}, + {file = "pyzmq-26.4.0-cp310-cp310-win_amd64.whl", hash = "sha256:fa59e1f5a224b5e04dc6c101d7186058efa68288c2d714aa12d27603ae93318b"}, + {file = "pyzmq-26.4.0-cp310-cp310-win_arm64.whl", hash = "sha256:a651fe2f447672f4a815e22e74630b6b1ec3a1ab670c95e5e5e28dcd4e69bbb5"}, + {file = "pyzmq-26.4.0-cp311-cp311-macosx_10_15_universal2.whl", hash = "sha256:bfcf82644c9b45ddd7cd2a041f3ff8dce4a0904429b74d73a439e8cab1bd9e54"}, + {file = "pyzmq-26.4.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e9bcae3979b2654d5289d3490742378b2f3ce804b0b5fd42036074e2bf35b030"}, + {file = "pyzmq-26.4.0-cp311-cp311-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ccdff8ac4246b6fb60dcf3982dfaeeff5dd04f36051fe0632748fc0aa0679c01"}, + {file = "pyzmq-26.4.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4550af385b442dc2d55ab7717837812799d3674cb12f9a3aa897611839c18e9e"}, + {file = "pyzmq-26.4.0-cp311-cp311-manylinux_2_28_x86_64.whl", hash = "sha256:2f9f7ffe9db1187a253fca95191854b3fda24696f086e8789d1d449308a34b88"}, + {file = "pyzmq-26.4.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:3709c9ff7ba61589b7372923fd82b99a81932b592a5c7f1a24147c91da9a68d6"}, + {file = "pyzmq-26.4.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:f8f3c30fb2d26ae5ce36b59768ba60fb72507ea9efc72f8f69fa088450cff1df"}, + {file = "pyzmq-26.4.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:382a4a48c8080e273427fc692037e3f7d2851959ffe40864f2db32646eeb3cef"}, + {file = "pyzmq-26.4.0-cp311-cp311-win32.whl", hash = "sha256:d56aad0517d4c09e3b4f15adebba8f6372c5102c27742a5bdbfc74a7dceb8fca"}, + {file = "pyzmq-26.4.0-cp311-cp311-win_amd64.whl", hash = "sha256:963977ac8baed7058c1e126014f3fe58b3773f45c78cce7af5c26c09b6823896"}, + {file = "pyzmq-26.4.0-cp311-cp311-win_arm64.whl", hash = "sha256:c0c8e8cadc81e44cc5088fcd53b9b3b4ce9344815f6c4a03aec653509296fae3"}, + {file = "pyzmq-26.4.0-cp312-cp312-macosx_10_15_universal2.whl", hash = "sha256:5227cb8da4b6f68acfd48d20c588197fd67745c278827d5238c707daf579227b"}, + {file = "pyzmq-26.4.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e1c07a7fa7f7ba86554a2b1bef198c9fed570c08ee062fd2fd6a4dcacd45f905"}, + {file = "pyzmq-26.4.0-cp312-cp312-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ae775fa83f52f52de73183f7ef5395186f7105d5ed65b1ae65ba27cb1260de2b"}, + {file = "pyzmq-26.4.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:66c760d0226ebd52f1e6b644a9e839b5db1e107a23f2fcd46ec0569a4fdd4e63"}, + {file = "pyzmq-26.4.0-cp312-cp312-manylinux_2_28_x86_64.whl", hash = "sha256:ef8c6ecc1d520debc147173eaa3765d53f06cd8dbe7bd377064cdbc53ab456f5"}, + {file = "pyzmq-26.4.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:3150ef4084e163dec29ae667b10d96aad309b668fac6810c9e8c27cf543d6e0b"}, + {file = "pyzmq-26.4.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:4448c9e55bf8329fa1dcedd32f661bf611214fa70c8e02fee4347bc589d39a84"}, + {file = "pyzmq-26.4.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:e07dde3647afb084d985310d067a3efa6efad0621ee10826f2cb2f9a31b89d2f"}, + {file = "pyzmq-26.4.0-cp312-cp312-win32.whl", hash = "sha256:ba034a32ecf9af72adfa5ee383ad0fd4f4e38cdb62b13624278ef768fe5b5b44"}, + {file = "pyzmq-26.4.0-cp312-cp312-win_amd64.whl", hash = "sha256:056a97aab4064f526ecb32f4343917a4022a5d9efb6b9df990ff72e1879e40be"}, + {file = "pyzmq-26.4.0-cp312-cp312-win_arm64.whl", hash = "sha256:2f23c750e485ce1eb639dbd576d27d168595908aa2d60b149e2d9e34c9df40e0"}, + {file = "pyzmq-26.4.0-cp313-cp313-macosx_10_15_universal2.whl", hash = "sha256:c43fac689880f5174d6fc864857d1247fe5cfa22b09ed058a344ca92bf5301e3"}, + {file = "pyzmq-26.4.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:902aca7eba477657c5fb81c808318460328758e8367ecdd1964b6330c73cae43"}, + {file = "pyzmq-26.4.0-cp313-cp313-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e5e48a830bfd152fe17fbdeaf99ac5271aa4122521bf0d275b6b24e52ef35eb6"}, + {file = "pyzmq-26.4.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:31be2b6de98c824c06f5574331f805707c667dc8f60cb18580b7de078479891e"}, + {file = "pyzmq-26.4.0-cp313-cp313-manylinux_2_28_x86_64.whl", hash = "sha256:6332452034be001bbf3206ac59c0d2a7713de5f25bb38b06519fc6967b7cf771"}, + {file = "pyzmq-26.4.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:da8c0f5dd352136853e6a09b1b986ee5278dfddfebd30515e16eae425c872b30"}, + {file = "pyzmq-26.4.0-cp313-cp313-musllinux_1_1_i686.whl", hash = "sha256:f4ccc1a0a2c9806dda2a2dd118a3b7b681e448f3bb354056cad44a65169f6d86"}, + {file = "pyzmq-26.4.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:1c0b5fceadbab461578daf8d1dcc918ebe7ddd2952f748cf30c7cf2de5d51101"}, + {file = "pyzmq-26.4.0-cp313-cp313-win32.whl", hash = "sha256:28e2b0ff5ba4b3dd11062d905682bad33385cfa3cc03e81abd7f0822263e6637"}, + {file = "pyzmq-26.4.0-cp313-cp313-win_amd64.whl", hash = "sha256:23ecc9d241004c10e8b4f49d12ac064cd7000e1643343944a10df98e57bc544b"}, + {file = "pyzmq-26.4.0-cp313-cp313-win_arm64.whl", hash = "sha256:1edb0385c7f025045d6e0f759d4d3afe43c17a3d898914ec6582e6f464203c08"}, + {file = "pyzmq-26.4.0-cp313-cp313t-macosx_10_15_universal2.whl", hash = "sha256:93a29e882b2ba1db86ba5dd5e88e18e0ac6b627026c5cfbec9983422011b82d4"}, + {file = "pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb45684f276f57110bb89e4300c00f1233ca631f08f5f42528a5c408a79efc4a"}, + {file = "pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f72073e75260cb301aad4258ad6150fa7f57c719b3f498cb91e31df16784d89b"}, + {file = "pyzmq-26.4.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:be37e24b13026cfedd233bcbbccd8c0bcd2fdd186216094d095f60076201538d"}, + {file = "pyzmq-26.4.0-cp313-cp313t-manylinux_2_28_x86_64.whl", hash = "sha256:237b283044934d26f1eeff4075f751b05d2f3ed42a257fc44386d00df6a270cf"}, + {file = "pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_aarch64.whl", hash = "sha256:b30f862f6768b17040929a68432c8a8be77780317f45a353cb17e423127d250c"}, + {file = "pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_i686.whl", hash = "sha256:c80fcd3504232f13617c6ab501124d373e4895424e65de8b72042333316f64a8"}, + {file = "pyzmq-26.4.0-cp313-cp313t-musllinux_1_1_x86_64.whl", hash = "sha256:26a2a7451606b87f67cdeca2c2789d86f605da08b4bd616b1a9981605ca3a364"}, + {file = "pyzmq-26.4.0-cp38-cp38-macosx_10_15_universal2.whl", hash = "sha256:831cc53bf6068d46d942af52fa8b0b9d128fb39bcf1f80d468dc9a3ae1da5bfb"}, + {file = "pyzmq-26.4.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:51d18be6193c25bd229524cfac21e39887c8d5e0217b1857998dfbef57c070a4"}, + {file = "pyzmq-26.4.0-cp38-cp38-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:445c97854204119ae2232503585ebb4fa7517142f71092cb129e5ee547957a1f"}, + {file = "pyzmq-26.4.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:807b8f4ad3e6084412c0f3df0613269f552110fa6fb91743e3e306223dbf11a6"}, + {file = "pyzmq-26.4.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:c01d109dd675ac47fa15c0a79d256878d898f90bc10589f808b62d021d2e653c"}, + {file = "pyzmq-26.4.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:0a294026e28679a8dd64c922e59411cb586dad307661b4d8a5c49e7bbca37621"}, + {file = "pyzmq-26.4.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:22c8dd677274af8dfb1efd05006d6f68fb2f054b17066e308ae20cb3f61028cf"}, + {file = "pyzmq-26.4.0-cp38-cp38-win32.whl", hash = "sha256:14fc678b696bc42c14e2d7f86ac4e97889d5e6b94d366ebcb637a768d2ad01af"}, + {file = "pyzmq-26.4.0-cp38-cp38-win_amd64.whl", hash = "sha256:d1ef0a536662bbbdc8525f7e2ef19e74123ec9c4578e0582ecd41aedc414a169"}, + {file = "pyzmq-26.4.0-cp39-cp39-macosx_10_15_universal2.whl", hash = "sha256:a88643de8abd000ce99ca72056a1a2ae15881ee365ecb24dd1d9111e43d57842"}, + {file = "pyzmq-26.4.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:0a744ce209ecb557406fb928f3c8c55ce79b16c3eeb682da38ef5059a9af0848"}, + {file = "pyzmq-26.4.0-cp39-cp39-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:9434540f333332224ecb02ee6278b6c6f11ea1266b48526e73c903119b2f420f"}, + {file = "pyzmq-26.4.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e6c6f0a23e55cd38d27d4c89add963294ea091ebcb104d7fdab0f093bc5abb1c"}, + {file = "pyzmq-26.4.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:6145df55dc2309f6ef72d70576dcd5aabb0fd373311613fe85a5e547c722b780"}, + {file = "pyzmq-26.4.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:2ea81823840ef8c56e5d2f9918e4d571236294fea4d1842b302aebffb9e40997"}, + {file = "pyzmq-26.4.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:cc2abc385dc37835445abe206524fbc0c9e3fce87631dfaa90918a1ba8f425eb"}, + {file = "pyzmq-26.4.0-cp39-cp39-win32.whl", hash = "sha256:41a2508fe7bed4c76b4cf55aacfb8733926f59d440d9ae2b81ee8220633b4d12"}, + {file = "pyzmq-26.4.0-cp39-cp39-win_amd64.whl", hash = "sha256:d4000e8255d6cbce38982e5622ebb90823f3409b7ffe8aeae4337ef7d6d2612a"}, + {file = "pyzmq-26.4.0-cp39-cp39-win_arm64.whl", hash = "sha256:b4f6919d9c120488246bdc2a2f96662fa80d67b35bd6d66218f457e722b3ff64"}, + {file = "pyzmq-26.4.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:98d948288ce893a2edc5ec3c438fe8de2daa5bbbd6e2e865ec5f966e237084ba"}, + {file = "pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a9f34f5c9e0203ece706a1003f1492a56c06c0632d86cb77bcfe77b56aacf27b"}, + {file = "pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:80c9b48aef586ff8b698359ce22f9508937c799cc1d2c9c2f7c95996f2300c94"}, + {file = "pyzmq-26.4.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f3f2a5b74009fd50b53b26f65daff23e9853e79aa86e0aa08a53a7628d92d44a"}, + {file = "pyzmq-26.4.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:61c5f93d7622d84cb3092d7f6398ffc77654c346545313a3737e266fc11a3beb"}, + {file = "pyzmq-26.4.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:4478b14cb54a805088299c25a79f27eaf530564a7a4f72bf432a040042b554eb"}, + {file = "pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8a28ac29c60e4ba84b5f58605ace8ad495414a724fe7aceb7cf06cd0598d04e1"}, + {file = "pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43b03c1ceea27c6520124f4fb2ba9c647409b9abdf9a62388117148a90419494"}, + {file = "pyzmq-26.4.0-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7731abd23a782851426d4e37deb2057bf9410848a4459b5ede4fe89342e687a9"}, + {file = "pyzmq-26.4.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:a222ad02fbe80166b0526c038776e8042cd4e5f0dec1489a006a1df47e9040e0"}, + {file = "pyzmq-26.4.0-pp38-pypy38_pp73-macosx_10_15_x86_64.whl", hash = "sha256:91c3ffaea475ec8bb1a32d77ebc441dcdd13cd3c4c284a6672b92a0f5ade1917"}, + {file = "pyzmq-26.4.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.whl", hash = "sha256:d9a78a52668bf5c9e7b0da36aa5760a9fc3680144e1445d68e98df78a25082ed"}, + {file = "pyzmq-26.4.0-pp38-pypy38_pp73-manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:b70cab356ff8c860118b89dc86cd910c73ce2127eb986dada4fbac399ef644cf"}, + {file = "pyzmq-26.4.0-pp38-pypy38_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:acae207d4387780838192326b32d373bb286da0b299e733860e96f80728eb0af"}, + {file = "pyzmq-26.4.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:f928eafd15794aa4be75463d537348b35503c1e014c5b663f206504ec1a90fe4"}, + {file = "pyzmq-26.4.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:552b0d2e39987733e1e9e948a0ced6ff75e0ea39ab1a1db2fc36eb60fd8760db"}, + {file = "pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dd670a8aa843f2ee637039bbd412e0d7294a5e588e1ecc9ad98b0cdc050259a4"}, + {file = "pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d367b7b775a0e1e54a59a2ba3ed4d5e0a31566af97cc9154e34262777dab95ed"}, + {file = "pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8112af16c406e4a93df2caef49f884f4c2bb2b558b0b5577ef0b2465d15c1abc"}, + {file = "pyzmq-26.4.0-pp39-pypy39_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:c76c298683f82669cab0b6da59071f55238c039738297c69f187a542c6d40099"}, + {file = "pyzmq-26.4.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:49b6ca2e625b46f499fb081aaf7819a177f41eeb555acb05758aa97f4f95d147"}, + {file = "pyzmq-26.4.0.tar.gz", hash = "sha256:4bd13f85f80962f91a651a7356fe0472791a5f7a92f227822b5acf44795c626d"}, ] [package.dependencies] @@ -1709,14 +1696,14 @@ files = [ [[package]] name = "soupsieve" -version = "2.6" +version = "2.7" description = "A modern CSS selector implementation for Beautiful Soup." optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "soupsieve-2.6-py3-none-any.whl", hash = "sha256:e72c4ff06e4fb6e4b5a9f0f55fe6e81514581fca1515028625d0f299c602ccc9"}, - {file = "soupsieve-2.6.tar.gz", hash = "sha256:e2e68417777af359ec65daac1057404a3c8a5455bb8abc36f1a9866ab1a51abb"}, + {file = "soupsieve-2.7-py3-none-any.whl", hash = "sha256:6e60cc5c1ffaf1cebcc12e8188320b72071e922c2e897f737cadce79ad5d30c4"}, + {file = "soupsieve-2.7.tar.gz", hash = "sha256:ad282f9b6926286d2ead4750552c8a6142bc4c783fd66b0293547c8fe6ae126a"}, ] [[package]] @@ -1912,14 +1899,14 @@ test = ["argcomplete (>=3.0.3)", "mypy (>=1.7.0)", "pre-commit", "pytest (>=7.0, [[package]] name = "typing-extensions" -version = "4.13.0" +version = "4.13.2" description = "Backported and Experimental Type Hints for Python 3.8+" optional = false python-versions = ">=3.8" groups = ["main"] files = [ - {file = "typing_extensions-4.13.0-py3-none-any.whl", hash = "sha256:c8dd92cc0d6425a97c18fbb9d1954e5ff92c1ca881a309c45f06ebc0b79058e5"}, - {file = "typing_extensions-4.13.0.tar.gz", hash = "sha256:0a4ac55a5820789d87e297727d229866c9650f6521b64206413c4fbada24d95b"}, + {file = "typing_extensions-4.13.2-py3-none-any.whl", hash = "sha256:a439e7c04b49fec3e5d3e2beaa21755cadbbdc391694e28ccdd36ca4a1408f8c"}, + {file = "typing_extensions-4.13.2.tar.gz", hash = "sha256:e6c81219bd689f51865d9e372991c540bda33a0379d5573cddb9a3a23f7caaef"}, ] [[package]] @@ -1951,14 +1938,14 @@ files = [ [[package]] name = "urllib3" -version = "2.3.0" +version = "2.4.0" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.9" groups = ["main"] files = [ - {file = "urllib3-2.3.0-py3-none-any.whl", hash = "sha256:1cee9ad369867bfdbbb48b7dd50374c0967a0bb7710050facf0dd6911440e3df"}, - {file = "urllib3-2.3.0.tar.gz", hash = "sha256:f8c5449b3cf0861679ce7e0503c7b44b5ec981bec0d1d3795a07f1ba96f0204d"}, + {file = "urllib3-2.4.0-py3-none-any.whl", hash = "sha256:4e16665048960a0900c702d4a66415956a584919c03361cac9f1df5c5dd7e813"}, + {file = "urllib3-2.4.0.tar.gz", hash = "sha256:414bc6535b787febd7567804cc015fee39daab8ad86268f1310a9250697de466"}, ] [package.extras] @@ -1982,4 +1969,4 @@ files = [ [metadata] lock-version = "2.1" python-versions = ">=3.12" -content-hash = "b5221708d5a15633f7272103bf12970d3da3b05f5861b3e6f3fdfd2b42d8ddad" +content-hash = "1d5c1e0bfc12e88ca9b4c46141c848064a45e9cc4b60990fa3ec7ecb5ef71209" diff --git a/pyproject.toml b/pyproject.toml index 096377a..56b1bb8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -19,10 +19,10 @@ dependencies = [ "pydantic-settings (>=2.8.1,<3.0.0)", "alembic (>=1.15.1,<2.0.0)", "pytest (>=8.3.5,<9.0.0)", - "hubspot-api-client (>=11.1.0,<12.0.0)", "monday (>=2.0.1,<3.0.0)", "beautifulsoup4 (>=4.13.4,<5.0.0)", "tqdm (>=4.67.1,<5.0.0)", + "hubspot-api-client (>=12.0.0,<13.0.0)", ] [tool.poetry] From 95598f2d86ce109ec03d8624a5a065e9dbe68233 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Wed, 14 May 2025 14:59:41 +0000 Subject: [PATCH 30/33] added db migration --- .../348cc76ccdb4_add_address_in_company.py | 33 +++++++++++++++++ .../4439bf516ac8_mistake_on_foreign_key.py | 34 +++++++++++++++++ ...0_added_more_data_columns_for_documnets.py | 37 +++++++++++++++++++ .../versions/c6d2f6bf094a_make_it_nullable.py | 34 +++++++++++++++++ 4 files changed, 138 insertions(+) create mode 100644 alembic/versions/348cc76ccdb4_add_address_in_company.py create mode 100644 alembic/versions/4439bf516ac8_mistake_on_foreign_key.py create mode 100644 alembic/versions/6f76c19a8930_added_more_data_columns_for_documnets.py create mode 100644 alembic/versions/c6d2f6bf094a_make_it_nullable.py diff --git a/alembic/versions/348cc76ccdb4_add_address_in_company.py b/alembic/versions/348cc76ccdb4_add_address_in_company.py new file mode 100644 index 0000000..ce10a85 --- /dev/null +++ b/alembic/versions/348cc76ccdb4_add_address_in_company.py @@ -0,0 +1,33 @@ +"""Add address in company + +Revision ID: 348cc76ccdb4 +Revises: 6f76c19a8930 +Create Date: 2025-05-14 14:50:09.952275 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +import sqlmodel + +# revision identifiers, used by Alembic. +revision: str = '348cc76ccdb4' +down_revision: Union[str, None] = '6f76c19a8930' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('companyinfo', sa.Column('address', sqlmodel.sql.sqltypes.AutoString(), nullable=False)) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_column('companyinfo', 'address') + # ### end Alembic commands ### diff --git a/alembic/versions/4439bf516ac8_mistake_on_foreign_key.py b/alembic/versions/4439bf516ac8_mistake_on_foreign_key.py new file mode 100644 index 0000000..8ab4777 --- /dev/null +++ b/alembic/versions/4439bf516ac8_mistake_on_foreign_key.py @@ -0,0 +1,34 @@ +"""mistake on foreign key + +Revision ID: 4439bf516ac8 +Revises: c6d2f6bf094a +Create Date: 2025-05-12 15:03:09.037008 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = '4439bf516ac8' +down_revision: Union[str, None] = 'c6d2f6bf094a' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint('presitenote_pre_site_note_description_id_fkey', 'presitenote', type_='foreignkey') + op.create_foreign_key(None, 'presitenote', 'propertydescription', ['pre_site_note_description_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'presitenote', type_='foreignkey') + op.create_foreign_key('presitenote_pre_site_note_description_id_fkey', 'presitenote', 'assessorinfo', ['pre_site_note_description_id'], ['id']) + # ### end Alembic commands ### diff --git a/alembic/versions/6f76c19a8930_added_more_data_columns_for_documnets.py b/alembic/versions/6f76c19a8930_added_more_data_columns_for_documnets.py new file mode 100644 index 0000000..4b36f0e --- /dev/null +++ b/alembic/versions/6f76c19a8930_added_more_data_columns_for_documnets.py @@ -0,0 +1,37 @@ +"""Added more data columns for documnets + +Revision ID: 6f76c19a8930 +Revises: 4439bf516ac8 +Create Date: 2025-05-14 14:06:01.697106 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + +import sqlmodel + +# revision identifiers, used by Alembic. +revision: str = '6f76c19a8930' +down_revision: Union[str, None] = '4439bf516ac8' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('documents', sa.Column('assessor_id', sa.Uuid(), nullable=False)) + op.create_foreign_key(None, 'documents', 'assessorinfo', ['assessor_id'], ['id']) + op.drop_column('documents', 'author') + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('documents', sa.Column('author', sa.VARCHAR(), autoincrement=False, nullable=False)) + op.drop_constraint(None, 'documents', type_='foreignkey') + op.drop_column('documents', 'assessor_id') + # ### end Alembic commands ### diff --git a/alembic/versions/c6d2f6bf094a_make_it_nullable.py b/alembic/versions/c6d2f6bf094a_make_it_nullable.py new file mode 100644 index 0000000..997981e --- /dev/null +++ b/alembic/versions/c6d2f6bf094a_make_it_nullable.py @@ -0,0 +1,34 @@ +"""make it nullable + +Revision ID: c6d2f6bf094a +Revises: 9f45742b4b2f +Create Date: 2025-05-12 14:55:43.208954 + +""" +from typing import Sequence, Union + +from alembic import op +import sqlalchemy as sa + + +# revision identifiers, used by Alembic. +revision: str = 'c6d2f6bf094a' +down_revision: Union[str, None] = '9f45742b4b2f' +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + """Upgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.add_column('presitenote', sa.Column('pre_site_note_description_id', sa.Uuid(), nullable=True)) + op.create_foreign_key(None, 'presitenote', 'assessorinfo', ['pre_site_note_description_id'], ['id']) + # ### end Alembic commands ### + + +def downgrade() -> None: + """Downgrade schema.""" + # ### commands auto generated by Alembic - please adjust! ### + op.drop_constraint(None, 'presitenote', type_='foreignkey') + op.drop_column('presitenote', 'pre_site_note_description_id') + # ### end Alembic commands ### From 9d2dfec179b523c9929840cedc83bde894e34348 Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Wed, 14 May 2025 16:07:50 +0100 Subject: [PATCH 31/33] make scraping a button --- .github/workflows/hubspot_to_invoice.yml | 37 ++++++++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 .github/workflows/hubspot_to_invoice.yml diff --git a/.github/workflows/hubspot_to_invoice.yml b/.github/workflows/hubspot_to_invoice.yml new file mode 100644 index 0000000..7ddd54a --- /dev/null +++ b/.github/workflows/hubspot_to_invoice.yml @@ -0,0 +1,37 @@ +name: Deal Notes From HubSpot Scraper +on: + schedule: + - cron: '0 19 * * 0' + workflow_dispatch: + +jobs: + sharepoint-validator: + runs-on: ubuntu-22.04 + steps: + - uses: actions/checkout@v3 + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.12' + + - name: Install dependencies + run: | + pip install poetry + poetry install --no-root + + - name: run script + run: | + pwd + ls -la + poetry run python etl/hubspot_to_invoice.py + env: + PYTHONPATH: ${{ github.workspace }} + DATABASE_URL: postgresql://postgres:makingwarmhomes@terraform-20250331175522503500000002.cdgzupxvdyp0.eu-west-2.rds.amazonaws.com:5432/surveyDB + SOUTH_COAST_INSULATION_SERVICE_SHAREPOINT_ID: ${{ secrets.SOUTH_COAST_INSULATION_SERVICE_SHAREPOINT_ID }} + JJC_SERVICE_SHAREPOINT_ID: ${{ secrets.JJC_SERVICE_SHAREPOINT_ID }} + BAXTER_KELLY_SERVICE_SHAREPOINT_ID: ${{ secrets.BAXTER_KELLY_SERVICE_SHAREPOINT_ID }} + SGEC_SERVICE_SHAREPOINT_ID: ${{ secrets.SGEC_SERVICE_SHAREPOINT_ID }} + SHAREPOINT_CLIENT_ID: ${{ secrets.SHAREPOINT_CLIENT_ID }} + SHAREPOINT_CLIENT_SECRET: ${{ secrets.SHAREPOINT_CLIENT_SECRET }} + SHAREPOINT_TENANT_ID: ${{ secrets.SHAREPOINT_TENANT_ID }} \ No newline at end of file From ca2b0b4621ec23b93252fa7eadb4b1f16330fa7b Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Wed, 14 May 2025 16:08:20 +0100 Subject: [PATCH 32/33] db --- .github/workflows/hubspot_to_invoice.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/hubspot_to_invoice.yml b/.github/workflows/hubspot_to_invoice.yml index 7ddd54a..937c7fe 100644 --- a/.github/workflows/hubspot_to_invoice.yml +++ b/.github/workflows/hubspot_to_invoice.yml @@ -1,4 +1,4 @@ -name: Deal Notes From HubSpot Scraper +name: HubSpot Deals to DB loading and Invoice Calculator on: schedule: - cron: '0 19 * * 0' From 6657b353b27fef1c7e6857d0d3294537d68781bb Mon Sep 17 00:00:00 2001 From: Jun-te Kim Date: Wed, 14 May 2025 16:10:15 +0100 Subject: [PATCH 33/33] w.c. for any date --- etl/scraper/scraper.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/etl/scraper/scraper.py b/etl/scraper/scraper.py index ffe1075..de7704d 100644 --- a/etl/scraper/scraper.py +++ b/etl/scraper/scraper.py @@ -14,8 +14,8 @@ from datetime import datetime, timedelta def previous_monday(): today = datetime.today() last_monday = today - timedelta(days=today.weekday() + 7) # Go back to last week's Monday - return f"W.C. 31.09.2000" - # return f"W.C. {last_monday.strftime('%d.%m.%Y')}" + # return f"W.C. 31.09.2000" + return f"W.C. {last_monday.strftime('%d.%m.%Y')}" WEEK_COMMENCING = os.getenv("WEEK_COMMENCING", previous_monday())