(20251225) Added multi-day In/Out Summary compatibility.
This commit is contained in:
+43
-29
@@ -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
@@ -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"],
|
||||||
)
|
)
|
||||||
|
|||||||
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user