Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 6 additions & 56 deletions src/woffu_client/woffu_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -440,29 +440,10 @@ def get_status(
current_time = datetime.now(tz=self._localzone)

for sign in signs_in_day:
# running_clock = sign['SignIn']
# sign_date = sign['TrueDate']
# # Split UtcTime and keep only the offset
# utc_offset = f"{sign['UtcTime'].split(' ')[1].zfill(3)}00"

running_clock = sign.get("SignIn", False)
sign_date = sign.get("TrueDate", None)
utc_time = sign.get("UtcTime", "")
try:
# Split UtcTime and keep only the offset (format '+HHMM')
utc_offset = f"{utc_time.split(' ')[1].zfill(3)}00"
except (IndexError, AttributeError):
utc_offset = UTC_OFFSET # fallback to UTC

# WORKAROUND: Woffu stores incorrect UTC information
# when signing in from the Webapp, showing same local date
# on 'TrueDate' and 'UtcTime' but with no UTC offset (+0).
# - For the time being, we'll be using our local timezone
# for any sign that comes with +0 in the UtcTime as we
# suspect Woffu DB is running on a server with the same
# local timezone as us.
if utc_offset == UTC_OFFSET:
utc_offset = current_time.strftime("%z")
utc_offset = current_time.strftime("%z")

sign_date_timezoned = f"{sign_date}{utc_offset}"

Expand Down Expand Up @@ -529,15 +510,6 @@ def sign(self, type: str = "") -> HTTPResponse | None:

# Send sign request
logger.info("Sending sign request...")
# # Get the current datetime with local timezone
# current_time = datetime.now(tz=self._localzone)

# # Get the actual time offset in minutes
# utc_offset = current_time.utcoffset()
# if utc_offset:
# timezone_offset = -(int(utc_offset.total_seconds() / 60))
# else:
# timezone_offset = 0

return self.post(
url=f"https://{self._domain}/api/svc/signs/signs",
Expand Down Expand Up @@ -660,40 +632,18 @@ def _get_slot_hours(self, slot: dict, date: str) -> float:

def _calculate_hours_from_in_out(self, slot: dict) -> float:
"""Calculate hours from 'in' and 'out' keys of a slot."""
in_utc_parts = slot["in"]["utcTime"].split(" ")
out_utc_parts = slot["out"]["utcTime"].split(" ")
if len(in_utc_parts) < 2 or len(out_utc_parts) < 2:
logger.warning(
f"Skipping slot with invalid UTC times: "
f"in='{slot['in']['utcTime']}'"
f"out='{slot['out']['utcTime']}'",
)
return 0.0
in_dt = self._parse_datetime(slot["in"]["trueDate"])
out_dt = self._parse_datetime(slot["out"]["trueDate"])

in_dt = self._parse_datetime(slot["in"]["trueDate"], in_utc_parts[1])
out_dt = self._parse_datetime(
slot["out"]["trueDate"], out_utc_parts[1],
)
return (out_dt - in_dt).total_seconds() / 3600

def _parse_datetime(self, date_str: str, utc_offset: str) -> datetime:
def _parse_datetime(self, date_str: str) -> datetime:
"""Parse date string and UTC offset safely into datetime."""
# Prepare current time with local timezone to use in case
# UTC offsets are wrong in Woffu.
# Prepare current time with local timezone
# - We do it this way to take into account
# Daylight Saving Timezones (CET +1, CEST +2, for example).
current_time = datetime.now(tz=self._localzone)

# WORKAROUND: Woffu stores incorrect UTC information
# when signing in from the Webapp, showing same local date
# on 'TrueDate' and 'UtcTime' but with no UTC offset (+0).
# - For the time being, we'll be using our local timezone
# for any sign that comes with +0 in the UtcTime as we
# suspect Woffu DB is running on a server with the same
# local timezone as us.
utc_offset = f"{utc_offset.zfill(3)}00"
if utc_offset == UTC_OFFSET:
utc_offset = current_time.strftime("%z")
utc_offset = current_time.strftime("%z")

date_timezoned = f"{date_str}{utc_offset}"
try:
Expand Down
55 changes: 20 additions & 35 deletions tests/test_woffu_api_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1106,26 +1106,26 @@ def test_sign_user_already_signed_returns_none(
self.assertIsNone(result)
mock_post.assert_not_called()

@patch.object(WoffuAPIClient, "get")
def test_get_status_multiple_invalid_utc_formats(self, mock_get):
"""get_status handles multiple invalid UtcTime formats."""
mock_get.return_value.status = 200
mock_get.return_value.json.return_value = [
{
"SignIn": True,
"TrueDate": "2025-09-12T12:00:00.000",
"UtcTime": "BAD1",
},
{
"SignIn": False,
"TrueDate": "2025-09-12T16:00:00.000",
"UtcTime": "BAD2",
},
]

total, running = self.client.get_status()
self.assertIsInstance(total, object)
self.assertFalse(running)
# @patch.object(WoffuAPIClient, "get")
# def test_get_status_multiple_invalid_utc_formats(self, mock_get):
# """get_status handles multiple invalid UtcTime formats."""
# mock_get.return_value.status = 200
# mock_get.return_value.json.return_value = [
# {
# "SignIn": True,
# "TrueDate": "2025-09-12T12:00:00.000",
# "UtcTime": "BAD1",
# },
# {
# "SignIn": False,
# "TrueDate": "2025-09-12T16:00:00.000",
# "UtcTime": "BAD2",
# },
# ]

# total, running = self.client.get_status()
# self.assertIsInstance(total, object)
# self.assertFalse(running)

@patch.object(WoffuAPIClient, "get")
def test_get_status_only_running_clock_last_sign_false(self, mock_get):
Expand All @@ -1146,21 +1146,6 @@ def test_get_status_only_running_clock_last_sign_false(self, mock_get):
_, running = self.client.get_status(only_running_clock=True)
self.assertFalse(running)

@patch.object(WoffuAPIClient, "get")
def test_get_status_invalid_utc_fallback_local(self, mock_get):
"""Test invalid UtcTime parsing."""
mock_get.return_value.status = 200
mock_get.return_value.json.return_value = [
{
"SignIn": True,
"TrueDate": "2025-09-12T12:00:00.000",
"UtcTime": "INVALID",
},
]
total, running = self.client.get_status()
self.assertTrue(running)
self.assertIsInstance(total, timedelta)

@patch.object(WoffuAPIClient, "get")
def test_get_status_invalid_utc_formats_mixed(self, mock_get):
"""Test several invalid UtcTime parsing."""
Expand Down