Files

276 lines
10 KiB
Python

"""
AUTHOR:
Khushal P Soonderji
DATE:
CREATED: Mon, 23rd Feb, 2026
UPDATED: Mon, 23rd Feb, 2026
OBJECTIVE:
To manually sync attendance for a specific time period.
REFERENCES:
N/A
DOWNLOADS:
N/A
"""
# *****************************************************************************************************************
# ***** ****
# *** IMPORT ***
# ***** ****
# *****************************************************************************************************************
# To make sibling directories accessible for imports:
import sys
sys.path.append(".")
sys.path.append("..")
# For system-level activities:
import os
import copy
# To work with date and time:
import time
import datetime
# To work with tabulate data:
import pandas as pd
# Cosec-related:
from cosec_web.cosec_web import CosecWeb
# TCAOFF-related:
from tcaoff.async_tcaoff import AsyncTheCAOffice
# My utils:
from utils_v2.system import files
from utils_v2.string import json
from utils_v2.string import regex
from utils_v2.date_time import date_time
# To work with datatypes:
from typing import List, Dict, Any
from collections import defaultdict
# To run a cron-like scheduler:
from apscheduler.schedulers.asyncio import AsyncIOScheduler
# For async activities:
import asyncio
# For debugging:
from icecream import IceCreamDebugger
# Common and Shared:
from scripts import common
# *****************************************************************************************************************
# ***** ****
# *** MACROS / ONE-TIME INIT ***
# ***** ****
# *****************************************************************************************************************
# Debugging:
printer = IceCreamDebugger(prefix = "Manual Att | ", includeContext = True)
err_printer = IceCreamDebugger(prefix = "[ERR] Manual Att | ", includeContext = True)
# *****************************************************************************************************************
# ***** ****
# *** VARIABLES ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** CLASSES ***
# ***** ****
# *****************************************************************************************************************
# --- Nothing Yet
# *****************************************************************************************************************
# ***** ****
# *** FUNCTIONS ***
# ***** ****
# *****************************************************************************************************************
async def manual_attendance(
cosec_creds: dict,
tcaoff_client: AsyncTheCAOffice,
from_date: date_time.datetime = None,
to_date: date_time.datetime = None,
test_mode: bool = False,
) -> None:
printer("MANUAL ATTENDANCE")
# Try the whole process once:
try:
# Log in to TCAOFF:
success = await tcaoff_client.login()
if not success: raise RuntimeError("TCAOFF Login Failed!")
# Try the part that needs COSEC:
try:
# Figure out the dates:
now = date_time.get_current_ist_date_time()
if not from_date: from_date = now - datetime.timedelta(days = 1)
if not to_date: to_date = now
# Set the correct sequence for the dates:
if from_date > to_date: from_date, to_date = to_date, from_date
# Get the previous day's In/Out Summary and then wait
# for the driver's resources to get freed:
success = common.get_in_out_summary(
cosec_creds = cosec_creds,
from_dt = from_date,
to_dt = to_date,
cache_file = common.get_manual_in_out_summary_cache_file_path,
test_mode = test_mode
)
# Sync data between Cosec and TCAOFF:
if success:
printer("IN/OUT SUMMARY: Sync'ing with TCAOFF")
print("SYNC FROM:", common.get_manual_in_out_summary_cache_file_path())
await common.sync_attendance_to_tcaoff(
tcaoff_client = tcaoff_client,
cosec_in_out_summary = json.from_file(common.get_manual_in_out_summary_cache_file_path()),
chunk_size = 10
)
# If something goes wrong in the COSEC step:
except Exception as exception:
err_printer(exception)
if test_mode: raise
# Log out of TCAOFF:
success = await tcaoff_client.logout()
if not success: raise RuntimeError("TCAOFF Logout Failed!")
# If something goes wrong:
except Exception as exception:
err_printer(exception)
await tcaoff_client.logout()
if test_mode: raise
# *****************************************************************************************************************
# ***** ****
# *** MAIN PROGRAM ***
# ***** ****
# *****************************************************************************************************************
if __name__ == "__main__":
# Parse args:
import argparse
ap = argparse.ArgumentParser()
ap.add_argument(
"--test", "--test-mode",
action = "store_true",
default = False
)
ap.add_argument(
"--verbose",
action = "store_true",
default = False
)
ap.add_argument(
"--from-date",
help = "The date from which you want to sync attendance.",
default = date_time.get_current_ist_date_time()
)
ap.add_argument(
"--to-date",
help = "The date till when you want to sync attendance.",
default = date_time.get_current_ist_date_time()
)
ap.add_argument(
"--cosec-creds",
help = "The credentials JSON from which you would like to connect to COSEC.",
default = "cosec.json"
)
ap.add_argument(
"--tcaoff-creds",
help = "The credentials JSON from which you would like to connect to TheCAOffice.",
default = "tcaoff.json"
)
ap.add_argument(
"--org", "--organization",
help = "When handling multiple organizations, this word will be used to keep their files separate.",
default = "default"
)
args = ap.parse_args()
# Hold temporary environment variables:
os.environ["ORG"] = args.org
# Read required credentials:
cosec_creds_path = os.path.join(common.CREDS_DIR, args.cosec_creds)
tcaoff_creds_path = os.path.join(common.CREDS_DIR, args.tcaoff_creds)
# ---
cosec_creds = json.from_file(cosec_creds_path)
tcaoff_creds = json.from_file(tcaoff_creds_path)
# Explicitly mention the expected file paths for other devs to maintain:
print("PROJ. DIR. :", common.PROJ_DIR)
print("COSEC CREDS :", cosec_creds_path)
print("TCAOFF CREDS:", tcaoff_creds_path)
# Ensure that certain required paths exist:
common.init_paths()
# Date-handling:
args.from_date = date_time.parse_date_time(args.from_date, timezone = date_time.TIMEZONE_IST)
args.to_date = date_time.parse_date_time(args.to_date, timezone = date_time.TIMEZONE_IST)
if args.from_date > args.to_date: args.from_date, args.to_date = args.to_date, args.from_date
args.from_date = args.from_date.replace(hour = 0, minute = 0, second = 0, microsecond = 0)
args.to_date = args.to_date.replace(hour = 23, minute = 59, second = 59, microsecond = 999999)
print("From Date:", args.from_date)
print(" To Date:", args.to_date)
# Create the clients:
tcaoff_client = AsyncTheCAOffice(
username = tcaoff_creds["creds"]["username"],
password = tcaoff_creds["creds"]["password"],
debug = True,
debug_only_errors = False if args.verbose else True,
)
# Schedule the activities:
asyncio.run(
manual_attendance(
cosec_creds = cosec_creds,
tcaoff_client = tcaoff_client,
from_date = args.from_date,
to_date = args.to_date,
test_mode = args.test,
)
)