Skip to content

Commit

Permalink
Merge locale and country
Browse files Browse the repository at this point in the history
This PR contains a major overhaul of the internal structure. The external interface via the bin/holidata script stays the same.

The biggest change is the transfer of the holiday definitions from the locales to the respective country. Before, there had to be a locale class for each locale, defining the holidays partially as class docstring, partially as dedicated functions. For Belgium (BE) the (abridged) locale in French (fr) looked like this:

```
class fr_BE(Locale):
    """
    01-01: [NF] Nouvel An
    05-01: [NF] Fête du Travail
    07-21: [NF] Fête nationale
...
    """
...
```

For Belgium, there were two more locale classes, for languages de and nl. As the holiday data is identical except for the respective language, this means duplication.
Instead of defining the holidays in each locale, they are now defined centrally for all (available) languages in the corresponding country. The definitions for the example Belgium now looks like this:

```
class BE(Country):
    id = "BE"
    languages = ["de", "fr", "nl"]
    easter_type = EASTER_WESTERN

    def __init__(self):
        super().__init__()

        self.define_holiday() \
            .with_names({
                "de": "Neujahr",
                "fr": "Nouvel An",
                "nl": "Nieuwjaar",
            }) \
            .on("01-01") \
            .with_flags("NF")

        self.define_holiday() \
            .with_names({
                "de": "Tag der Arbeit",
                "fr": "Fête du Travail",
                "nl": "Dag van de arbeid",
            }) \
            .on("05-01") \
            .with_flags("NF")
...
```

Instead of docstring and functions, holidays are now defined via a DSL, which allows to specify all required information.

Also, it is now also possible to specify holidays for regions in separate classes, intended to simplify the handling of regional holidays. This has been applied to the holidays of Spain (ES) which is now a subpackage (see src/holidata/holidays/ES/init.py).

Signed-off-by: Thomas Lauf <[email protected]>
  • Loading branch information
lauft authored Nov 21, 2023
2 parents 243207f + c3be9cf commit a8c92de
Show file tree
Hide file tree
Showing 93 changed files with 5,060 additions and 3,452 deletions.
187 changes: 96 additions & 91 deletions ChangeLog
Original file line number Diff line number Diff line change
@@ -1,104 +1,109 @@
- Fix permalinks for holidays in locale de-DE for region BE
- #97 Merge locale and country
- Fix permalinks for holidays in locale de-DE for region BE

2023.11.0
- es-ES: Implement 2024 holidays of Spain
- Drop support for Python 3.7
- Use double quotes instead of single quotes and f-strings
- es-ES: Implement 2024 holidays of Spain
- Drop support for Python 3.7
- Use double quotes instead of single quotes and f-strings

2023.04.0
- en-GB: Add bank holiday for the coronation of King Charles III
- Move creator functions to holidata module
- Print proper error messages for exceptions instead of stack traces
- Fix regex for locale
- en-GB: Add bank holiday for the coronation of King Charles III
- Move creator functions to holidata module
- Print proper error messages for exceptions instead of stack traces
- Fix regex for locale

2022.11.0
- es-ES: Implement 2023 holidays of Spain
- es-ES: Implement 2023 holidays of Spain

2022.9.0
- en-GB: Add Bank Holiday for State Funeral of Queen Elizabeth II
- en-GB: Add Bank Holiday for State Funeral of Queen Elizabeth II

2022.8.2
- Add ability to specify a locale via country and language
- Add ability to specify a locale via country and language

2022.8.0
- #87 Update legal sources for Turkish holidays
- #87 Update legal sources for Turkish holidays

