(20260119) Attendance now captures total hours, over-time hours, first in time, and last out time.

This commit is contained in:
2026-01-19 14:02:35 +05:30
parent 9640972085
commit 9423af0919
4 changed files with 139 additions and 8 deletions
+12 -2
View File
@@ -276,7 +276,7 @@ def sync_teams_to_tcaoff(
role = (cosec_team.get("Grade Name") or "Unknown").strip(), role = (cosec_team.get("Grade Name") or "Unknown").strip(),
username = tcaoff_username, username = tcaoff_username,
password = "Vispl@123", password = "Vispl@123",
application_notes = {"cosec": cosec_team} applicant_notes = {"cosec": cosec_team}
) )
# response["total"] += 1 # response["total"] += 1
if success: response["success"] += 1 if success: response["success"] += 1
@@ -397,6 +397,10 @@ def sync_attendance_to_tcaoff(
"work_date": last_dt "work_date": last_dt
} }
# Add fixed known details:
user_id_to_time[user_id]["first_in"] = first_in
user_id_to_time[user_id]["last_out"] = last_out
print("\n") print("\n")
print("Result:") print("Result:")
print("------") print("------")
@@ -448,7 +452,7 @@ def sync_attendance_to_tcaoff(
continue continue
# Update the attendance on TCAOFF: # Update the attendance on TCAOFF:
hours_worked = work["work_seconds"] / (60 * 60) hours_worked = work["work_seconds"] // (60 * 60)
if hours_worked > 10.0: status = "OT" if hours_worked > 10.0: status = "OT"
elif 7.5 < hours_worked <= 10.0: status = "P" elif 7.5 < hours_worked <= 10.0: status = "P"
elif 2.5 < hours_worked <= 5.0: status = "H" elif 2.5 < hours_worked <= 5.0: status = "H"
@@ -460,6 +464,11 @@ def sync_attendance_to_tcaoff(
status = status, status = status,
over_time = max(0.0, hours_worked - 10.0), over_time = max(0.0, hours_worked - 10.0),
attendance_date = work["work_date"], attendance_date = work["work_date"],
json_notes = {
"totHours": hours_worked,
"firstIn": work["first_in"],
"lastOut": work["last_out"],
}
) )
if success: response["success"] += 1 if success: response["success"] += 1
else: response["fail"] += 1 else: response["fail"] += 1
@@ -935,6 +944,7 @@ if __name__ == "__main__":
"deptAdd": "https://api.thecaoffice.com/commons/departments/add", "deptAdd": "https://api.thecaoffice.com/commons/departments/add",
"teamList": "https://api.thecaoffice.com/team/list", "teamList": "https://api.thecaoffice.com/team/list",
"teamAdd": "https://api.thecaoffice.com/team/add", "teamAdd": "https://api.thecaoffice.com/team/add",
"teamUpdate": "https://api.thecaoffice.com/team/update",
"attendanceList": "https://api.thecaoffice.com/user/attendance/register", "attendanceList": "https://api.thecaoffice.com/user/attendance/register",
"attendanceMark": "https://api.thecaoffice.com/user/attendance/mark" "attendanceMark": "https://api.thecaoffice.com/user/attendance/mark"
} }
+77 -6
View File
@@ -376,7 +376,7 @@ def team_add(
role: str, role: str,
username: str, username: str,
password: str, password: str,
application_notes: dict | list | str = None applicant_notes: dict | list | str = None
) -> bool: ) -> bool:
""" """
@@ -391,13 +391,13 @@ def team_add(
:param role: The role of the team member in the organization. :param role: The role of the team member in the organization.
:param username: The unique username of the team member. Cannot be the same as anyone else. :param username: The unique username of the team member. Cannot be the same as anyone else.
:param password: The password for this team member's login. :param password: The password for this team member's login.
:param application_notes: Optional notes about the team member. :param applicant_notes: Optional notes about the team member.
:return: True if successful, else False. :return: True if successful, else False.
""" """
# Ensure that JSON notes are converted to string: # # Ensure that JSON notes are converted to string:
if isinstance(application_notes, (list, dict)): # if isinstance(applicant_notes, (list, dict)):
application_notes = json.to_string(application_notes, no_space = True) # applicant_notes = json.to_string(applicant_notes, no_space = True)
# Make the API call: # Make the API call:
response = requests.post( response = requests.post(
@@ -414,7 +414,7 @@ def team_add(
"username": username, "username": username,
"password": password, "password": password,
"hierarchy": 1, "hierarchy": 1,
"applicantNotes": application_notes "applicantNotes": applicant_notes
} }
) )
@@ -437,12 +437,79 @@ def team_add(
# --------------------------------------------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------------------------------------------
def team_update(
tcaoff_creds: dict,
user_id: int,
branch_id: int,
dept_id: int,
reporting_to: int | None,
team_name: str,
email: str,
phone_no: str,
role: str,
applicant_notes: dict | list | str = None
) -> bool:
"""
Add a new team member.
:param tcaoff_creds: The set of credentials to use with the API call.
:param dept_id: The id of the department that this team member is working in.
:param branch_id: The id of the branch that this team member is working in.
:param reporting_to: The id of the senior to whom this team member will report.
:param team_name: The name of the team member. This is the full display name. Can be the same as others.
:param email: The email id of the team member.
:param phone_no: The phone no. of the team member.
:param role: The role of the team member in the organization.
:param applicant_notes: Optional notes about the team member.
:return: True if successful, else False.
"""
# # Ensure that JSON notes are converted to string:
# if isinstance(applicant_notes, (list, dict)):
# applicant_notes = json.to_string(applicant_notes, no_space = True)
# Make the API call:
response = requests.post(
url = tcaoff_creds["urls"]["teamUpdate"],
headers = {"X-Session-Token": tcaoff_creds["creds"]["sessionToken"]},
json = {
"branchId": branch_id,
"idDepartment": dept_id,
"reportingTo": reporting_to,
"name": team_name,
"email": email,
"phoneNo": phone_no,
"role": role,
"applicantNotes": applicant_notes
}
)
# Debugging:
if response.status_code not in [200]:
response_json = response.json()
print("TCAOFF Team-Update:", json.to_string(response_json))
# If the call succeeded:
if response.status_code in [200]:
print(f"TCAOFF: team '{user_id}' updated")
return True
# If the call failed:
else:
print(f"TCAOFF: team '{user_id}' NOT updated")
return False
# ---------------------------------------------------------------------------------------------------------------------
def attendance_mark( def attendance_mark(
tcaoff_creds: dict, tcaoff_creds: dict,
user_id: int, user_id: int,
status: Literal["P", "H", "A", "OT"], status: Literal["P", "H", "A", "OT"],
over_time: int | float, over_time: int | float,
attendance_date: datetime.datetime | None = None, attendance_date: datetime.datetime | None = None,
json_notes: dict = None
) -> bool: ) -> bool:
""" """
@@ -470,6 +537,10 @@ def attendance_mark(
"ot": over_time, "ot": over_time,
} }
# Add JSON notes is needed:
if json_notes:
json_payload["jsonNotes"] = json_notes
# Make the API call: # Make the API call:
response = requests.post( response = requests.post(
url = tcaoff_creds["urls"]["attendanceMark"], url = tcaoff_creds["urls"]["attendanceMark"],
File diff suppressed because one or more lines are too long
+49
View File
@@ -0,0 +1,49 @@
"""
This is a one-time fix script where we need to fix some content of the accounts created.
"""
# Imports:
import os
import pandas as pd
from helpers import tcaoff
from utils_v2.string import json
from utils_v2.system import files
# File paths:
file_dir = files.get_file_directory(include_filename = False)
proj_dir = files.get_parent_directory(file_dir, depth = 1)
tcaoff_creds_file = os.path.join(proj_dir, "creds", "tcaoff.json")
# Load the creds:
tcaoff_creds = json.from_file(tcaoff_creds_file)
# Log in:
tcaoff.login(tcaoff_creds)
# Load the team list:
team_list = tcaoff.team_list(tcaoff_creds)
# Loop through the list,
# check if `applicantNotes` in `json_notes` is a string,
# if it is a string, convert it to a dict/list and update the user:
for team in team_list:
# Extract info:
json_notes = json.from_string(team.get("json_notes", {}))
app_notes = json_notes.get("applicantNotes")
# If the app notes are a string, update them:
if isinstance(app_notes, str):
app_notes = json.from_string(app_notes)
json_notes["applicantNotes"] = app_notes
team["json_notes"] = json.to_string(json_notes, no_space = True)
# Make a pandas DataFrame:
team_df = pd.DataFrame(team_list)
# Log out:
tcaoff.logout(tcaoff_creds)
# Show and save the DF:
print(team_df[:10].to_string())
team_df[["user_id", "json_notes"]].to_csv(r"C:\Users\Khushal P Soonderji\Downloads\20260116_cosec_team_fix.csv", index = False)