2. adding rules

Most companies just follow the law when it comes to paying bonuses for work on weekends. Libestg3b comes with a standard set of rules to cover that use case. It is, howerver, also possible to extend the Rules to match your situation. This enables you to define special situations like “12-12 is our annual christmas party, pay 100% extra for any shifts during that”.

2.1. Defining it

Rules are implemented using Rule objects. They contain all neccesary information like the rules name, when to apply it and what do to, when it does.

from decimal import Decimal
from libestg3b.rule import Rule

r = Rule(
    "CHRISTMAS_PARTY",
    "Annual company christmas party",
    lambda minute, start, holidays: minute.month == 12 and minute.day == 12,
    multiply=Decimal(1),
)

We’ve got a rule! Most of this should be pretty easy to understand, but the lambda in there can raise some questions, so let’s go over it: The function you pass into the constructor is the actual implementation of your rule. It defines when to apply it. To make sure it’s able to do its job, a couple of data points are passed into it:

  • minute (datetime)

Also refer to libestg3b.rule.Rule.match() for further information and examples.

2.1.1. Boiling it down

Writing a lambda function for each rule you want to write is fun, but generates a lot of boilerplate code. Since many rules are quite repetitive (“match on some date”, “match after some time”, …), libestg3b comes with a couple of helpers to save you some time:

  • DayRule: match on a given month/day combination (e.g. YYYY-03-28)
  • DayTimeRule: like DayRule, but also require the shift to be after a certain time (e.g. 14:00).

Documentation and examples on these classes can be found in the respective class docs (follow the link, alice!). To clear things up a bit, have a look at the following example on how to shorten our CHRISTMAS_PARTY rule using the DayRule class:

from decimal import Decimal
from libestg3b.rule import DayRule

r = DayRule("CHRISTMAS_PARTY", 12, 12, multiply=Decimal(1))

Except for the imports, we can now even fit it into one line without feeling bad.

2.2. Plugging it in

Feel floating rule objects are great, but they don’t do much. To convince the library to actually use your rules to match shifts, we need to tell it about them:

from decimal import Decimal
from libestg3b import EStG3b
from libestg3b.rule import DayRule, RuleGroup

est = EStG3b("DE")(add_rules=[
    RuleGroup(
        "GRP_CUSTOM",
        "Rules special to our company",
        [DayRule("CHRISTMAS_PARTY", 12, 12, multiply=Decimal(1))],
    )
])

You’ll quickly notice a new thing here: Groups.

A libestg3b.rule.RuleGroup is a set of rules of which only one may ever match. A pratical example of why this might be useful is outlined in German law: there is night work (+25%), work on sundays (+50%) and work on holidays (+125%). While work during sunday nights allows combining the rules to yield +175%, work on sundays, which happen to be a holiday, only allows one of the rules to be applied, resulting in +125%. In case two or more rules match, group chooses the one with the highest bonus and discards all other matches.

Since all rules need to be in a group, we just make up a new one (GRP_CUSTOM) with nothing in it except for our special rule. This allows it to be matched in addition to any other rules already predefined by law.

2.3. Running it

We’ve got a rule, we’ve told the library about it, let’s see, if it actually works. Make up a shift from 12-12 19:00 until 01:00 the next day, plug it into calculate_shift as outlined in the first guide and run it:

...

import datetime as DT

m = est.calculate_shift([DT.datetime(2018, 12, 12, 19), DT.datetime(2018, 12, 13, 1)])
print(m)
[
    <Match 2018-12-12T19:00~2018-12-12T20:00, CHRISTMAS_PARTY, add=0, multiply=1>,
    <Match 2018-12-12T20:00~2018-12-13T00:00, CHRISTMAS_PARTY+DE_NIGHT, add=0, multiply=1.25>,
    <Match 2018-12-13T00:00~2018-12-13T01:00, DE_NIGHT_START_YESTERDAY, add=0, multiply=0.4>
]

As you can see, our rule worked just as intended. In addition to the predefined DE_NIGHT rules, there is now also a match for CHRISTMAS_PARTY during the relevant times.

Happy matching!