diff --git a/backend/api/blueprints/reports/in_out_report.py b/backend/api/blueprints/reports/in_out_report.py index 26e8ba2..e25295a 100644 --- a/backend/api/blueprints/reports/in_out_report.py +++ b/backend/api/blueprints/reports/in_out_report.py @@ -206,7 +206,7 @@ async def in_out_report_generate( if report_path is None: success = False - # Convert the In/Out Summary to a Pandas DF: + # If the data was loaded successfully: else: success = True report_df = cosec.read_in_out_summary_xls(report_path) diff --git a/backend/api/blueprints/reports/muster_roll.py b/backend/api/blueprints/reports/muster_roll.py index 0e908ba..0a787ce 100644 --- a/backend/api/blueprints/reports/muster_roll.py +++ b/backend/api/blueprints/reports/muster_roll.py @@ -68,6 +68,9 @@ from utils_v2.api.async_quart import ( from backend.models.api.common import SimpleCosecCredentialsHeaders from backend.models.api.reports.muster_roll import CosecMusterRollReportRequestData +# Helpers: +from backend.api.helpers import muster_roll + # Cosec-related: from cosec_web.cosec_web import CosecWeb @@ -160,61 +163,19 @@ async def muster_roll_report_generate( success = False report_data = None - # Figure out the paths: - base_dir = files.get_parent_directory( - files.get_file_directory(include_filename = False), - depth = 4 - ) - - # Put together the directory for the drivers, the downloads, etc.: - chrome_driver_dir = os.path.join(base_dir, r"drivers/chrome") - user_data_dir = os.path.join(base_dir, r"browser/user_data") - downloads_dir = os.path.join(base_dir, r"downloads") - - # Show all the paths for debugging: - current_app.printer("PATHS:", chrome_driver_dir, user_data_dir, downloads_dir) - - # Create an instance of the automation object: - cosec = CosecWeb( + # Get the muster roll data: + report_data = muster_roll.load( cosec_url = inbound_headers.cosecUrl, username = inbound_headers.cosecUsername, password = inbound_headers.cosecPassword, - driver_dir = chrome_driver_dir, - user_data_dir = user_data_dir, - downloads_dir = downloads_dir, - ) - - # Perform the login: - cosec.login(initial_sleep = 2.5) - - # Get the in/out report: - report_path = cosec.get_muster_roll( - initial_sleep = 1.0, - on_date = inbound_data.onDate, group_ids = inbound_data.groupIds, - download_timeout = 60.0 + on_date = inbound_data.onDate, ) - # Log out to end the cycle: - cosec.logout() - - # Close the browser window: - cosec.quit() - - # If we didn't get any path, the download failed: - if report_path is None: - success = False - - # Convert the In/Out Summary to a Pandas DF: - else: + # If data was loaded: + if report_data is not None: + report_data = report_data.to_dict(orient = "records") success = True - report_df = cosec.read_muster_roll_xls(report_path) - report_df = report_df[[ - "User ID", "User Name", "Category Name", - "Grade Name", "Branch Name", "Department Name", - "Direct Reporting", "Level-1" - ]] - report_data = report_df.to_dict(orient = "records") # ┳┓ # ┣┫┏┓┏┏┓┏┓┏┓┏┏┓ diff --git a/backend/api/helpers/__init__.py b/backend/api/helpers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/api/helpers/muster_roll.py b/backend/api/helpers/muster_roll.py new file mode 100644 index 0000000..0b81588 --- /dev/null +++ b/backend/api/helpers/muster_roll.py @@ -0,0 +1,211 @@ +""" + + AUTHOR: + + Khushal P Soonderji + + DATE: + + CREATED: Wed, 19th Nov, 2025 + UPDATED: Wed, 19th Nov, 2025 + + OBJECTIVE: + + To load and manage offline master muster roll data. + + REFERENCES: + + N/A + + DOWNLOADS: + + N/A + +""" + + +# ***************************************************************************************************************** +# ***** **** +# *** IMPORT *** +# ***** **** +# ***************************************************************************************************************** + + +# To make sibling directories accessible for imports: +import sys +sys.path.append(".") +sys.path.append("..") + +# System-level: +import os +import datetime + +# For using Quart: +from quart import current_app + +# To work with tabulated data: +import pandas as pd + +# Cosec-related: +from cosec_web.cosec_web import CosecWeb + +# My utils: +from utils_v2.date_time import date_time +from utils_v2.system import files + +# To work with datatypes: +from typing import List + + +# ***************************************************************************************************************** +# ***** **** +# *** MACROS / ONE-TIME INIT *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** VARIABLES *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** CLASSES *** +# ***** **** +# ***************************************************************************************************************** + + +# --- Nothing Yet + + +# ***************************************************************************************************************** +# ***** **** +# *** FUNCTIONS *** +# ***** **** +# ***************************************************************************************************************** + + +def init() -> None: + + current_app.muster_roll = None + current_app.last_muster_roll_load_ts = date_time.get_current_utc_date_time(as_string = False) + + +# --------------------------------------------------------------------------------------------------------------------- + + +def load_from_cosec( + cosec_url: str, + username: str, + password: str, + group_ids: List[str], + on_date: datetime.datetime = None, +) -> pd.DataFrame | None: + + # Figure out the paths: + base_dir = files.get_parent_directory( + files.get_file_directory(include_filename = False), + depth = 3 + ) + + # Put together the directory for the drivers, the downloads, etc.: + chrome_driver_dir = os.path.join(base_dir, r"drivers/chrome") + user_data_dir = os.path.join(base_dir, r"browser/user_data") + downloads_dir = os.path.join(base_dir, r"downloads") + masters_dir = os.path.join(base_dir, r"masters") + + # Create an instance of the automation object: + cosec = CosecWeb( + cosec_url = cosec_url, + username = username, + password = password, + driver_dir = chrome_driver_dir, + user_data_dir = user_data_dir, + downloads_dir = downloads_dir, + ) + + # Perform the login: + cosec.login(initial_sleep = 2.5) + + # Get the in/out report: + report_path = cosec.get_muster_roll( + initial_sleep = 1.0, + on_date = on_date or date_time.get_current_utc_date_time(), + group_ids = group_ids, + download_timeout = 60.0 + ) + + # Log out to end the cycle: + cosec.logout() + + # Close the browser window: + cosec.quit() + + # If we didn't get any path, the download failed: + if report_path is None: + current_app.printer("FAILED to fetch Muster Roll.") + current_app.muster_roll = None + + # Else, we parse the data and hold it: + else: + current_app.printer("Fetched Muster Roll.") + report_df = cosec.read_muster_roll_xls(report_path) + report_df = report_df[[ + "User ID", "User Name", "Category Name", + "Grade Name", "Branch Name", "Department Name", + "Direct Reporting", "Level-1" + ]] + current_app.muster_roll = report_df + current_app.last_muster_roll_load_ts = date_time.get_current_utc_date_time(as_string = False) + + # Done here: + return current_app.muster_roll + + +# --------------------------------------------------------------------------------------------------------------------- + + +def load( + cosec_url: str, + username: str, + password: str, + group_ids: List[str], + on_date: datetime.datetime = None, +) -> pd.DataFrame: + + # If the data either doesn't exist at all, + # or the data has gone stale, reload: + if ( + current_app.muster_roll is None + # or current_app.last_muster_roll_load_ts.timestamp() - date_time.get_current_utc_date_time(as_string = False).timestamp() >= 300 + ): load_from_cosec( + cosec_url = cosec_url, + username = username, + password = password, + group_ids = group_ids, + on_date = on_date, + ) + + # Done here: + return current_app.muster_roll + + +# ***************************************************************************************************************** +# ***** **** +# *** MAIN PROGRAM *** +# ***** **** +# ***************************************************************************************************************** + + +if __name__ == "__main__": + + pass diff --git a/backend/api/main.py b/backend/api/main.py index 495f4d6..4132c6f 100644 --- a/backend/api/main.py +++ b/backend/api/main.py @@ -47,6 +47,7 @@ from quart_cors import cors # My utils: from utils_v2.date_time import date_time +from utils_v2.system import files from utils_v2.api.async_quart import ( set_api_version, log_request_to_mongo, @@ -65,6 +66,9 @@ from icecream import IceCreamDebugger from backend.api.blueprints.reports.in_out_report import in_out_report_bp from backend.api.blueprints.reports.muster_roll import muster_roll_report_bp +# The helpers: +from backend.api.helpers import muster_roll + # ***************************************************************************************************************** # ***** **** @@ -207,7 +211,21 @@ async def app_startup(**kwargs): # ┃┃┃┓┏┏ # ┛ ┗┗┛┗ - pass + # cosec_creds = files.read_file( + # file_path = os.path.join( + # files.get_parent_directory( + # path = files.get_file_directory(include_filename = False), + # depth = 2 + # ), + # "creds", + # "cosec", + # "cosec_velankani.json" + # ) + # ) + muster_roll.init() + # muster_roll.load( + # + # ) # ┏┓┓ # ┃ ┃┏┓┏┓┏┓┓┏┏┓