from uploader import save_logs import os import pandas as pd from rapidfuzz import process, fuzz from random import randint from time import sleep #other imports (google API) def fuzzy_match(row, choices, scorer, cutoff): match = process.extractOne(row['Libellé d\'opération'], choices, scorer=scorer, score_cutoff=cutoff) if match: return match[0] return "missing receipt" def reconciliate_and_upload(data_path, name_of_csv, folder_to_save_processed_reciepts, folder_to_save_reconciled_data, name_of_raw_transcripts = "transcript_raw.txt", name_of_output = "[AI Generated] Output.xlsx" ): with open(f"{data_path}/{name_of_raw_transcripts}") as file: transcripts = eval(file.read()) imgs = [] path_to_pdfs =data_path for root, dirs, files in os.walk(path_to_pdfs): for file in files: if file.endswith('.png'): print(os.path.join(root, file)) imgs.append({"path": os.path.join(root, file)}) pass list_transcripts_evaled = [] objects = [] for i,t in enumerate(transcripts): content = eval(t["content"].replace('null', '-1')) try: obk = save_logs(imgs[i]["path"], f"P{i+1}.png", folder_to_save_processed_reciepts) except: print("sleeping a bit innit") breakpoint() sleep(randint(30,40)) obk = save_logs(imgs[i]["path"], f"P{i+1}.png", folder_to_save_processed_reciepts) objects.append(obk) print("uploaded image!") try: list_transcripts_evaled.append({ "path": imgs[i]["path"], "name_of_supplier" :content["name_of_supplier"], "amount":content["amount"], "currency":content["currency"], "date": content["date"]}) except: breakpoint() urls = [] for ob in objects: url = "https://drive.google.com/file/d/" + ob["id"] urls.append(url) df_app = pd.DataFrame(list_transcripts_evaled) float_regex = r'[-+]?\d*\.\d+|\d+' df_app['amount'] = df_app['amount'].astype(str).str.extract(f'({float_regex})', expand=False) # Replace spaces and commas, then handle N/A and convert to float df_app['amount'] = ( df_app['amount'] .str.replace(" ", "", regex=False) .str.replace(",", ".", regex=False) .str.replace("N/A", "-1", regex=False) .astype(float) ) df_app["date"] = pd.to_datetime(df_app['date'], format="%d/%m/%Y", errors='coerce') df_app["url"] = urls df_app = df_app.drop_duplicates(["name_of_supplier", "amount", "date"]).reset_index(drop=True) print(data_path) print("NAME OF CSV: ", name_of_csv) df_opp_app = pd.read_csv(f"{data_path}/{name_of_csv}",skiprows=3) # df_opp_app["Débit"] = df_opp_app["Débit"].str.replace(" ", "").str.replace(",", ".").astype("float") df_opp_app['Débit'] = df_opp_app['Débit'].astype(str).str.extract(f'({float_regex})', expand=False) # Replace spaces and commas, then handle N/A and convert to float df_opp_app['Débit'] = ( df_opp_app['Débit'] .str.replace(" ", "", regex=False) .str.replace(",", ".", regex=False) .str.replace("N/A", "-1", regex=False) .astype(float) ) # df_opp_app["Crédit"] = df_opp_app["Crédit"].str.replace(" ", "").str.replace(",", ".").astype("float") df_opp_app['Crédit'] = df_opp_app['Crédit'].astype(str).str.extract(f'({float_regex})', expand=False) # Replace spaces and commas, then handle N/A and convert to float df_opp_app['Crédit'] = ( df_opp_app['Crédit'] .str.replace(" ", "", regex=False) .str.replace(",", ".", regex=False) .str.replace("N/A", "-1", regex=False) .astype(float) ) df_opp_app["Date"] = pd.to_datetime(df_opp_app['Date'], format="%d/%m/%Y", errors='coerce') merged_df_app = pd.merge(df_opp_app, df_app, left_on=['Débit'], right_on=['amount'], how='left').drop(columns=["currency", "date","path"]).rename(columns={"name_of_supplier": "Nom fournisseur facture"}) merged_df_app["Nom fournisseur facture"] = merged_df_app["Nom fournisseur facture"].fillna("* Facture manquante *") # Merge on amount (Débit and amount) merged_df_app = pd.merge(df_opp_app, df_app, left_on='Débit', right_on='amount', how='left', suffixes=('_ops', '_df')) # Apply fuzzy matching raw_choices = df_app['name_of_supplier'].tolist() choices = [] for r in raw_choices: choices.append(r.upper()) merged_df_app['fuzzy_matched_supplier'] = merged_df_app.apply(lambda row: fuzzy_match(row, choices, fuzz.WRatio, 80), axis=1) merged_df_app = merged_df_app.drop_duplicates(subset=["Date", "Valeur", "Libellé d'opération", "Débit"]) # Identify residuals in df that were not matched df_residuals_app = df_app[~df_app['name_of_supplier'].isin(merged_df_app['name_of_supplier'])] # Replace original supplier column with fuzzy_matched_supplier and drop the name_of_supplier column from df merged_df_app['name_of_supplier'] = merged_df_app['fuzzy_matched_supplier'] # merged_df_app.drop(columns=['name_of_supplier', 'fuzzy_matched_supplier'], inplace=True) merged_df_app.drop(columns=["name_of_supplier", "currency", "date", "path", "fuzzy_matched_supplier"], inplace=True) df_residuals_app.drop(columns=["path"], inplace=True) merged_df_app['url'] = merged_df_app['url'].apply(lambda x: f'=HYPERLINK("{x}", "Voir Facture")' if pd.notna(x) else '') df_residuals_app['url'] = df_residuals_app['url'].apply(lambda x: f'=HYPERLINK("{x}", "Voir Facture")' if pd.notna(x) else '') with pd.ExcelWriter(name_of_output) as writer: merged_df_app.to_excel(writer, sheet_name='Données réconciliées', index=False) df_residuals_app.to_excel(writer, sheet_name='Résidus et transactions introuvables', index=False) id_output = save_logs(name_of_output, name_of_output , folder_to_save_reconciled_data) return id_output