2022.7.0
- Restructured package
- #85 Error in locale en-GB, year 2022
(Thanks to Richard Fieldsend)
- #80 Add locale en-ZA
(Thanks to girtu)
- #83 Update non-working days for 2022 in locale hu-HU
- #81 Add locale et-EE
(Thanks to h0adp0re)
- #78 Add locale for sl-SI
(Thanks to Dejan Dezman)
- #21 Update locale for pt-BR
(Thanks to Adriano Correa)
- #58 Update locale for es-ES
(Thanks to Vicente Jimenez Aguilar)
- #59 Add locale for hu-HU
(Thanks to David Hanak)
- #60 Add locale for tr-TR
(Thanks to farukara)
- #55 Fixed: Wrong assignment of flag `N` in locales en-US and es-US
- #54 Fixed: Wrong assignment of flag `N` in locale de-CH
- #53 Fixed: Regional holidays marked as national in locale de-DE
- #52 Fixed: Non-regional holidays not marked as national in locale nl-NL
- #51 Fixed: Regional holidays marked as national in locale de-AT
- #19 Add locale for sv-SE
(Thanks to Martijn van der Ven)
- #49 Add additional office holidays for en-US/es-US
(Thanks to Brian Burnett)
- #13 Add locale for es-CO
(Thanks to Jorge E. Gómez)
- #44 Add locale fr-CA
(Thanks to Ghyslain Leclerc)
- #29 Add locale en-CA
(Thanks to Ghyslain Leclerc)
- Add locale sv-FI
- #30 Add locale fi-FI
(Thanks to Antti Heinonen)
- #22 Add locale pl-PL
(Thanks to sebacyp)
- #10 Add locale cs-CZ
(Thanks to Jakub Pavlík)
- #40 Add missing holidays in locale de-DE for region BE
- #38 Update croatian holidays according to the law valid for the year 2020 and onwards
(Thanks to Saša Janiška)
- #32 Make project python only
- #25 Add locale for is-IS
(Thanks to Matt Riggott)
- #33 Add locale for pt-PT
(Thanks to lldh)
- Add locale it-IT
- Add locale fr-FR
- Add locale el-GR
- Add locale nl-NL
- Add locale en-NZ
- Add locale es-ES
- Add locale ru-RU
- Add XML emitter
- Add YAML emitter
- Add locale en-GB
- Add locale nb-NO
- Add locale da-DK
- Add locale nl-BE
- Add locale fr-BE
- Add locale de-BE
- Add locale es-US
- Add regions to locale de-AT
- Add locale de-AT
- Add locale de-CH
- Add emitters
- Add locale de-DE
- Replace module 'countries' by subpackage 'holidays'
- Add support for regions
- Add option for output format
- #2 Fix some Croatian holidays
(Thanks to Saša Janiška)
- #1 Add locale hr-HR
(Thanks to Saša Janiška)
- Add locale sk-SK and en-US
- Add Proof-Of-Concept python implementation
- Restructured package
- #85 Error in locale en-GB, year 2022
(Thanks to Richard Fieldsend)
- #80 Add locale en-ZA
(Thanks to girtu)
- #83 Update non-working days for 2022 in locale hu-HU
- #81 Add locale et-EE
(Thanks to h0adp0re)
- #78 Add locale for sl-SI
(Thanks to Dejan Dezman)
- #21 Update locale for pt-BR
(Thanks to Adriano Correa)
- #58 Update locale for es-ES
(Thanks to Vicente Jimenez Aguilar)
- #59 Add locale for hu-HU
(Thanks to David Hanak)
- #60 Add locale for tr-TR
(Thanks to farukara)
- #55 Fixed: Wrong assignment of flag `N` in locales en-US and es-US
- #54 Fixed: Wrong assignment of flag `N` in locale de-CH
- #53 Fixed: Regional holidays marked as national in locale de-DE
- #52 Fixed: Non-regional holidays not marked as national in locale nl-NL
- #51 Fixed: Regional holidays marked as national in locale de-AT
- #19 Add locale for sv-SE
(Thanks to Martijn van der Ven)
- #49 Add additional office holidays for en-US/es-US
(Thanks to Brian Burnett)
- #13 Add locale for es-CO
(Thanks to Jorge E. Gómez)
- #44 Add locale fr-CA
(Thanks to Ghyslain Leclerc)
- #29 Add locale en-CA
(Thanks to Ghyslain Leclerc)
- Add locale sv-FI
- #30 Add locale fi-FI
(Thanks to Antti Heinonen)
- #22 Add locale pl-PL
(Thanks to sebacyp)
- #10 Add locale cs-CZ
(Thanks to Jakub Pavlík)
- #40 Add missing holidays in locale de-DE for region BE
- #38 Update croatian holidays according to the law valid for the year 2020 and onwards
(Thanks to Saša Janiška)
- #32 Make project python only
- #25 Add locale for is-IS
(Thanks to Matt Riggott)
- #33 Add locale for pt-PT
(Thanks to lldh)
- Add locale it-IT
- Add locale fr-FR
- Add locale el-GR
- Add locale nl-NL
- Add locale en-NZ
- Add locale es-ES
- Add locale ru-RU
- Add XML emitter
- Add YAML emitter
- Add locale en-GB
- Add locale nb-NO
- Add locale da-DK
- Add locale nl-BE
- Add locale fr-BE
- Add locale de-BE
- Add locale es-US
- Add regions to locale de-AT
- Add locale de-AT
- Add locale de-CH
- Add emitters
- Add locale de-DE
- Replace module 'countries' by subpackage 'holidays'
- Add support for regions
- Add option for output format
- #2 Fix some Croatian holidays
(Thanks to Saša Janiška)
- #1 Add locale hr-HR
(Thanks to Saša Janiška)
- Add locale sk-SK and en-US
- Add Proof-Of-Concept python implementation

