(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.system import files
from utils_v2.system import pfinfo
from utils_v2.date_time import date_time
# For browser automation:
from selenium import webdriver
@@ -311,6 +312,10 @@ class CosecWeb:
df = df[3:] # ............... drop more rows till the actual data starts
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:
df = df.dropna(how = "all", axis = 0) # ......... cleans rows
df = df.dropna(how = "all", axis = 1) # ......... cleans cols
@@ -991,7 +996,8 @@ class CosecWeb:
to_date: datetime.datetime,
group_ids: List[str],
download_timeout: float = 60.0,
initial_sleep: float = 1.0
initial_sleep: float = 1.0,
timezone: str = None,
) -> str | None:
"""
@@ -1003,9 +1009,15 @@ class CosecWeb:
one group id.
: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 timezone: A timezone to apply to the given date-time objects.
: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:
for file_name in files.list_files(
self.downloads_dir,
@@ -1057,8 +1069,8 @@ class CosecWeb:
texts = [
from_date.strftime("%d/%m/%Y"),
to_date.strftime("%d/%m/%Y"),
"00:00",
"23:59",
from_date.strftime("%H:%M"),
to_date.strftime("%H:%M"),
],
clear_firsts = [
True,
@@ -1235,14 +1247,14 @@ if __name__ == "__main__":
# # Create an instance of the automation object:
# cosec = CosecWeb(
# cosec_url = "http://103.89.8.21/cosec",
# username = "hrd1",
# password = "cosec",
# cosec_url = "http://103.89.8.21/COSEC",
# username = input("Username : "),
# password = input("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)
#
@@ -1258,14 +1270,14 @@ if __name__ == "__main__":
# ],
# download_timeout = 60.0
# )
#
# # 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_df = cosec.read_in_out_summary_xls(in_out_report_path)
# in_out_report_df = in_out_report_df[:35]
# print(in_out_report_df.to_string())
# print("\n\n---\n\n")
# # print(in_out_report_df.info())
# Convert the In/Out Summary to a Pandas DF:
in_out_report_path = input("File Path : ")
in_out_report_df = CosecWeb.read_in_out_summary_xls(in_out_report_path)
in_out_report_df = in_out_report_df[:35]
print(in_out_report_df.to_string())
print("\n\n---\n\n")
print(in_out_report_df.info())
# # Go back to the home page:
# cosec.return_home()
@@ -1281,9 +1293,9 @@ if __name__ == "__main__":
# initial_sleep = 1.0,
# download_timeout = 60.0
# )
#
# 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 = muster_roll_df[[
"User ID", "User Name", "Category Name",
@@ -1291,18 +1303,20 @@ if __name__ == "__main__":
"Direct Reporting", "Level-1"
]]
muster_roll_df = muster_roll_df[:20]
# print(muster_roll_df.to_string())
# print("\n\n---\n\n")
# 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")
unique_reportees = muster_roll_df[["Branch Name", "Department Name", "Direct Reporting"]].drop_duplicates().to_dict(orient = "records")
unique_combos = {
"Branch Name": unique_branches,
"Department Name": unique_depts,
"Direct Reporting": unique_reportees
}
print("UNIQUE COMBOS:", json.to_string(unique_combos))
print(muster_roll_df.to_string())
print("\n\n---\n\n")
print(muster_roll_df.info())
# # Get unique combinations:
# unique_branches = muster_roll_df["Branch Name"].unique().tolist()
# unique_depts = muster_roll_df[["Branch Name", "Department Name"]].drop_duplicates().to_dict(orient = "records")
# unique_reportees = muster_roll_df[["Branch Name", "Department Name", "Direct Reporting"]].drop_duplicates().to_dict(orient = "records")
# unique_combos = {
# "Branch Name": unique_branches,
# "Department Name": unique_depts,
# "Direct Reporting": unique_reportees
# }
# print("UNIQUE COMBOS:", json.to_string(unique_combos))
# # Log out to end the cycle:
# cosec.logout()
+40 -15
View File
@@ -33,6 +33,9 @@
# To make sibling directories accessible for imports:
import sys
import pandas as pd
sys.path.append(".")
sys.path.append("..")
@@ -41,6 +44,7 @@ import os
# To work with date and time:
import time
import datetime
# Cosec-related:
from cosec_web.cosec_web import CosecWeb
@@ -189,12 +193,20 @@ def get_in_out_summary(cosec_creds: dict):
# Get the in/out report:
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(
initial_sleep = 1.0,
from_date = now_utc,
to_date = now_utc,
from_date = from_dt,
to_date = to_dt,
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:
@@ -212,15 +224,16 @@ def get_in_out_summary(cosec_creds: dict):
# Assume that the punch time in the data is IST data,
# then normalize it to UTC:
report_data["Punch Time"] = report_data["Punch Time"].apply(
lambda x: date_time.to_timezone(
def parse_dt(x):
if pd.isnull(x): return None
else: return date_time.to_timezone(
datetime_object = date_time.as_if_timezone(
datetime_object = x,
timezone = date_time.TIMEZONE_IST
datetime_object = date_time.parse_date_time(x),
timezone = cosec_creds["generalConfig"]["timezone"]
),
timezone = date_time.TIMEZONE_UTC
).timestamp()
)
report_data["Punch Time"] = report_data["Punch Time"].apply(lambda x: parse_dt(x))
# Do the remaining cleanup and formatting:
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
# for the driver's resources to get freed:
try:
get_muster_roll(cosec_creds = cosec_creds)
time.sleep(2.5)
except Exception as e:
print("MUSTER ROLL FETCH FAILED!")
# try:
# get_muster_roll(cosec_creds = cosec_creds)
# time.sleep(2.5)
# except Exception as e:
# print("MUSTER ROLL FETCH FAILED!")
# Get the In-Out Summary and then wait
# for the driver's resources to get freed:
@@ -258,6 +271,7 @@ def get_reports(cosec_creds: dict) -> None:
time.sleep(2.5)
except Exception as e:
print("IN-OUT SUMMARY FETCH FAILED!")
raise e
# ---------------------------------------------------------------------------------------------------------------------
@@ -298,6 +312,16 @@ if __name__ == "__main__":
"username": "<usr>",
"password": "<pwd>"
},
"generalConfig": {
"pollInterval": 3600,
"timezone": "Asia/Kolkata",
"timedelta": {
"days": 0,
"hours": 24,
"minutes": 0,
"seconds": 0
}
},
"musterRollConfig": {
"groupIds": ["2", "3", "4"]
},
@@ -307,7 +331,8 @@ if __name__ == "__main__":
}
"""
cosec_creds = json.from_file(cosec_creds_file)
loop(
cosec_creds = json.from_file(cosec_creds_file),
interval_seconds = 900 # ... Run every 15 mins.
cosec_creds = cosec_creds,
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