-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
230 lines (193 loc) · 7.22 KB
/
main.py
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
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
"""
Module to query bank API for transactions, calculate daily money in and out
in account currency and UAH currency, with added logging.
"""
import sys
import requests
import json
import logging
import csv
import argparse
from collections import defaultdict
from datetime import datetime, timedelta
PRIVATBANK_TX_STATEMENTS_URL = "https://acp.privatbank.ua/api/statements/transactions"
def get_transactions(account_number, start_date, end_date, token):
"""
Get all transactions for a given account within a specified date range.
Args:
account_number (str): Bank account number.
start_date (str): Start date in format 'dd-MM-yyyy'.
end_date (str): End date in format 'dd-MM-yyyy'.
token (str): Authorization token for API access.
Returns:
list: List of transaction dictionaries.
"""
transactions = []
follow_id = None
headers = {"Content-Type": "application/json;utf8", "token": token}
while True:
response = requests.get(
f"{PRIVATBANK_TX_STATEMENTS_URL}"
f"?acc={account_number}&startDate={start_date}"
f"&endDate={end_date}&followId={follow_id if follow_id else ''}",
headers=headers,
)
data = response.json()
if data["status"] != "SUCCESS":
logging.error(f"Failed to fetch transactions: {data}")
break
transactions.extend([t for t in data["transactions"] if t["PR_PR"] == "r"])
logging.info(f"Fetched {len(transactions)} transactions so far.")
if not data["exist_next_page"]:
break
follow_id = data["next_page_id"]
return transactions
def calculate_daily_amounts(transactions, start_date, end_date=None):
"""
Calculate daily amounts of money in and out.
Args:
transactions (list): List of transaction dictionaries.
start_date (str): Start date in format 'dd-MM-yyyy'.
end_date (str, optional): End date in format 'dd-MM-yyyy'. Defaults to current date if None.
Returns:
dict: Dictionary with dates as keys and money in/out as values.
"""
start = datetime.strptime(start_date, "%d-%m-%Y")
end = datetime.strptime(end_date, "%d-%m-%Y") if end_date else datetime.now()
day_count = (end - start).days + 1
daily_totals = defaultdict(
lambda: {"money_in": 0, "money_out": 0, "money_in_UAH": 0, "money_out_UAH": 0}
)
for transaction in transactions:
date = datetime.strptime(
transaction["DATE_TIME_DAT_OD_TIM_P"], "%d.%m.%Y %H:%M:%S"
).date()
amount = float(transaction["SUM"])
amount_uah = float(transaction["SUM_E"])
if transaction["TRANTYPE"] == "C":
daily_totals[date]["money_in"] += amount
daily_totals[date]["money_in_UAH"] += amount_uah
else:
daily_totals[date]["money_out"] += amount
daily_totals[date]["money_out_UAH"] += amount_uah
for day in (start + timedelta(n) for n in range(day_count)):
if day.date() not in daily_totals:
daily_totals[day.date()] = {
"money_in": 0,
"money_out": 0,
"money_in_UAH": 0,
"money_out_UAH": 0,
}
sorted_totals = sorted(daily_totals.items())
return [
{"date": date.strftime("%Y-%m-%d"), **totals} for date, totals in sorted_totals
]
def generate_report(accounts, start_date, end_date, token, format="json"):
"""
Generate a report for a list of accounts over a specified date range in either JSON or CSV format.
Args:
accounts (list): List of account numbers.
start_date (str): Start date in format 'dd-MM-yyyy'.
end_date (str): End date in format 'dd-MM-yyyy'.
token (str): Authorization token for API access.
format (str): Output format of the report ('json' or 'csv').
Returns:
None: Outputs a file for each account in the specified format.
"""
for account in accounts:
logging.info(f"Processing account {account}.")
transactions = get_transactions(account, start_date, end_date, token)
if not transactions:
logging.warning(f"No transactions found for account {account}.")
continue
account_name = (
transactions[0].get("AUT_MY_NAM", "Unknown") if transactions else "Unknown"
)
daily_amounts = calculate_daily_amounts(transactions, start_date, end_date)
if format.lower() == "csv":
file_name = f"{account_name}_{account}_report_{start_date}_{end_date}.csv"
with open(file_name, "w", newline="") as file:
writer = csv.DictWriter(file, fieldnames=daily_amounts[0].keys())
writer.writeheader()
writer.writerows(daily_amounts)
else:
file_name = f"{account_name}_{account}_report_{start_date}_{end_date}.json"
with open(file_name, "w") as file:
json.dump(daily_amounts, file, indent=4)
logging.info(f"Report generated for account {account}: {file_name}")
def read_token_from_file(file_path):
"""
Reads a token from a given file.
Args:
file_path (str): Path to the file containing the token.
Returns:
str: Token as a string.
"""
try:
with open(file_path, "r") as file:
return file.read().strip()
except Exception as e:
logging.error(f"Error reading token from file: {e}")
sys.exit(1)
if __name__ == "__main__":
logging.basicConfig(
level=logging.INFO,
format="%(asctime)s - %(levelname)s - %(message)s",
stream=sys.stdout,
)
parser = argparse.ArgumentParser(
description="Generate transaction report for given accounts."
)
parser.add_argument(
"-s",
"--start-date",
type=str,
default=(datetime.now().replace(day=1) - timedelta(days=1))
.replace(day=1)
.strftime("%d-%m-%Y"),
help="Start date in format 'dd-MM-yyyy'. Default is first day of previous month.",
)
parser.add_argument(
"-e",
"--end-date",
type=str,
default="",
help="End date in format 'dd-MM-yyyy'. Default is empty (current date).",
)
parser.add_argument(
"-f",
"--format",
type=str,
default="csv",
choices=["json", "csv"],
help="Format of the report. Default is 'csv'.",
)
parser.add_argument(
"-t",
"--token",
type=str,
default="",
help="Authorization token for API access. This or -tf must be provided.",
)
parser.add_argument(
"-tf",
"--token-file",
type=str,
default="",
help="Path to a file containing the authorization token. This or -t must be provided.",
)
parser.add_argument(
"-a",
"--accounts",
type=lambda s: set(s.split(",")),
required=True,
help="Comma-separated list of account numbers.",
)
args = parser.parse_args()
if not args.token and not args.token_file:
parser.error("Either --token or --token-file must be provided.")
elif args.token_file:
args.token = read_token_from_file(args.token_file)
generate_report(
args.accounts, args.start_date, args.end_date, args.token, args.format
)