Spaces:
Sleeping
Sleeping
File size: 8,674 Bytes
e612627 cd5e987 e612627 0dbb03c b905bd6 e612627 52b3c48 e612627 c37c62e e612627 0dbb03c e612627 c37c62e e612627 e506f4d 52b3c48 e612627 0dbb03c c37c62e e612627 bd3733d 0dbb03c 52b3c48 e612627 0dbb03c e612627 52b3c48 e612627 52b3c48 bd3733d 52b3c48 e506f4d 52b3c48 cd5e987 52b3c48 e506f4d 52b3c48 e506f4d 52b3c48 c37c62e 52b3c48 bd3733d 52b3c48 e506f4d e612627 c37c62e e612627 c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d c37c62e e506f4d e612627 e506f4d c37c62e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 | import os
import glob
import shutil
import logging
import warnings
from typing import Optional, Union, Dict, List
from datetime import datetime
import pandas as pd
from dateparser import parse
from fastapi import HTTPException
import csv
from app.categorization.categorizer_list import categorize_list
from app.categorization.config import RESULT_OUTPUT_FILE, CATEGORY_REFERENCE_OUTPUT_FILE
from app.model.transaction import Transaction
from app.schema.index import TransactionCreate
from sqlalchemy.ext.asyncio import AsyncSession
# Read file and process it (e.g. categorize transactions)
async def process_file(file_path: str, df: pd.DataFrame) -> Dict[str, Union[str, pd.DataFrame]]:
"""
Process the input file by reading, cleaning, standardizing, and categorizing the transactions.
Args:
file_path (str): Path to the input file.
df (dataframe): Dataframe representing input file
Returns:
Dict[str, Union[str, pd.DataFrame]]: Dictionary containing the file name, processed output, and error information if any
"""
file_name = os.path.basename(file_path)
result = {'file_path': file_name, 'output': pd.DataFrame(), 'error': ''}
try:
# Read file into standardized tx format: transaction_date, name_description, type, amount, category, source
tx_list = standardize_csv_file(file_path, df)
result["columns"] = tx_list.columns.tolist()
# Categorize transactions
result["output"] = await categorize_list(tx_list)
# print(f"File processed sucessfully: {file_name}")
except Exception as e:
# Return an error indicator and exception info
logging.debug(logging.ERROR, f"| File: {file_name} | Unexpected Error: {e}")
result["error"] = str(e)
raise HTTPException(status_code = 500, detail=f"\n\n process_file result: \n{result}")
return result
def standardize_csv_file(file_path: str, d: pd.DataFrame) -> pd.DataFrame:
"""
Read and prepare the data from the input file.
Args:
file_path (str): Path to the input file.
Returns:
pd.DataFrame: Prepared transaction data.
"""
result = {"csv_file": "", "file_path": file_path, "new_csv_file": "", "transaction_date": "", "tx_list_new_columns": "", "error": ""}
try:
#1
# result["csv_file"] = csv_file.read()
# reader = csv.reader(csv_file)
# data = list(reader)
# df = pd.DataFrame(data, columns=data[0])
#2
# df = pd.read_csv(file_path)
#3
# with open(file_path, 'r', encoding='utf-8') as file:
# csv_reader = csv.reader(file)
# data = list(csv_reader)
# df = pd.DataFrame(data)
headers = d.columns
df = pd.DataFrame(d.values[1:], columns=headers)
result["csv_file"] = df
df.attrs['file_name'] = file_path
# print(f"\n\n standardize_csv_file columns: \n{df} \n date:\n{df['transaction_date']} \n")
# tx_list.columns = ['transaction_date', 'name_description', 'type', 'amount', 'category']
# # Standardize dates to YYYY/MM/DD format
warnings.filterwarnings('ignore', 'Parsing dates', category=UserWarning)
# df['transaction_date'] = pd.to_datetime(df['transaction_date']).dt.strftime('%d/%m/%Y')
# Add source and reindex to desired tx format; category column is new and therefore empty
# tx_list.insert(0, "source", [os.path.basename(file_path) for i in range(len(tx_list.columns))])
# tx_list.insert(0, "category", [])
# df = df.reindex(columns=['transaction_date', 'name_description', 'type', 'amount', 'category'])
# tx_list['transaction_date'] = pd.to_datetime(tx_list['transaction_date']).dt.strftime('%Y/%m/%d')
# tx_list.set_index(['transaction_date', 'name_description', 'type', 'amount', 'category'])
df['source'] = pd.Series([os.path.basename(file_path)] * len(df.index))
df['category'] = pd.Series([])
result["new_csv_file"] = df
# print(f"\n\n standardize_csv_file result: \n{df} \n")
return df
except Exception as e:
# Return an error indicator and exception info
logging.debug("standardize_csv_file Error: {e}")
if hasattr(e, 'message'):
result["error"] = str(e.message)
else:
result["error"] = str(e)
raise HTTPException(status_code = 401, detail=f"standardize_csv_file exception: {result}")
async def save_results(db: AsyncSession, results: List) -> Dict[str, Union[List[str], List[TransactionCreate], pd.DataFrame]]:
"""
Merge all interim results in the input folder and write the merged results to the output file.
Args:
in_folder (str): Path to the input folder containing interim results.
out_file (str): Path to the output file.
Returns:
None
"""
result = {'ok_files': '', 'ko_files': '', 'error_messages': '', 'txn_list_to_save': '', 'old_ref_data': '', 'new_ref_data': ''}
try:
# Concatenate all (valid) results into a single DataFrame
# Print errors to console
ok_files = []
ko_files = []
error_messages = []
# print(f"save_results results: {results}")
col_list = ["transaction_date", "name_description", "type", "amount", "category"]
tx_list = pd.DataFrame(columns=col_list)
# print(f"save_results tx_list: {tx_list}")
for result in results:
if not result["error"]:
ok_files.append(result["file_name"])
print(f"save_results ok_files: {ok_files}\n")
result_df = result["output"]
print(f"save_results result_df: {result_df}\n")
result_df.columns = col_list
print(f"save_results result_df.columns: {result_df.columns}\n")
tx_list = pd.concat([tx_list, result_df], ignore_index=True)
print(f"save_results tx_list: {tx_list}\n")
else:
ko_files.append(result["file_name"])
print(f"save_results ko_files: {ko_files}\n")
error_messages.append(f"{result['file_name']}: {result['error']}")
print(f"save_results error_messages: {error_messages}\n")
result['ok_files'] = ok_files
result['ko_files'] = ko_files
result['error_messages'] = error_messages
# Save to database
# FIXME: get user_id from session
txn_list_to_save = [TransactionCreate(**row.to_dict(), user_id=1) for _, row in tx_list.iterrows()]
result['txn_list_to_save'] = txn_list_to_save
print(f"save_results txn_list_to_save: {txn_list_to_save}\n")
await Transaction.bulk_create(db, txn_list_to_save)
new_ref_data = tx_list[["name_description", "category"]]
if not os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
os.makedirs(CATEGORY_REFERENCE_OUTPUT_FILE)
if os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
# If it exists, add master file to interim results
old_ref_data = pd.read_csv(CATEGORY_REFERENCE_OUTPUT_FILE, names=["name_description", "category"], header=0)
new_ref_data = pd.concat([old_ref_data, new_ref_data], ignore_index=True)
print(f"save_results new_ref_data: {new_ref_data}\n")
print(f"save_results old_ref_data: {old_ref_data}\n")
# Drop duplicates, sort, and write to create new Master File
new_ref_data.drop_duplicates(subset=["name_description"]).sort_values(by=["name_description"]).to_csv(
CATEGORY_REFERENCE_OUTPUT_FILE, mode="w", index=False, header=True
)
result['old_ref_data'] = old_ref_data
result['new_ref_data'] = new_ref_data
print(f"\nsave_results result:\n {result}\n")
# Summarize results
# print(f"\nProcessed {len(results)} files: {len(ok_files)} successful, {len(ko_files)} with errors\n")
logging.debug(f"\nProcessed {len(results)} files: {len(ok_files)} successful, {len(ko_files)} with errors\n")
if len(ko_files):
print(f"Errors in the following files:")
for message in error_messages:
print(f" {message}")
print("\n")
except Exception as e:
# Return an error indicator and exception info
# print(f"standardize_csv_file exception: {e}")
logging.debug("save_results Error: {e}")
result["error"] = str(e)
raise HTTPException(status_code = 401, detail=f"save_results exception: {error_messages}")
return result |