palexis3 commited on
Commit
52b3c48
·
1 Parent(s): e506f4d

Fixed file processing and standardization logic

Browse files
app/api/routers/file_upload.py CHANGED
@@ -6,6 +6,8 @@ import asyncio
6
  import os
7
  import csv
8
  import codecs
 
 
9
 
10
  from app.engine.postgresdb import get_db_session
11
  from sqlalchemy.ext.asyncio import AsyncSession
@@ -31,31 +33,65 @@ async def create_file(input_file: UploadFile, db: AsyncSession = Depends(get_db_
31
  if not os.path.exists(file_upload_directory_path):
32
  os.makedirs(file_upload_directory_path)
33
 
34
- input_csv_file = open(output_csv_file_path, "a")
35
 
36
  try:
37
  if input_file.filename.endswith(".csv"):
38
- read_csv = await input_file.read()
39
- # result["output"] = read_csv
40
-
 
41
  # Write items of .csv filte to directory
42
- with open(output_csv_file_path, 'w', encoding="utf-8") as csv_file:
43
- result["processed_file"] = read_csv
44
- [csv_file.write(" ".join(row)+'\n') for row in read_csv.decode("utf-8").strip().splitlines()]
45
  csv_file.close()
 
 
46
 
47
- with open(output_csv_file_path, 'r', encoding="utf-8") as csv_file:
48
- result["output"] = csv_file.read()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
49
  csv_file.close()
 
 
 
 
 
 
50
 
51
- print(f"create_file result: {result}")
52
 
53
  # With the newly created file and it's path, process and save it for embedding
54
- processed_file = await process_file(output_csv_file_path)
 
55
  result["processed_file"] = processed_file
56
  result = await asyncio.gather(processed_file)
57
  result["result"] = result
58
  await save_results(db, result)
 
 
 
59
 
60
  except Exception as e:
61
  result["error"] = str(e)
 
6
  import os
7
  import csv
8
  import codecs
9
+ from io import StringIO
10
+ import pandas as pd
11
 
12
  from app.engine.postgresdb import get_db_session
13
  from sqlalchemy.ext.asyncio import AsyncSession
 
33
  if not os.path.exists(file_upload_directory_path):
34
  os.makedirs(file_upload_directory_path)
35
 
36
+ # input_csv_file = open(output_csv_file_path, "a")
37
 
38
  try:
39
  if input_file.filename.endswith(".csv"):
40
+ input_contents = await input_file.read()
41
+ buffer = StringIO(input_contents.decode('utf-8'))
42
+ read_csv = csv.reader(buffer)
43
+
44
  # Write items of .csv filte to directory
45
+ with codecs.open(output_csv_file_path, 'w+', encoding='utf-8') as csv_file:
46
+ [csv_file.write(" ".join(row)+'\n') for row in read_csv]
 
47
  csv_file.close()
48
+
49
+ new_output_csv_file_path = os.path.join(file_upload_directory_path, f"new_{input_file.filename}")
50
 
51
+ with codecs.open(output_csv_file_path, 'r', encoding='utf-8') as csv_file:
52
+ with codecs.open(new_output_csv_file_path, 'w+', encoding='utf-8') as new_csv_file:
53
+ csvreader = csv.reader(csv_file)
54
+ header = next(csvreader)[0]
55
+ new_header = header.replace(" ", ",")
56
+ # print(f"header: \n{header} headerItem: {header[0]} new_header: {new_header}\n")
57
+ new_csv_file.write(" ".join(new_header)+'\n')
58
+ for row in csvreader:
59
+ new_row_items = row[0].split(' ')
60
+ new_row = ""
61
+ description = ""
62
+ for idx, element in enumerate(new_row_items):
63
+ # Since our string has been delimited by spaces, we want to get all the words of the description which comes after the 'date' index (0) and
64
+ # before the second to last 'type' index
65
+ if idx > 0 and idx < len(new_row_items) - 2:
66
+ description = description + f" {element}"
67
+ else:
68
+ delim = ", " if idx > 0 else ""
69
+ item = element if len(description) == 0 else f"{description}, {element}"
70
+ new_row = new_row + delim + item
71
+ description = ""
72
+
73
+ # print(f"row: {row} rowType: \n{type(row)} new_row: {new_row}\n")
74
+ new_csv_file.write(" ".join(new_row)+'\n')
75
+ new_csv_file.close()
76
  csv_file.close()
77
+
78
+ df = pd.read_csv(new_output_csv_file_path, names=['transaction_date', 'name_description', 'type', 'amount'], header=0, dtype=str)
79
+ # df = df.reindex(columns=['transaction_date', 'name_description', 'type', 'amount'])
80
+ # transaction_date_index = df.columns.get_loc('transaction_date')
81
+ result["output"] = df
82
+ # print(f"column: \n{df['transaction_date']} \n")
83
 
 
84
 
85
  # With the newly created file and it's path, process and save it for embedding
86
+ # processed_file = await process_file(new_output_csv_file_path)
87
+ processed_file = await process_file(new_output_csv_file_path, df)
88
  result["processed_file"] = processed_file
89
  result = await asyncio.gather(processed_file)
90
  result["result"] = result
91
  await save_results(db, result)
92
+
93
+ buffer.close()
94
+ input_file.close()
95
 
96
  except Exception as e:
97
  result["error"] = str(e)
app/categorization/categorizer.py CHANGED
@@ -1,4 +1,3 @@
1
- # Standard library imports
2
  import re
3
  import ast
4
  import json
@@ -6,7 +5,6 @@ import logging
6
  import os
7
  from typing import Any, List, Tuple, Optional, Dict, Union
8
 
9
- # Third-party library imports
10
  import numpy as np
11
  import pandas as pd
12
  import asyncio
@@ -14,7 +12,8 @@ from rapidfuzz import process
14
  from tenacity import retry, wait_random_exponential, stop_after_attempt
15
  from pydantic import ValidationError
16
 
17
- # Local application/library specific imports
 
18
  from langchain_openai import ChatOpenAI
19
  from langchain.chains import LLMChain
20
  from langchain.output_parsers import PydanticOutputParser, OutputFixingParser
@@ -44,16 +43,23 @@ def fuzzy_match_list_categorizer(
44
  Returns:
45
  str or None: Category of the matched description, or None if no match found.
46
  """
47
-
48
- # Fuzzy-match this description against the reference descriptions
49
- match_results = process.extractOne(
50
- description, descriptions, score_cutoff=threshold)
51
-
52
- # If a match is found, return the category of the matched description
53
- if match_results:
54
- return description_category_pairs.at[match_results[2], 'category']
55
-
56
- return None
 
 
 
 
 
 
 
57
 
58
 
59
  async def llm_list_categorizer(tx_list: pd.DataFrame) -> pd.DataFrame:
@@ -68,31 +74,42 @@ async def llm_list_categorizer(tx_list: pd.DataFrame) -> pd.DataFrame:
68
  Returns:
69
  pd.DataFrame: DataFrame mapping transaction descriptions to their inferred categories.
70
  """
 
 
71
 
72
- # Initialize language model and prompt
73
- openai_api_key = os.environ['OPENAI_API_KEY']
74
- llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0125",
75
- api_key=openai_api_key)
76
- prompt = PromptTemplate.from_template(template=CATEGORY_TEMPLATE)
77
- chain = LLMChain(llm=llm, prompt=prompt)
78
-
79
- # Iterate over the DataFrame in batches of TX_PER_LLM_RUN transactions
80
- tasks = [llm_sublist_categorizer(tx_list.attrs['file_name'], chain=chain, tx_descriptions="\n".join(chunk['name_description']).strip())
81
- for chunk in np.array_split(tx_list, tx_list.shape[0] // TX_PER_LLM_RUN + 1)]
82
-
83
- # Gather results and extract (valid) outputs
84
- # The results variable is a list of 'results', each 'result' being the output of a single LLM run
85
- results = await asyncio.gather(*tasks)
86
-
87
- # Extract valid results (each valid result is a list of description-category pairs)
88
- valid_results = [result['output'] for result in results if result['valid']]
89
-
90
- # Flatten the list of valid results to obtain a single list of description-category pairs
91
- valid_outputs = [
92
- output for valid_result in valid_results for output in valid_result]
 
 
 
 
 
 
 
 
 
 
 
93
 
94
- # Return a DataFrame with the valid outputs
95
- return pd.DataFrame(valid_outputs, columns=['name_description', 'category'])
96
 
97
 
98
  @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
@@ -143,5 +160,6 @@ async def llm_sublist_categorizer(
143
  logging.log(
144
  logging.ERROR, f"| File: {file_name} | Unexpected Error: {e}\nRaw Result: {raw_result}")
145
  result['valid'] = False
 
146
 
147
  return result
 
 
1
  import re
2
  import ast
3
  import json
 
5
  import os
6
  from typing import Any, List, Tuple, Optional, Dict, Union
7
 
 
8
  import numpy as np
9
  import pandas as pd
10
  import asyncio
 
12
  from tenacity import retry, wait_random_exponential, stop_after_attempt
13
  from pydantic import ValidationError
14
 
15
+ from fastapi import HTTPException
16
+
17
  from langchain_openai import ChatOpenAI
18
  from langchain.chains import LLMChain
19
  from langchain.output_parsers import PydanticOutputParser, OutputFixingParser
 
43
  Returns:
44
  str or None: Category of the matched description, or None if no match found.
45
  """
46
+ result = {"description": description, "descriptions": descriptions, "description_category_pairs": description_category_pairs, "match_results": "", "error": ""}
47
+ try:
48
+ # Fuzzy-match this description against the reference descriptions
49
+ match_results = process.extractOne(
50
+ description, descriptions, score_cutoff=threshold)
51
+
52
+ result["match_results"] = match_results
53
+
54
+ # If a match is found, return the category of the matched description
55
+ if match_results:
56
+ return description_category_pairs.at[match_results[2], 'category']
57
+
58
+ return None
59
+
60
+ except Exception as e:
61
+ result["error"] = str(e)
62
+ raise HTTPException(status_code = 500, detail=f"fuzzy_match_list_categorizer result: {result}")
63
 
64
 
65
  async def llm_list_categorizer(tx_list: pd.DataFrame) -> pd.DataFrame:
 
74
  Returns:
75
  pd.DataFrame: DataFrame mapping transaction descriptions to their inferred categories.
76
  """
77
+
78
+ result = {"tx_list": tx_list, "results": "", "valid results": "", "valid outputs": "", "error": ""}
79
 
80
+ try:
81
+ # Initialize language model and prompt
82
+ openai_api_key = os.environ['OPENAI_API_KEY']
83
+ llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo-0125",
84
+ api_key=openai_api_key)
85
+ prompt = PromptTemplate.from_template(template=CATEGORY_TEMPLATE)
86
+ chain = LLMChain(llm=llm, prompt=prompt)
87
+
88
+ # Iterate over the DataFrame in batches of TX_PER_LLM_RUN transactions
89
+ tasks = [llm_sublist_categorizer(tx_list.attrs['file_name'], chain=chain, tx_descriptions="\n".join(chunk['name_description']).strip())
90
+ for chunk in np.array_split(tx_list, tx_list.shape[0] // TX_PER_LLM_RUN + 1)]
91
+
92
+ # Gather results and extract (valid) outputs
93
+ # The results variable is a list of 'results', each 'result' being the output of a single LLM run
94
+ results = await asyncio.gather(*tasks)
95
+ result["results"] = results
96
+
97
+ # Extract valid results (each valid result is a list of description-category pairs)
98
+ valid_results = [result['output'] for result in results if result['valid']]
99
+ result["valid results"] = valid_results
100
+
101
+ # Flatten the list of valid results to obtain a single list of description-category pairs
102
+ valid_outputs = [
103
+ output for valid_result in valid_results for output in valid_result]
104
+ result["valid outputs"] = valid_outputs
105
+
106
+ # Return a DataFrame with the valid outputs
107
+ return pd.DataFrame(valid_outputs, columns=['name_description', 'category'])
108
+
109
+ except Exception as e:
110
+ result["error"] = str(e)
111
+ raise HTTPException(status_code = 500, detail=f"llm_list_categorizer result: {result}")
112
 
 
 
113
 
114
 
115
  @retry(wait=wait_random_exponential(min=1, max=20), stop=stop_after_attempt(6))
 
160
  logging.log(
161
  logging.ERROR, f"| File: {file_name} | Unexpected Error: {e}\nRaw Result: {raw_result}")
162
  result['valid'] = False
163
+ raise HTTPException(status_code = 500, detail=f"llm_sublist_categorizer result: {result}")
164
 
165
  return result
app/categorization/categorizer_list.py CHANGED
@@ -24,7 +24,7 @@ async def categorize_list(tx_list: pd.DataFrame) -> pd.DataFrame:
24
  Returns:
25
  pd.DataFrame: The original DataFrame with an additional column for the category.
26
  """
27
- result = {"tx_list": tx_list, "description_category_pairs": "", "uncategorized_descriptions": "", "categorized_descriptions": "", "error": ""}
28
 
29
  try:
30
  if not os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
@@ -33,17 +33,22 @@ async def categorize_list(tx_list: pd.DataFrame) -> pd.DataFrame:
33
  if os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
34
  # Read description-category pairs from the reference file
35
  description_category_pairs = pd.read_csv(
36
- CATEGORY_REFERENCE_OUTPUT_FILE, header=None, names=['name_description', 'category']
 
37
  )
38
- result['description_category_pairs'] = description_category_pairs
39
-
40
  # Extract only descriptions for faster matching
41
- descriptions = description_category_pairs['name_description'].values
 
 
 
 
 
42
 
43
  # Use fuzzy matching to find similar descriptions and assign the category
44
  tx_list['category'] = tx_list['name_description'].apply(
45
  fuzzy_match_list_categorizer,
46
- args=(descriptions, description_category_pairs),
47
  )
48
 
49
  # Filter out uncategorized transactions, deduplicate, and sort by description
 
24
  Returns:
25
  pd.DataFrame: The original DataFrame with an additional column for the category.
26
  """
27
+ result = {"tx_list": pd.DataFrame(), "description_category": "", "description_category_pairs": "", "uncategorized_descriptions": "", "categorized_descriptions": "", "error": ""}
28
 
29
  try:
30
  if not os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
 
33
  if os.path.exists(CATEGORY_REFERENCE_OUTPUT_FILE):
34
  # Read description-category pairs from the reference file
35
  description_category_pairs = pd.read_csv(
36
+ CATEGORY_REFERENCE_OUTPUT_FILE, sep=r'\s*,\s*', header=None, names=['name_description', 'category'], encoding='utf-8',
37
+ engine='python'
38
  )
39
+
 
40
  # Extract only descriptions for faster matching
41
+ description_category_pairs.columns = description_category_pairs.columns.str.strip()
42
+ # descriptions = description_category_pairs['name_description'].values
43
+ descriptions = tx_list['name_description'].values
44
+
45
+ result['description_category'] = descriptions
46
+ result['tx_list'] = tx_list['name_description']
47
 
48
  # Use fuzzy matching to find similar descriptions and assign the category
49
  tx_list['category'] = tx_list['name_description'].apply(
50
  fuzzy_match_list_categorizer,
51
+ args=(descriptions, description_category_pairs)
52
  )
53
 
54
  # Filter out uncategorized transactions, deduplicate, and sort by description
app/categorization/file_processing.py CHANGED
@@ -20,7 +20,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
20
 
21
 
22
  # Read file and process it (e.g. categorize transactions)
23
- async def process_file(file_path: str) -> Dict[str, Union[str, pd.DataFrame]]:
24
  """
25
  Process the input file by reading, cleaning, standardizing, and categorizing the transactions.
26
 
@@ -32,11 +32,11 @@ async def process_file(file_path: str) -> Dict[str, Union[str, pd.DataFrame]]:
32
  """
33
 
34
  file_name = os.path.basename(file_path)
35
- result = {"file_name": file_name, "output": pd.DataFrame(), "error": ""}
36
  try:
37
  # Read file into standardized tx format: transaction_date, name_description, type, amount, category, source
38
- tx_list = standardize_csv_file(file_path)
39
- print(f"process_file tx_list columns: \n {tx_list.columns.tolist()}")
40
 
41
  # Categorize transactions
42
  result["output"] = await categorize_list(tx_list)
@@ -45,14 +45,13 @@ async def process_file(file_path: str) -> Dict[str, Union[str, pd.DataFrame]]:
45
  except Exception as e:
46
  # Return an error indicator and exception info
47
  logging.debug(logging.ERROR, f"| File: {file_name} | Unexpected Error: {e}")
48
- print(f"ERROR processing file {file_name}: {e} errorType: {type(e)}")
49
  result["error"] = str(e)
50
- raise HTTPException(status_code = 500, detail=f"process_file result: {result}")
51
 
52
  return result
53
 
54
 
55
- def standardize_csv_file(file_path: str) -> pd.DataFrame:
56
  """
57
  Read and prepare the data from the input file.
58
 
@@ -62,46 +61,60 @@ def standardize_csv_file(file_path: str) -> pd.DataFrame:
62
  Returns:
63
  pd.DataFrame: Prepared transaction data.
64
  """
65
- result = {"csv_file": "", "file_path": file_path, "tx_list_columns": {}, "transaction_date": "", "tx_list_new_columns": "", "error": ""}
 
66
  try:
 
67
  # result["csv_file"] = csv_file.read()
68
  # reader = csv.reader(csv_file)
69
  # data = list(reader)
70
- # tx_list = pd.DataFrame(data, columns=data[0])
71
-
72
- tx_list = pd.read_csv(file_path, encoding="utf-8")
73
- print(f"standardize_csv_file tx_list: {tx_list} \n")
74
- tx_list.loc[:, "category"] = ""
75
- result["csv_file"] = tx_list
76
-
77
- # result["tx_list_columns"] = tx_list
78
- tx_list.attrs["file_name"] = file_path
79
- tx_list.columns = tx_list.columns.str.lower().str.strip()
80
 
81
- # # Standardize dates to YYYY/MM/DD format
82
- # result["transaction_date"] = pd.to_datetime(tx_list["transaction_date"])
83
- # print(f"transaction_date: {tx_list['transaction_date']}")
84
- # tx_list["transaction_date"] = pd.to_datetime(tx_list["transaction_date"]).dt.strftime("%Y/%m/%d")
 
 
 
 
 
85
 
 
 
 
 
86
  # Add source and reindex to desired tx format; category column is new and therefore empty
87
- tx_list.loc[:, "source"] = os.path.basename(file_path)
88
- # tx_list = tx_list.reindex(columns=["transaction_date", "name_description", "type", "amount", "category"])
89
- # result["tx_list_new_columns"] = tx_list.columns
90
-
91
- # print(f"standardize_csv_file result: {result}")
92
-
93
- # with open(file_path, 'r') as csv_file:
94
- # return tx_list
 
 
 
 
95
 
96
  except Exception as e:
97
  # Return an error indicator and exception info
98
- # print(f"standardize_csv_file exception: {e}")
99
  logging.debug("standardize_csv_file Error: {e}")
100
- result["error"] = str(e)
 
 
 
101
  raise HTTPException(status_code = 401, detail=f"standardize_csv_file exception: {result}")
102
 
103
- return tx_list
104
-
105
 
106
  async def save_results(db: AsyncSession, results: List) -> None:
107
  """
@@ -168,4 +181,4 @@ async def save_results(db: AsyncSession, results: List) -> None:
168
  # print(f"standardize_csv_file exception: {e}")
169
  logging.debug("save_results Error: {e}")
170
  result["error"] = str(e)
171
- raise HTTPException(status_code = 401, detail=f"save_results exception: {result}")
 
20
 
21
 
22
  # Read file and process it (e.g. categorize transactions)
23
+ async def process_file(file_path: str, df: pd.DataFrame) -> Dict[str, Union[str, pd.DataFrame]]:
24
  """
25
  Process the input file by reading, cleaning, standardizing, and categorizing the transactions.
26
 
 
32
  """
33
 
34
  file_name = os.path.basename(file_path)
35
+ result = {"file_path": file_path, "output": pd.DataFrame(), "columns": [], "error": ""}
36
  try:
37
  # Read file into standardized tx format: transaction_date, name_description, type, amount, category, source
38
+ tx_list = standardize_csv_file(file_path, df)
39
+ result["columns"] = tx_list.columns.tolist()
40
 
41
  # Categorize transactions
42
  result["output"] = await categorize_list(tx_list)
 
45
  except Exception as e:
46
  # Return an error indicator and exception info
47
  logging.debug(logging.ERROR, f"| File: {file_name} | Unexpected Error: {e}")
 
48
  result["error"] = str(e)
49
+ raise HTTPException(status_code = 500, detail=f"\n\n process_file result: \n{result}")
50
 
51
  return result
52
 
53
 
54
+ def standardize_csv_file(file_path: str, d: pd.DataFrame) -> pd.DataFrame:
55
  """
56
  Read and prepare the data from the input file.
57
 
 
61
  Returns:
62
  pd.DataFrame: Prepared transaction data.
63
  """
64
+ result = {"csv_file": "", "file_path": file_path, "new_csv_file": "", "transaction_date": "", "tx_list_new_columns": "", "error": ""}
65
+
66
  try:
67
+ #1
68
  # result["csv_file"] = csv_file.read()
69
  # reader = csv.reader(csv_file)
70
  # data = list(reader)
71
+ # df = pd.DataFrame(data, columns=data[0])
72
+
73
+ #2
74
+ # df = pd.read_csv(file_path)
75
+
76
+ #3
77
+ # with open(file_path, 'r', encoding='utf-8') as file:
78
+ # csv_reader = csv.reader(file)
79
+ # data = list(csv_reader)
 
80
 
81
+ # df = pd.DataFrame(data)
82
+
83
+ headers = d.columns
84
+ df = pd.DataFrame(d.values[1:], columns=headers)
85
+
86
+ result["csv_file"] = df
87
+ df.attrs['file_name'] = file_path
88
+ # print(f"\n\n standardize_csv_file columns: \n{df} \n date:\n{df['transaction_date']} \n")
89
+ # tx_list.columns = ['transaction_date', 'name_description', 'type', 'amount', 'category']
90
 
91
+ # # Standardize dates to YYYY/MM/DD format
92
+ warnings.filterwarnings('ignore', 'Parsing dates', category=UserWarning)
93
+ # df['transaction_date'] = pd.to_datetime(df['transaction_date']).dt.strftime('%d/%m/%Y')
94
+
95
  # Add source and reindex to desired tx format; category column is new and therefore empty
96
+ # tx_list.insert(0, "source", [os.path.basename(file_path) for i in range(len(tx_list.columns))])
97
+ # tx_list.insert(0, "category", [])
98
+
99
+ # df = df.reindex(columns=['transaction_date', 'name_description', 'type', 'amount', 'category'])
100
+ # tx_list['transaction_date'] = pd.to_datetime(tx_list['transaction_date']).dt.strftime('%Y/%m/%d')
101
+ # tx_list.set_index(['transaction_date', 'name_description', 'type', 'amount', 'category'])
102
+ df['source'] = pd.Series([os.path.basename(file_path)] * len(df.index))
103
+ df['category'] = pd.Series([])
104
+ result["new_csv_file"] = df
105
+ print(f"\n\n standardize_csv_file result: \n{df} \n")
106
+
107
+ return df
108
 
109
  except Exception as e:
110
  # Return an error indicator and exception info
 
111
  logging.debug("standardize_csv_file Error: {e}")
112
+ if hasattr(e, 'message'):
113
+ result["error"] = str(e.message)
114
+ else:
115
+ result["error"] = str(e)
116
  raise HTTPException(status_code = 401, detail=f"standardize_csv_file exception: {result}")
117
 
 
 
118
 
119
  async def save_results(db: AsyncSession, results: List) -> None:
120
  """
 
181
  # print(f"standardize_csv_file exception: {e}")
182
  logging.debug("save_results Error: {e}")
183
  result["error"] = str(e)
184
+ raise HTTPException(status_code = 401, detail=f"save_results exception: {error_messages}")
migration/versions/e8e988ebb7c7_.py ADDED
@@ -0,0 +1,30 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+
3
+ Revision ID: e8e988ebb7c7
4
+ Revises: 4e76691ab103
5
+ Create Date: 2024-06-08 18:13:46.160432
6
+
7
+ """
8
+ from typing import Sequence, Union
9
+
10
+ from alembic import op
11
+ import sqlalchemy as sa
12
+
13
+
14
+ # revision identifiers, used by Alembic.
15
+ revision: str = 'e8e988ebb7c7'
16
+ down_revision: Union[str, None] = '4e76691ab103'
17
+ branch_labels: Union[str, Sequence[str], None] = None
18
+ depends_on: Union[str, Sequence[str], None] = None
19
+
20
+
21
+ def upgrade() -> None:
22
+ # ### commands auto generated by Alembic - please adjust! ###
23
+ pass
24
+ # ### end Alembic commands ###
25
+
26
+
27
+ def downgrade() -> None:
28
+ # ### commands auto generated by Alembic - please adjust! ###
29
+ pass
30
+ # ### end Alembic commands ###
transactions_2024.csv CHANGED
@@ -1,29 +1,29 @@
1
  transaction_date,name_description,type,amount
2
- 2023-12-30,Comcast Internet,Expense,9.96
3
- 2023-12-30,Lemonade Home Insurance,Expense,17.53
4
- 2023-12-30,Monthly Appartment Rent,Expense,2000.0
5
- 2023-12-30,Staples Office Supplies,Expense,12.46
6
- 2023-12-29,Selling Paintings,Income,13.63
7
- 2023-12-29,Spotify,Expense,12.19
8
- 2023-12-23,Target,Expense,27.08
9
- 2023-12-22,IT Consulting,Income,541.57
10
- 2023-12-22,Phone,Expense,10.7
11
- 2023-12-20,ML Consulting,Income,2641.93
12
- 2023-12-19,Chipotle,Expense,18.9
13
- 2023-12-18,Cold Brew Coffee,Expense,17.67
14
- 2023-12-18,Gas,Expense,8.80
15
- 2023-12-18,CA Property Tax,Expense,1670.34
16
- 2022-11-26,Salary,Income,4000.36
17
- 2022-11-26,Cellphone,Expense,19.27
18
- 2022-11-26,Gym Membership,Expense,24.71
19
- 2022-11-25,Wholefoods,Expense,17.35
20
- 2022-11-24,Freelancing,Income,2409.55
21
- 2022-11-19,Spotify,Expense,20.76
22
- 2022-10-25,Blogging,Income,4044.27
23
- 2022-10-24,Uber Taxi,Expense,18.9
24
- 2022-10-23,Uber Taxi,Expense,27.54
25
- 2022-10-22,Apple Services,Expense,41.25
26
- 2022-10-21,Netflix,Expense,22.8
27
- 2022-01-16,Amazon Lux,Expense,24.11
28
- 2022-01-15,Burger King,Expense,30.08
29
- 2022-01-14,Amazon,Expense,11.0
 
1
  transaction_date,name_description,type,amount
2
+ 20/11/2023,Comcast Internet,Expense,9.96
3
+ 21/04/2023,Lemonade Home Insurance,Expense,17.53
4
+ 22/05/2023,Monthly Appartment Rent,Expense,2000.0
5
+ 14/06/2023,Staples Office Supplies,Expense,12.46
6
+ 17/07/2023,Selling Paintings,Income,13.63
7
+ 19/12/2023,Spotify,Expense,12.19
8
+ 05/10/2023,Target,Expense,27.08
9
+ 03/08/2023,IT Consulting,Income,541.57
10
+ 06/04/2023,Phone,Expense,10.7
11
+ 02/01/2023,ML Consulting,Income,2641.93
12
+ 05/04/2023,Chipotle,Expense,18.9
13
+ 18/05/2023,Cold Brew Coffee,Expense,17.67
14
+ 13/03/2023,Gas,Expense,8.80
15
+ 19/07/2023,CA Property Tax,Expense,1670.34
16
+ 20/08/2023,Salary,Income,4000.36
17
+ 20/09/2023,Cellphone,Expense,19.27
18
+ 06/10/2023,Gym Membership,Expense,24.71
19
+ 02/01/2023,Wholefoods,Expense,17.35
20
+ 11/06/2023,Freelancing,Income,2409.55
21
+ 12/11/2023,Spotify,Expense,20.76
22
+ 20/03/2023,Blogging,Income,4044.27
23
+ 12/02/2023,Uber Taxi,Expense,18.9
24
+ 23/07/2023,Uber Taxi,Expense,27.54
25
+ 01/09/2023,Apple Services,Expense,41.25
26
+ 07/05/2023,Netflix,Expense,22.8
27
+ 09/08/2023,Amazon Lux,Expense,24.11
28
+ 17/01/2023,Burger King,Expense,30.08
29
+ 14/01/2023,Amazon,Expense,11.0