(20251225) Added multi-day In/Out Summary compatibility.

This commit is contained in:
2025-12-25 15:54:22 +05:30
parent eed09b8135
commit d96a46ad46
4 changed files with 85 additions and 46 deletions
+43 -29
View File
@@ -51,6 +51,7 @@ import pandas as pd
from utils_v2.string import json from utils_v2.string import json
from utils_v2.system import files from utils_v2.system import files
from utils_v2.system import pfinfo from utils_v2.system import pfinfo
from utils_v2.date_time import date_time
# For browser automation: # For browser automation:
from selenium import webdriver from selenium import webdriver
@@ -311,6 +312,10 @@ class CosecWeb:
df = df[3:] # ............... drop more rows till the actual data starts df = df[3:] # ............... drop more rows till the actual data starts
df.columns = new_header # ... the actual header row becomes the DF's header df.columns = new_header # ... the actual header row becomes the DF's header
# If you request for data that spans over multiple days, you will get a row with datetime in to visually mark
# date change. You don't need this, drop it:
df = df[~df["User ID"].apply(lambda x: isinstance(x, (datetime.datetime, pd.Timestamp)))]
# Drop fully null rows: # Drop fully null rows:
df = df.dropna(how = "all", axis = 0) # ......... cleans rows df = df.dropna(how = "all", axis = 0) # ......... cleans rows
df = df.dropna(how = "all", axis = 1) # ......... cleans cols df = df.dropna(how = "all", axis = 1) # ......... cleans cols
@@ -991,7 +996,8 @@ class CosecWeb:
to_date: datetime.datetime, to_date: datetime.datetime,
group_ids: List[str], group_ids: List[str],
download_timeout: float = 60.0, download_timeout: float = 60.0,
initial_sleep: float = 1.0 initial_sleep: float = 1.0,
timezone: str = None,
) -> str | None: ) -> str | None:
""" """
@@ -1003,9 +1009,15 @@ class CosecWeb:
one group id. one group id.
:param download_timeout: The time to wait for the report to get downloaded. :param download_timeout: The time to wait for the report to get downloaded.
:param initial_sleep: How many seconds to wait before the first action is taken. :param initial_sleep: How many seconds to wait before the first action is taken.
:param timezone: A timezone to apply to the given date-time objects.
:return: The path to the downloaded report file. :return: The path to the downloaded report file.
""" """
# Apply the timezone if given:
if timezone:
from_date = date_time.to_timezone(from_date, timezone)
to_date = date_time.to_timezone(to_date, timezone)
# Empty out the past downloads: # Empty out the past downloads:
for file_name in files.list_files( for file_name in files.list_files(
self.downloads_dir, self.downloads_dir,
@@ -1057,8 +1069,8 @@ class CosecWeb:
texts = [ texts = [
from_date.strftime("%d/%m/%Y"), from_date.strftime("%d/%m/%Y"),
to_date.strftime("%d/%m/%Y"), to_date.strftime("%d/%m/%Y"),
"00:00", from_date.strftime("%H:%M"),
"23:59", to_date.strftime("%H:%M"),
], ],
clear_firsts = [ clear_firsts = [
True, True,
@@ -1235,14 +1247,14 @@ if __name__ == "__main__":
# # Create an instance of the automation object: # # Create an instance of the automation object:
# cosec = CosecWeb( # cosec = CosecWeb(
# cosec_url = "http://103.89.8.21/cosec", # cosec_url = "http://103.89.8.21/COSEC",
# username = "hrd1", # username = input("Username : "),
# password = "cosec", # password = input("Password : "),
# driver_dir = chrome_driver_dir, # driver_dir = chrome_driver_dir,
# user_data_dir = user_data_dir, # user_data_dir = user_data_dir,
# downloads_dir = downloads_dir, # downloads_dir = downloads_dir,
# ) # )
#
# # Perform the login: # # Perform the login:
# cosec.login(initial_sleep = 2.5) # cosec.login(initial_sleep = 2.5)
# #
@@ -1258,14 +1270,14 @@ if __name__ == "__main__":
# ], # ],
# download_timeout = 60.0 # download_timeout = 60.0
# ) # )
#
# # Convert the In/Out Summary to a Pandas DF: # Convert the In/Out Summary to a Pandas DF:
# # in_out_report_path = r"D:\programming\python\elcita_ofc_2025\downloads\Monthly_Details.xls" in_out_report_path = input("File Path : ")
# in_out_report_df = cosec.read_in_out_summary_xls(in_out_report_path) in_out_report_df = CosecWeb.read_in_out_summary_xls(in_out_report_path)
# in_out_report_df = in_out_report_df[:35] in_out_report_df = in_out_report_df[:35]
# print(in_out_report_df.to_string()) print(in_out_report_df.to_string())
# print("\n\n---\n\n") print("\n\n---\n\n")
# # print(in_out_report_df.info()) print(in_out_report_df.info())
# # Go back to the home page: # # Go back to the home page:
# cosec.return_home() # cosec.return_home()
@@ -1281,9 +1293,9 @@ if __name__ == "__main__":
# initial_sleep = 1.0, # initial_sleep = 1.0,
# download_timeout = 60.0 # download_timeout = 60.0
# ) # )
#
# Convert the Muster Roll to a Pandas DF: # Convert the Muster Roll to a Pandas DF:
muster_roll_path = r"D:\kps\PycharmProjects\cosec\cosec_web\sample_files\muster_roll.xls" muster_roll_path = input("File Path : ")
muster_roll_df = CosecWeb.read_muster_roll_xls(muster_roll_path) muster_roll_df = CosecWeb.read_muster_roll_xls(muster_roll_path)
muster_roll_df = muster_roll_df[[ muster_roll_df = muster_roll_df[[
"User ID", "User Name", "Category Name", "User ID", "User Name", "Category Name",
@@ -1291,18 +1303,20 @@ if __name__ == "__main__":
"Direct Reporting", "Level-1" "Direct Reporting", "Level-1"
]] ]]
muster_roll_df = muster_roll_df[:20] muster_roll_df = muster_roll_df[:20]
# print(muster_roll_df.to_string()) print(muster_roll_df.to_string())
# print("\n\n---\n\n") print("\n\n---\n\n")
# print(muster_roll_df.info()) print(muster_roll_df.info())
unique_branches = muster_roll_df["Branch Name"].unique().tolist()
unique_depts = muster_roll_df[["Branch Name", "Department Name"]].drop_duplicates().to_dict(orient = "records") # # Get unique combinations:
unique_reportees = muster_roll_df[["Branch Name", "Department Name", "Direct Reporting"]].drop_duplicates().to_dict(orient = "records") # unique_branches = muster_roll_df["Branch Name"].unique().tolist()
unique_combos = { # unique_depts = muster_roll_df[["Branch Name", "Department Name"]].drop_duplicates().to_dict(orient = "records")
"Branch Name": unique_branches, # unique_reportees = muster_roll_df[["Branch Name", "Department Name", "Direct Reporting"]].drop_duplicates().to_dict(orient = "records")
"Department Name": unique_depts, # unique_combos = {
"Direct Reporting": unique_reportees # "Branch Name": unique_branches,
} # "Department Name": unique_depts,
print("UNIQUE COMBOS:", json.to_string(unique_combos)) # "Direct Reporting": unique_reportees
# }
# print("UNIQUE COMBOS:", json.to_string(unique_combos))
# # Log out to end the cycle: # # Log out to end the cycle:
# cosec.logout() # cosec.logout()
+40 -15
View File
@@ -33,6 +33,9 @@
# To make sibling directories accessible for imports: # To make sibling directories accessible for imports:
import sys import sys
import pandas as pd
sys.path.append(".") sys.path.append(".")
sys.path.append("..") sys.path.append("..")
@@ -41,6 +44,7 @@ import os
# To work with date and time: # To work with date and time:
import time import time
import datetime
# Cosec-related: # Cosec-related:
from cosec_web.cosec_web import CosecWeb from cosec_web.cosec_web import CosecWeb
@@ -189,12 +193,20 @@ def get_in_out_summary(cosec_creds: dict):
# Get the in/out report: # Get the in/out report:
now_utc = date_time.get_current_utc_date_time() now_utc = date_time.get_current_utc_date_time()
from_dt = now_utc - datetime.timedelta(
days = cosec_creds["generalConfig"]["timedelta"]["days"],
hours = cosec_creds["generalConfig"]["timedelta"]["hours"],
minutes = cosec_creds["generalConfig"]["timedelta"]["minutes"],
seconds = cosec_creds["generalConfig"]["timedelta"]["seconds"],
)
to_dt = now_utc
report_path = cosec.get_in_out_summary( report_path = cosec.get_in_out_summary(
initial_sleep = 1.0, initial_sleep = 1.0,
from_date = now_utc, from_date = from_dt,
to_date = now_utc, to_date = to_dt,
group_ids = cosec_creds["inOutConfig"]["groupIds"], group_ids = cosec_creds["inOutConfig"]["groupIds"],
download_timeout = 60.0 download_timeout = 60.0,
timezone = cosec_creds["generalConfig"]["timezone"],
) )
# Log out to end the cycle: # Log out to end the cycle:
@@ -212,15 +224,16 @@ def get_in_out_summary(cosec_creds: dict):
# Assume that the punch time in the data is IST data, # Assume that the punch time in the data is IST data,
# then normalize it to UTC: # then normalize it to UTC:
report_data["Punch Time"] = report_data["Punch Time"].apply( def parse_dt(x):
lambda x: date_time.to_timezone( if pd.isnull(x): return None
else: return date_time.to_timezone(
datetime_object = date_time.as_if_timezone( datetime_object = date_time.as_if_timezone(
datetime_object = x, datetime_object = date_time.parse_date_time(x),
timezone = date_time.TIMEZONE_IST timezone = cosec_creds["generalConfig"]["timezone"]
), ),
timezone = date_time.TIMEZONE_UTC timezone = date_time.TIMEZONE_UTC
).timestamp() ).timestamp()
) report_data["Punch Time"] = report_data["Punch Time"].apply(lambda x: parse_dt(x))
# Do the remaining cleanup and formatting: # Do the remaining cleanup and formatting:
report_data = report_data.where(report_data.notna(), None) report_data = report_data.where(report_data.notna(), None)
@@ -245,11 +258,11 @@ def get_reports(cosec_creds: dict) -> None:
# Get the Muster Roll and then wait # Get the Muster Roll and then wait
# for the driver's resources to get freed: # for the driver's resources to get freed:
try: # try:
get_muster_roll(cosec_creds = cosec_creds) # get_muster_roll(cosec_creds = cosec_creds)
time.sleep(2.5) # time.sleep(2.5)
except Exception as e: # except Exception as e:
print("MUSTER ROLL FETCH FAILED!") # print("MUSTER ROLL FETCH FAILED!")
# Get the In-Out Summary and then wait # Get the In-Out Summary and then wait
# for the driver's resources to get freed: # for the driver's resources to get freed:
@@ -258,6 +271,7 @@ def get_reports(cosec_creds: dict) -> None:
time.sleep(2.5) time.sleep(2.5)
except Exception as e: except Exception as e:
print("IN-OUT SUMMARY FETCH FAILED!") print("IN-OUT SUMMARY FETCH FAILED!")
raise e
# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
@@ -298,6 +312,16 @@ if __name__ == "__main__":
"username": "<usr>", "username": "<usr>",
"password": "<pwd>" "password": "<pwd>"
}, },
"generalConfig": {
"pollInterval": 3600,
"timezone": "Asia/Kolkata",
"timedelta": {
"days": 0,
"hours": 24,
"minutes": 0,
"seconds": 0
}
},
"musterRollConfig": { "musterRollConfig": {
"groupIds": ["2", "3", "4"] "groupIds": ["2", "3", "4"]
}, },
@@ -307,7 +331,8 @@ if __name__ == "__main__":
} }
""" """
cosec_creds = json.from_file(cosec_creds_file)
loop( loop(
cosec_creds = json.from_file(cosec_creds_file), cosec_creds = cosec_creds,
interval_seconds = 900 # ... Run every 15 mins. interval_seconds = cosec_creds["generalConfig"]["pollInterval"],
) )
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long