Skip to content

Commit 66461a3

Browse files
authored
v2.3.0 refactor of database service (#37)
* fixed error showing url * added new database module
1 parent 8465899 commit 66461a3

File tree

10 files changed

+501
-0
lines changed

10 files changed

+501
-0
lines changed

bot/database_service/__init__.py

Whitespace-only changes.

bot/database_service/auth.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
from firebase_admin import firestore
2+
import firebase_admin
3+
from firebase_admin import credentials
4+
import os
5+
import json
6+
7+
8+
def get_db_client():
9+
"""
10+
Get credentials for the Google Sheets API.
11+
"""
12+
firebase_json = json.loads(os.environ["FIREBASE_JSON"])
13+
14+
cred = credentials.Certificate(firebase_json)
15+
firebase_admin.initialize_app(cred)
16+
17+
return firestore.client()
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
from bot.database_service.auth import get_db_client
2+
3+
4+
class FirestoreService:
5+
"""
6+
This class is responsible for managing the Firestore database.
7+
"""
8+
9+
def __init__(self):
10+
self.db = get_db_client()
11+
12+
# New user setup
13+
def new_user_setup(self, telegram_id, sheet_id):
14+
user_ref = self.db.collection("users").document(str(telegram_id))
15+
user_ref.set({"sheet_id": sheet_id})
16+
17+
# Check if user exists
18+
def check_if_user_exists(self, telegram_id):
19+
user_ref = self.db.collection("users").document(str(telegram_id))
20+
user_doc = user_ref.get()
21+
return user_doc.exists
22+
23+
# Get user sheet id
24+
def get_user_sheet_id(self, telegram_id):
25+
user_ref = self.db.collection("users").document(str(telegram_id))
26+
user_doc = user_ref.get()
27+
if user_doc.exists:
28+
return user_doc.get("sheet_id")
29+
else:
30+
return None
31+
32+
# Get all user IDs
33+
def get_all_user_id(self):
34+
users_ref = self.db.collection("users")
35+
user_ids = [int(user.id) for user in users_ref.stream()]
36+
return user_ids
37+
38+
# Get all sheet IDs
39+
def get_all_sheet_id(self):
40+
users_ref = self.db.collection("users")
41+
sheet_ids = []
42+
for user in users_ref.stream():
43+
sheet_ids.append(user.get("sheet_id"))
44+
return sheet_ids

bot/sheet_service/__init__.py

Whitespace-only changes.

bot/sheet_service/auth.py

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
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+
12+
import os
13+
import json
14+
from google.oauth2 import service_account
15+
16+
17+
def get_credentials():
18+
"""
19+
Get credentials for the Google Sheets API.
20+
"""
21+
SCOPES = ["https://www.googleapis.com/auth/spreadsheets"]
22+
google_json = os.getenv("GOOGLE_JSON")
23+
google_service = json.loads(google_json)
24+
creds = service_account.Credentials.from_service_account_info(
25+
google_service, scopes=SCOPES
26+
)
27+
return creds

bot/sheet_service/exceptions.py

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/sheet_service/sheets_range.py

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
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+
11+
# Sheet names
12+
DD_SHEET = "Dropdown" # DD for Dropdown
13+
TR_SHEET = "Tracker" # TR for Tracker
14+
15+
# Tracker columns
16+
TR_QUICKADD_TP_PAY = "G" # Transport Payment Column
17+
TR_QUICKADD_TP_TYPE = "H" # Transport Type Column
18+
TR_QUICKADD_OT_PAY = "I" # Others Payment Column
19+
TR_QUICKADD_OT_TYPE = "J" # Others Type Column
20+
21+
TR_QUICKADD_ROW_START = 3 # Quick Add Row
22+
TR_QUICKADD_ROW_END = 13 # Quick Add End Row
23+
24+
TR_START_COL = "B" # Start Column
25+
TR_END_COL = "E" # End Column
26+
TR_ROW = 3
27+
28+
# Dropdown rows
29+
DD_MAIN_CAT_ROW = 2
30+
DD_SUBCAT_START = 3
31+
DD_SUBCAT_END = 9
32+
33+
DD_MAIN_PAY_ROW = 12
34+
DD_SUBPAY_START = 13
35+
DD_SUBPAY_END = 19
36+
37+
DD_TRANSPORT_COL = "A"
38+
DD_OTHERS_COL_START = "B"
39+
DD_OTHERS_COL_END = "J"
40+
41+
DD_INCOME_COL = "L"
42+
43+
DD_PAYMENT_COL_START = "A"
44+
DD_PAYMENT_COL_END = "J"
45+
46+
# Column indexes
47+
START_COL_IDX = 0
48+
END_COL_IDX = 11
49+
50+
# Months
51+
OVERALL_RANGE = "!M13:O25"
52+
53+
# Transport ranges
54+
TRANSPORT_RANGE = create_range(
55+
DD_SHEET, DD_TRANSPORT_COL, DD_SUBCAT_START, DD_TRANSPORT_COL, DD_SUBCAT_END
56+
)
57+
58+
# Others ranges
59+
OTHERS_MAIN_RANGE = create_range(
60+
DD_SHEET, DD_OTHERS_COL_START, DD_MAIN_CAT_ROW, DD_OTHERS_COL_END, DD_MAIN_CAT_ROW
61+
)
62+
63+
OTHERS_SUB_RANGE = create_complex_range(
64+
DD_SHEET,
65+
ord(DD_OTHERS_COL_START),
66+
ord(DD_OTHERS_COL_END),
67+
DD_MAIN_CAT_ROW,
68+
DD_SUBCAT_END,
69+
)
70+
71+
# Payment ranges
72+
PAYMENT_MAIN_RANGE = create_range(
73+
DD_SHEET, DD_PAYMENT_COL_START, DD_MAIN_PAY_ROW, DD_PAYMENT_COL_END, DD_MAIN_PAY_ROW
74+
)
75+
76+
PAYMENT_SUB_RANGE = create_complex_range(
77+
DD_SHEET,
78+
ord(DD_PAYMENT_COL_START),
79+
ord(DD_PAYMENT_COL_END),
80+
DD_MAIN_PAY_ROW,
81+
DD_SUBPAY_END,
82+
)
83+
84+
# Income range
85+
INCOME_RANGE = create_range(
86+
DD_SHEET, DD_INCOME_COL, DD_MAIN_CAT_ROW, DD_INCOME_COL, DD_SUBCAT_END
87+
)
88+
89+
# Tracker ranges
90+
TRACKER_RANGE = create_range(TR_SHEET, TR_START_COL, TR_ROW, TR_END_COL, TR_ROW)
91+
92+
# Quick add ranges
93+
QUICK_ADD_RANGE = create_range(
94+
TR_SHEET,
95+
TR_QUICKADD_TP_PAY,
96+
TR_QUICKADD_ROW_START,
97+
TR_QUICKADD_OT_TYPE,
98+
TR_QUICKADD_ROW_START,
99+
)
100+
101+
QUICK_OTHERS_RANGE = create_range(
102+
TR_SHEET,
103+
TR_QUICKADD_OT_PAY,
104+
TR_QUICKADD_ROW_START,
105+
TR_QUICKADD_OT_TYPE,
106+
TR_QUICKADD_ROW_END,
107+
)
108+
109+
QUICK_TRANSPORT_RANGE = create_range(
110+
TR_SHEET,
111+
TR_QUICKADD_TP_PAY,
112+
TR_QUICKADD_ROW_START,
113+
TR_QUICKADD_TP_TYPE,
114+
TR_QUICKADD_ROW_END,
115+
)

0 commit comments

Comments
 (0)