Skip to content

Commit 2a461fa

Browse files
committed
some minor updates
1 parent 747060e commit 2a461fa

File tree

6 files changed

+234
-31
lines changed

6 files changed

+234
-31
lines changed

bot/google_sheet_service/auth.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,23 @@
1+
"""
2+
auth.py
3+
4+
This file contains a function that returns the credentials for the Google Sheets API.
5+
The credentials are obtained from the GOOGLE_JSON environment variable, which is set in the .env file.
6+
The get_credentials function uses the google.oauth2.service_account module to create a credentials
7+
object from the service account info in the GOOGLE_JSON environment variable.
8+
The credentials object is then returned to the caller.
9+
10+
"""
11+
112
import os
213
import json
314
from google.oauth2 import service_account
415

516

617
def get_credentials():
18+
"""
19+
Get credentials for the Google Sheets API.
20+
"""
721
SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
822
google_json = os.getenv("GOOGLE_JSON")
923
google_service = json.loads(google_json)
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
from googleapiclient.errors import HttpError
2+
from functools import wraps
3+
4+
# Custom exception classes defined here
5+
6+
7+
def google_sheets_exception_handler(func):
8+
@wraps(func)
9+
def wrapper(*args, **kwargs):
10+
try:
11+
return func(*args, **kwargs)
12+
except HttpError as e:
13+
if e.resp.status == 401:
14+
raise AuthenticationError(
15+
"Authentication failed: Check your credentials."
16+
)
17+
elif e.resp.status == 404:
18+
raise SpreadsheetNotFoundError(
19+
"Spreadsheet not found: Check your spreadsheet ID."
20+
)
21+
else:
22+
raise APIRequestError(
23+
f"API request failed with status {e.resp.status}: {e.error_details}"
24+
)
25+
except Exception as e:
26+
raise SheetsServiceError(f"An unexpected error occurred: {str(e)}")
27+
28+
return wrapper
29+
30+
31+
class SheetsServiceError(Exception):
32+
"""Base class for exceptions in this module."""
33+
34+
pass
35+
36+
37+
class AuthenticationError(SheetsServiceError):
38+
"""Raised when there's an issue with authentication or credentials."""
39+
40+
pass
41+
42+
43+
class SpreadsheetNotFoundError(SheetsServiceError):
44+
"""Raised when a specified spreadsheet cannot be found."""
45+
46+
pass
47+
48+
49+
class APIRequestError(SheetsServiceError):
50+
"""Raised for errors related to making API requests to Google Sheets."""
51+
52+
pass
53+
54+
55+
class InvalidEntryTypeError(SheetsServiceError):
56+
"""Raised when an invalid entry type is specified."""
57+
58+
pass

bot/google_sheet_service/sheets_api.py

Lines changed: 0 additions & 9 deletions
This file was deleted.

bot/google_sheet_service/config.py renamed to bot/google_sheet_service/sheets_range.py

Lines changed: 14 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,13 @@
1+
"""
2+
sheets_range.py
3+
4+
This file contains the ranges for the Google Sheets Services for consistency.
5+
The ranges are used to access specific cells in the Google Sheets.
6+
7+
"""
8+
9+
from bot.google_sheet_service.utils import create_range, create_complex_range
10+
111
# Sheet names
212
DD_SHEET = "Dropdown" # DD for Dropdown
313
TR_SHEET = "Tracker" # TR for Tracker
@@ -40,28 +50,6 @@
4050
# Months
4151
OVERALL_RANGE = "!M13:O25"
4252

