File: class-options.md

package info (click to toggle)
python-workalendar 17.0.0-1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm
  • size: 1,568 kB
  • sloc: python: 16,500; makefile: 34; sh: 5
file content (108 lines) | stat: -rw-r--r-- 4,592 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# Advanced feature: class options

[Home](index.md) / [Basic usage](basic.md) / [Advanced usage](advanced.md) / [ISO Registry](iso-registry.md) / [iCal Export](ical.md) / [Contributing](contributing.md)


As of `v13.0.0` you can define *options* for your calendar.

## Options as "flags"

Let's consider the following calendar class:

```python
@iso_register('ZK')
class Zhraa(WesternCalendar):
    include_easter_monday = True
    include_labour_day = True
    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
        (8, 2, "King Birthday"),
    )

    def get_variable_days(self, year):
        # usual variable days
        days = super().get_variable_days(year)

        days.append(
            (Zhraa.get_nth_weekday_in_month(year, 6, MON),
            'Day of the Founder'),
        )
        return days
```

In our example, we'll add a special holiday. It's **not** an official holiday, but it **can** be taken as a non-working day, if your organization or your administration decides it becomes one.

For the sake of our use-case, let's say that the Zhraa Kingdom decides that January the 2nd can be considered as a day off in some special cases, for banks or schools, for example. Or at the discretion of your company.

As an integrator, if you need to implement this behaviour, you can decide to have a separate class which includes your extra day. This would fit your needs.

But what if your special organization decides to keep this day as a non-working day depending on a variable context? Let's say that it does not happen every year, or it's not in every city of the Kingdom, or not for all of your employees, etc. Using a dedicated class won't help you there.  
But **optional arguments** surely will!

Here's a slightly different version of our `Zhraa` class:


```python
@iso_register('ZK')
class Zhraa(WesternCalendar):
    include_easter_monday = True
    include_labour_day = True
    FIXED_HOLIDAYS = WesternCalendar.FIXED_HOLIDAYS + (
        (8, 2, "King Birthday"),
    )

    def __init__(self, include_january_2nd=False, **kwargs):
        super().__init__(**kwargs)
        self.include_january_2nd = include_january_2nd

    def get_variable_days(self, year):
        # usual variable days
        days = super().get_variable_days(year)

        days.append(
            (Zhraa.get_nth_weekday_in_month(year, 6, MON),
            'Day of the Founder'),
        )

        if self.include_january_2nd:
            days.append(
                (date(year, 1, 2), "January 2nd")
            )
        return days
```

In your *business-logic code*, you could then write:

```python
include_january_2nd = year == 2019 \
    and (employee_name == "Bob" or employee_location == "North")
calendar = Zhraa(include_january_2nd=include_january_2nd)
```

As a consequence, depending on the context required by your business-logic process, you can include or exclude January 2nd as a holiday.

## More than "flags"

Of course, **you are not limited to booleans** to activate/deactivate a holiday when you want to create options. Use strings, numbers, objects, or whatever suits your needs.

Here are some examples:

* A ``region`` argument: in some regions, it defines new holidays, exceptions on including or excluding other days, etc.
* A `number_of_days_after_new_year` as a variable number of days that are to be accounted as non-working days after New Year's day.
* A ``day_of_the_founder_label`` string variable to easily customize the *Day of the Founder* label, according to your liking.
* ... sky is the limit!

## Caveats

### Why not using derivative classes?

Again, for each of these options, you could define an inherited class of `Zhraa`. But as you have more and more option values and exceptions to your rules, you'd have to define a new class for each of these combinations.

If you only have a couple of exceptions to your rules, you may prefer ot have a dedicated class. If the number of combinations goes over a limit, opt for *options*.

### ERR_TOO_MANY_OPTIONS

**Beware!** Options are nice and will help you in many cases, but it's probably a bad idea to go on and add dozens of them to your class constructor. As a consequence, your runtime code would be more and more complex. You'd have hard time covering all of the combinations around their values and test them.

As in many other cases, your mileage may vary, but I doubt that you want to combine more than 5 of them.

[Home](index.md) / [Basic usage](basic.md) / [Advanced usage](advanced.md) / [ISO Registry](iso-registry.md) / [iCal Export](ical.md) / [Contributing](contributing.md)