Project started November 8, 2016.

------ start -----------------------------------

4 changes: 2 additions & 2 deletions bin/holidata
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ import sys

from docopt import docopt

from holidata import Holidata
from holidata import for_locale


def parse_locale(locale):
Expand Down Expand Up @@ -65,7 +65,7 @@ if __name__ == "__main__":
# When neither '--locale' nor '--country' are given, docopt prints usage
sys.exit(1)

print(Holidata(country=country_id, language=lang_id, year=args["--year"]).formatted_as(args["--output"]), end="")
print(for_locale(country_id, lang_id).holidays_of(args["--year"]).formatted_as(args["--output"]), end="")

except ValueError as e:
sys.exit(str(e))
56 changes: 29 additions & 27 deletions src/holidata/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
from .holidays import *
from .emitters import Emitter
from .holidays.holidays import LocaleWrapper
from .holidays import *


def get_country_for(identifier):
Expand All @@ -12,15 +11,6 @@ def get_country_for(identifier):
return country_class()


def get_locale_for(identifier):
locale_class = Locale.get(identifier)

if not locale_class:
raise ValueError(f"No plugin found for locale '{identifier}'!")

return locale_class()


def get_emitter_for(identifier):
emitter_class = Emitter.get(identifier)

Expand All @@ -30,31 +20,20 @@ def get_emitter_for(identifier):
return emitter_class()


def create_locale_for(country_id=None, lang_id=None):
def for_locale(country_id, lang_id=None):
country = get_country_for(country_id)
lang_id = country.validate_language_or_get_default(lang_id)

if hasattr(country, "get_holidays_of"):
return LocaleWrapper(country, lang_id)

return get_locale_for(f"{lang_id}-{country_id}")


def parse_year(year):
try:
return int(year)
except ValueError:
raise ValueError(f"Invalid year '{year}'! Has to be an integer.")
return Locale(country, lang_id)


class Holidata:
emitter = None
holidays = None

def __init__(self, country=None, language=None, year=None):
year = parse_year(year)
locale = create_locale_for(country_id=country, lang_id=language)
self.holidays = locale.get_holidays_of(year)
def __init__(self, holidays, emitter=None):
self.holidays = holidays
self.emitter = emitter

def __str__(self):
return self.emitter.output(self.holidays)
Expand All @@ -63,3 +42,26 @@ def formatted_as(self, format_id):
self.emitter = get_emitter_for(format_id)

return self


class Locale:
def __init__(self, country, lang):
self.country = country
self.lang = lang

def get_holidays_of(self, year):
return self.country.get_holidays_of(year, self.lang)

def holidays_of(self, year):
return Holidata(self.country.get_holidays_of(self._parse_year(year), self.lang))

@staticmethod
def _parse_year(year):
try:
return int(year)
except ValueError:
raise ValueError(f"Invalid year '{year}'! Has to be an integer.")

@property
def id(self):
return f"{self.lang}-{self.country.id}"
Loading

0 comments on commit a8c92de

Please sign in to comment.