Skip to content

Commit

Permalink
fixup! WIP: Write implementation
Browse files Browse the repository at this point in the history
Signed-off-by: Carmen Bianca BAKKER <[email protected]>
  • Loading branch information
carmenbianca committed Aug 30, 2024
1 parent 75568a3 commit c5f2031
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 152 deletions.
1 change: 0 additions & 1 deletion resource_multi_week_calendar/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,3 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

from . import models
from .hooks import post_load_hook
1 change: 0 additions & 1 deletion resource_multi_week_calendar/__manifest__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,4 @@
"data": [
"views/resource_calendar_views.xml",
],
"post_load": "post_load_hook",
}
135 changes: 0 additions & 135 deletions resource_multi_week_calendar/hooks.py

This file was deleted.

57 changes: 42 additions & 15 deletions resource_multi_week_calendar/models/resource_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
# SPDX-License-Identifier: AGPL-3.0-or-later

import math
from datetime import timedelta
from datetime import datetime, timedelta

from odoo import _, api, fields, models
from odoo.exceptions import ValidationError
Expand Down Expand Up @@ -130,6 +130,8 @@ def _get_week_number(self, day=None):
self.ensure_one()
if day is None:
day = fields.Date.today()
if isinstance(day, datetime):
day = day.date()
family_size = len(self.family_calendar_ids)
weeks_since_epoch = math.floor(
(day - self._get_first_day_of_epoch_week()).days / 7
Expand Down Expand Up @@ -231,6 +233,27 @@ def _check_epoch_date_matches_parent(self):
% calendar.name
)

@api.model
def _split_into_weeks(self, start_dt, end_dt):
current_start = start_dt

while current_start <= end_dt:
# Calculate the end of the week (Sunday, 23:59:59)
days_until_sunday = 6 - current_start.weekday()
week_end = current_start + timedelta(days=days_until_sunday)
week_end = week_end.replace(
hour=23, minute=59, second=59, microsecond=999999
)

current_end = min(week_end, end_dt)
yield (current_start, current_end)

# Move to the next week (start of next Monday)
current_start = current_end + timedelta(days=1)
current_start = current_start.replace(
hour=0, minute=0, second=0, microsecond=0
)

def _attendance_intervals_batch(
self, start_dt, end_dt, resources=None, domain=None, tz=None
):
Expand All @@ -239,37 +262,41 @@ def _attendance_intervals_batch(
return super()._attendance_intervals_batch(

Check warning on line 262 in resource_multi_week_calendar/models/resource_calendar.py

View check run for this annotation

Codecov / codecov/patch

resource_multi_week_calendar/models/resource_calendar.py#L262

Added line #L262 was not covered by tests
start_dt, end_dt, resources=resources, domain=domain, tz=tz
)

if self.parent_calendar_id:
return self.parent_calendar_id._attendance_intervals_batch(
start_dt, end_dt, resources=resources, domain=domain, tz=tz
)
calendars = self | self.child_calendar_ids
calendars_by_week = {
calendar.week_number: calendar
for calendar in self | self.child_calendar_ids
}
results = []
for calendar in calendars:

# Calculate each week separately, choosing the correct calendar for each
# week.
for week_start, week_end in self._split_into_weeks(start_dt, end_dt):
results.append(
super(
ResourceCalendar,
# This context isn't used here, but could be used by
# dependencies to prevent loops.
calendar.with_context(recursive_multi_week=True),
calendars_by_week[self._get_week_number(week_start)].with_context(
# This context is not used here, but could possibly be
# used by other modules that use this module. I am not
# sure how useful it is.
recursive_multi_week=True
),
)._attendance_intervals_batch(
start_dt, end_dt, resources=resources, domain=domain, tz=tz
week_start, week_end, resources=resources, domain=domain, tz=tz
)
)

# Aggregate the results from each week.
result = {}
for item in results:
for resource, intervals in item.items():
if resource not in result:
result[resource] = intervals
else:
result[resource] += intervals
result[resource] |= intervals

return result

# See hooks.py for where this is used.
def _day_in_calendar(self, day):
self.ensure_one()
if not self.is_multi_week:
return super()._day_in_calendar(day)
return self._get_week_number(day) == self.week_number
88 changes: 88 additions & 0 deletions resource_multi_week_calendar/tests/test_calendar.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
from freezegun import freeze_time

from odoo.exceptions import ValidationError
from odoo.fields import Command
from odoo.tests.common import TransactionCase


Expand Down Expand Up @@ -236,3 +237,90 @@ def test_compute_current_week_when_day_changes(self):
child._compute_current_week()
self.assertEqual(child.current_week_number, 2)
self.assertEqual(child.current_calendar_id, child)


class TestMultiCalendar(CalendarCase):
def setUp(self):
super().setUpClass()
# The parent calendar has attendances by default: Every weekday from 8
# to 12, and 13 to 17.
self.child_calendar = self.create_simple_child()
# In the child calendar, only work the mornings.
self.child_calendar.attendance_ids = False
self.child_calendar.attendance_ids = [
Command.create(
{
"name": "Monday Morning",
"dayofweek": "0",
"hour_from": 8,
"hour_to": 12,
"day_period": "morning",
}
),
Command.create(
{
"name": "Tuesday Morning",
"dayofweek": "1",
"hour_from": 8,
"hour_to": 12,
"day_period": "morning",
}
),
Command.create(
{
"name": "Wednesday Morning",
"dayofweek": "2",
"hour_from": 8,
"hour_to": 12,
"day_period": "morning",
}
),
Command.create(
{
"name": "Thursday Morning",
"dayofweek": "3",
"hour_from": 8,
"hour_to": 12,
"day_period": "morning",
}
),
Command.create(
{
"name": "Friday Morning",
"dayofweek": "4",
"hour_from": 8,
"hour_to": 12,
"day_period": "morning",
}
),
]

def test_count_work_hours_two_weeks(self):
hours = self.parent_calendar.get_work_hours_count(
# 1st of July is a Monday.
datetime.datetime.fromisoformat("2024-07-01T00:00:00+00:00"),
datetime.datetime.fromisoformat("2024-07-14T23:59:59+00:00"),
)
# 40 from the parent, 20 from the child
self.assertEqual(hours, 60)

def test_count_work_hours_from_child(self):
# It doesn't matter whether you call the method from the child.
hours = self.child_calendar.get_work_hours_count(
datetime.datetime.fromisoformat("2024-07-01T00:00:00+00:00"),
datetime.datetime.fromisoformat("2024-07-14T23:59:59+00:00"),
)
self.assertEqual(hours, 60)

def test_count_work_hours_weeks_separately(self):
self.parent_calendar.multi_week_epoch_date = "2024-07-01"
hours = self.parent_calendar.get_work_hours_count(
datetime.datetime.fromisoformat("2024-07-01T00:00:00+00:00"),
datetime.datetime.fromisoformat("2024-07-07T23:59:59+00:00"),
)
self.assertEqual(hours, 40)
hours = self.parent_calendar.get_work_hours_count(
datetime.datetime.fromisoformat("2024-07-08T00:00:00+00:00"),
datetime.datetime.fromisoformat("2024-07-14T23:59:59+00:00"),
)
self.assertEqual(hours, 20)

0 comments on commit c5f2031

Please sign in to comment.