43-
44-
# Helper functions
45-
def create_range(sheet, start_col, start_row, end_col=None, end_row=None):
46-
"""
47-
Create a standard range string.
48-
Sample output: "Dropdown!A2:A9"
49-
"""
50-
end_part = f":{end_col}{end_row}" if end_col and end_row else ""
51-
return f"{sheet}!{start_col}{start_row}{end_part}"
52-
53-
54-
def create_complex_range(sheet, start_col_ord, end_col_ord, row_start, row_end):
55-
"""
56-
Create a range string for complex cases.
57-
Sample output: ["Dropdown!B2:B9", "Dropdown!C2:C9", ...]
58-
"""
59-
return [
60-
f"{sheet}!{chr(i)}{row_start}:{chr(i)}{row_end}"
61-
for i in range(start_col_ord, end_col_ord)
62-
]
63-
64-
6553
# Transport ranges
6654
TRANSPORT_RANGE = create_range(
6755
DD_SHEET, DD_TRANSPORT_COL, DD_SUBCAT_START, DD_TRANSPORT_COL, DD_SUBCAT_END
@@ -71,6 +59,7 @@ def create_complex_range(sheet, start_col_ord, end_col_ord, row_start, row_end):
7159
OTHERS_MAIN_RANGE = create_range(
7260
DD_SHEET, DD_OTHERS_COL_START, DD_MAIN_CAT_ROW, DD_OTHERS_COL_END, DD_MAIN_CAT_ROW
7361
)
62+
7463
OTHERS_SUB_RANGE = create_complex_range(
7564
DD_SHEET,
7665
ord(DD_OTHERS_COL_START),
@@ -83,6 +72,7 @@ def create_complex_range(sheet, start_col_ord, end_col_ord, row_start, row_end):
8372
PAYMENT_MAIN_RANGE = create_range(
8473
DD_SHEET, DD_PAYMENT_COL_START, DD_MAIN_PAY_ROW, DD_PAYMENT_COL_END, DD_MAIN_PAY_ROW
8574
)
75+
8676
PAYMENT_SUB_RANGE = create_complex_range(
8777
DD_SHEET,
8878
ord(DD_PAYMENT_COL_START),
@@ -107,13 +97,15 @@ def create_complex_range(sheet, start_col_ord, end_col_ord, row_start, row_end):
10797
TR_QUICKADD_OT_TYPE,
10898
TR_QUICKADD_ROW_START,
10999
)
100+
110101
QUICK_OTHERS_RANGE = create_range(
111102
TR_SHEET,
112103
TR_QUICKADD_OT_PAY,
113104
TR_QUICKADD_ROW_START,
114105
TR_QUICKADD_OT_TYPE,
115106
TR_QUICKADD_ROW_END,
116107
)
108+
117109
QUICK_TRANSPORT_RANGE = create_range(
118110
TR_SHEET,
119111
TR_QUICKADD_TP_PAY,
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
"""
2+
sheet_api.py
3+
4+
5+
"""
6+
7+
# sheets_api.py
8+
from googleapiclient.discovery import build
9+
from bot.common import EntryType
10+
from bot.google_sheet_service.auth import get_credentials
11+
from bot.google_sheet_service.sheets_range import *
12+
13+
14+
class GoogleSheetsClient:
15+
_instance = None
16+
17+
@classmethod
18+
def get_instance(cls):
19+
if cls._instance is None:
20+
creds = get_credentials()
21+
cls._instance = build("sheets", "v4", credentials=creds)
22+
return cls._instance
23+
24+
25+
class DropdownManager:
26+
"""
27+
This class is responsible for managing the Dropdown sheet in the google sheet.
28+
"""
29+
30+
def __init__(self):
31+
self.sheets_api = GoogleSheetsClient.get_instance()
32+
33+
# to be rename as get_header_values
34+
def get_main_dropdown_value(self, spreadsheet_id, entry_type) -> list[str]:
35+
"""
36+
This method gets the header values for the CATEGORY/PAYMENT of the transaction.
37+
"""
38+
range = []
39+
if entry_type == EntryType.TRANSPORT:
40+
# actually if entry_type is transport shouldnt be calling this
41+
# but instead call get_sub_dropdown_value instead
42+
# will do something to make this change after refactoring
43+
range = TRANSPORT_RANGE
44+
elif entry_type == EntryType.OTHERS:
45+
range = OTHERS_MAIN_RANGE
46+
else:
47+
range = PAYMENT_MAIN_RANGE
48+
49+
results = (
50+
self.sheets_api.spreadsheets()
51+
.values()
52+
.get(spreadsheetId=spreadsheet_id, range=range)
53+
.execute()
54+
)
55+
56+
values_results = results.get("values", [])
57+
58+
# to remove this and probably move it to get_sub_dropdown_value
59+
if entry_type == EntryType.TRANSPORT:
60+
results_list = []
61+
for sublist in values_results:
62+
for item in sublist:
63+
results_list.append(item)
64+
return results_list
65+
66+
return values_results[0]
67+
68+
# to be rename as get_sub_values
69+
def get_sub_dropdown_value(
70+
self, spreadsheet_id, header_value, entry_type
71+
) -> list[str]:
72+
range = []
73+
# if entry_type is transport, should be calling this instead of get_main_dropdown_value
74+
if entry_type == EntryType.OTHERS:
75+
range = OTHERS_SUB_RANGE
76+
else:
77+
range = PAYMENT_SUB_RANGE
78+
results = (
79+
self.sheets_api.spreadsheets()
80+
.values()
81+
.batchGet(spreadsheetId=spreadsheet_id, ranges=range)
82+
.execute()
83+
)
84+
85+
value_results = results.get("valueRanges", [])
86+
87+
dropdown = []
88+
for value in value_results:
89+
if value.get("values", []):
90+
if header_value == value.get("values", [])[0][0]:
91+
dropdown.append(value.get("values", []))
92+
pass
93+
94+
result_list = [item for sublist in dropdown[0] for item in sublist]
95+
return result_list
96+
97+
98+
class TrackerManager:
99+
"""
100+
This class is responsible for managing the Tracker sheet in the google sheet.
101+
"""
102+
103+
def __init__(self):
104+
self.sheets_api = GoogleSheetsClient.get_instance()
105+
106+
107+
class EntryManager:
108+
"""
109+
This class is responsible for logging of transactions in the google sheet.
110+
"""
111+
112+
def __init__(self):
113+
self.sheets_api = GoogleSheetsClient.get_instance()
114+
115+
116+
class SheetManager:
117+
"""
118+
This class is responsible for retrieving/moving the google sheet.
119+
"""
120+
121+
def __init__(self):
122+
self.sheets_api = GoogleSheetsClient.get_instance()

bot/google_sheet_service/utils.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
utils.py
3+
4+
This file contains utility functions for the Google Sheet Services.
5+
6+
"""
7+
8+
9+
def create_range(sheet, start_col, start_row, end_col=None, end_row=None):
10+
"""
11+
Create a standard range string.
12+
Sample output: "Dropdown!A2:A9"
13+
"""
14+
end_part = f":{end_col}{end_row}" if end_col and end_row else ""
15+
return f"{sheet}!{start_col}{start_row}{end_part}"
16+
17+
18+
def create_complex_range(sheet, start_col_ord, end_col_ord, row_start, row_end):
19+
"""
20+
Create a range string for complex cases.
21+
Sample output: ["Dropdown!B2:B9", "Dropdown!C2:C9", ...]
22+
"""
23+
return [
24+
f"{sheet}!{chr(i)}{row_start}:{chr(i)}{row_end}"
25+
for i in range(start_col_ord, end_col_ord + 1)
26+
]

0 commit comments

Comments
 (0)