-
Notifications
You must be signed in to change notification settings - Fork 13
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Refactor Region, Country, HolidayGenerator Optimize emitters, utils Use @DataClass for 'Holiday' Add type hints Replace date parsing with DSL Replace custom date functions with DSL Rearrange package structure Signed-off-by: Thomas Lauf <[email protected]>
- Loading branch information
Showing
73 changed files
with
1,314 additions
and
1,310 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,67 +1,67 @@ | ||
from .emitters import Emitter | ||
from .holidays import * | ||
from typing import Iterator | ||
|
||
|
||
def get_country_for(identifier): | ||
country_class = Country.get(identifier) | ||
|
||
if not country_class: | ||
raise ValueError(f"No country found for id '{identifier}'!") | ||
|
||
return country_class() | ||
|
||
|
||
def get_emitter_for(identifier): | ||
emitter_class = Emitter.get(identifier) | ||
|
||
if not emitter_class: | ||
raise ValueError(f"Unsupported output format '{identifier}'!") | ||
|
||
return emitter_class() | ||
|
||
|
||
def for_locale(country_id, lang_id=None): | ||
country = get_country_for(country_id) | ||
lang_id = country.validate_language_or_get_default(lang_id) | ||
|
||
return Locale(country, lang_id) | ||
from holidata.emitters import Emitter | ||
from holidata.holiday import Country, Holiday | ||
from holidata.holidays import * | ||
|
||
|
||
class Holidata: | ||
emitter = None | ||
holidays = None | ||
|
||
def __init__(self, holidays, emitter=None): | ||
def __init__(self, holidays, emitter: Emitter = None): | ||
self.holidays = holidays | ||
self.emitter = emitter | ||
|
||
def __str__(self): | ||
def __str__(self) -> str: | ||
return self.emitter.output(self.holidays) | ||
|
||
def formatted_as(self, format_id): | ||
def formatted_as(self, format_id: str) -> 'Holidata': | ||
self.emitter = get_emitter_for(format_id) | ||
|
||
return self | ||
|
||
|
||
class Locale: | ||
def __init__(self, country, lang): | ||
def __init__(self, country: Country, lang: str): | ||
self.country = country | ||
self.lang = lang | ||
|
||
def get_holidays_of(self, year): | ||
def get_holidays_of(self, year: int) -> Iterator[Holiday]: | ||
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)) | ||
def holidays_of(self, year: str) -> Holidata: | ||
return Holidata(self.country.get_holidays_of(Locale._parse_year(year), self.lang)) | ||
|
||
@staticmethod | ||
def _parse_year(year): | ||
def _parse_year(year: str) -> int: | ||
try: | ||
return int(year) | ||
except ValueError: | ||
raise ValueError(f"Invalid year '{year}'! Has to be an integer.") | ||
|
||
@property | ||
def id(self): | ||
def id(self) -> str: | ||
return f"{self.lang}-{self.country.id}" | ||
|
||
|
||
def get_country_for(identifier: str) -> Country: | ||
country_class = Country.get(identifier) | ||
|
||
if not country_class: | ||
raise ValueError(f"No country found for id '{identifier}'!") | ||
|
||
return country_class() | ||
|
||
|
||
def get_emitter_for(identifier: str) -> Emitter: | ||
emitter_class = Emitter.get(identifier) | ||
|
||
if not emitter_class: | ||
raise ValueError(f"Unsupported output format '{identifier}'!") | ||
|
||
return emitter_class() | ||
|
||
|
||
def for_locale(country_id: str, lang_id: str = None) -> Locale: | ||
country = get_country_for(country_id) | ||
lang_id = country.validate_language_or_get_default(lang_id) | ||
|
||
return Locale(country, lang_id) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,94 +1,108 @@ | ||
import csv | ||
import io | ||
import json | ||
from typing import List, Dict, Any, Callable | ||
|
||
from holidata.holiday import Holiday | ||
from holidata.plugin import PluginMount | ||
|
||
|
||
class Emitter(metaclass=PluginMount): | ||
type = None | ||
type: str = None | ||
|
||
def __init__(self): | ||
if self.type is None: | ||
raise ValueError(f"Emitter {self.__class__.__name__} does not provide its type!") | ||
|
||
@staticmethod | ||
def get(identifier): | ||
def get(identifier: str) -> Callable[[], 'Emitter']: | ||
return Emitter.get_plugin(identifier, "type") | ||
|
||
def output(self, holidays): | ||
pass | ||
def output(self, holidays: List[Holiday]) -> str: | ||
raise NotImplementedError | ||
|
||
|
||
class JsonEmitter(Emitter): | ||
type = "json" | ||
type: str = "json" | ||
|
||
def output(self, holidays): | ||
export_data = [h.as_dict() for h in holidays] | ||
def output(self, holidays: List[Holiday]) -> str: | ||
export_data: List[Dict[str, Any]] = [h.as_dict() for h in holidays] | ||
export_data.sort(key=lambda x: (x["date"], x["description"], x["region"])) | ||
return "\n".join([json.dumps(h, ensure_ascii=False, sort_keys=False, indent=None, separators=(",", ":")) for h in export_data]) + "\n" | ||
return json.dumps(export_data, ensure_ascii=False, sort_keys=False, indent=None, separators=(",", ":")) + "\n" | ||
|
||
|
||
class CsvEmitter(Emitter): | ||
type = "csv" | ||
type: str = "csv" | ||
|
||
def output(self, holidays): | ||
export_data = [h.as_dict() for h in holidays] | ||
def output(self, holidays: List[Holiday]) -> str: | ||
export_data: List[Dict[str, Any]] = [h.as_dict() for h in holidays] | ||
export_data.sort(key=lambda x: (x["date"], x["description"], x["region"])) | ||
result = io.StringIO() | ||
result: io.StringIO = io.StringIO() | ||
|
||
writer = csv.DictWriter(result, | ||
["locale", "region", "date", "description", "type", "notes"], | ||
quoting=csv.QUOTE_ALL, | ||
lineterminator="\n") | ||
writer: csv.DictWriter = csv.DictWriter( | ||
result, | ||
["locale", "region", "date", "description", "type", "notes"], | ||
quoting=csv.QUOTE_ALL, | ||
lineterminator="\n") | ||
writer.writeheader() | ||
writer.writerows(export_data) | ||
|
||
return result.getvalue() | ||
|
||
|
||
class YamlEmitter(Emitter): | ||
type = "yaml" | ||
type: str = "yaml" | ||
|
||
def output(self, holidays): | ||
export_data = [h.as_dict() for h in holidays] | ||
@staticmethod | ||
def _format_yaml(holiday: Dict[str, Any]) -> str: | ||
output: str = " holiday:\n" | ||
for key in ["locale", "region", "date", "description", "type", "notes"]: | ||
value: Any = holiday[key] | ||
|
||
if value is not None and value != "": | ||
output += f" {key}: {value}\n" | ||
else: | ||
output += f" {key}:\n" | ||
|
||
return output | ||
|
||
def output(self, holidays: List[Holiday]) -> str: | ||
export_data: List[Dict[str, Any]] = [h.as_dict() for h in holidays] | ||
export_data.sort(key=lambda x: (x["date"], x["description"], x["region"])) | ||
|
||
output = "%YAML 1.1\n" | ||
output: str = "%YAML 1.1\n" | ||
output += "---\n" | ||
for holiday in export_data: | ||
output += " holiday:\n" | ||
|
||
for key in ["locale", "region", "date", "description", "type", "notes"]: | ||
value = holiday[key] | ||
|
||
if value is not None and value != "": | ||
output += f" {key}: {value}\n" | ||
else: | ||
output += f" {key}:\n" | ||
for holiday in export_data: | ||
output += YamlEmitter._format_yaml(holiday) | ||
|
||
output += "...\n" | ||
return output | ||
|
||
|
||
class XmlEmitter(Emitter): | ||
type = "xml" | ||
type: str = "xml" | ||
|
||
def output(self, holidays): | ||
export_data = [h.as_dict() for h in holidays] | ||
@staticmethod | ||
def _format_xml(holiday: Dict[str, Any]) -> str: | ||
output: str = " <holiday>\n" | ||
|
||
for key in ["locale", "region", "date", "description", "type", "notes"]: | ||
value: Any = holiday[key] if key in holiday else "" | ||
output += f" <{key}>{value if value is not None else ''}</{key}>\n" | ||
|
||
output += " </holiday>\n" | ||
return output | ||
|
||
def output(self, holidays: List[Holiday]) -> str: | ||
export_data: List[Dict[str, Any]] = [h.as_dict() for h in holidays] | ||
export_data.sort(key=lambda x: (x["date"], x["description"], x["region"])) | ||
|
||
output = "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n" | ||
output: str = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | ||
output += "<holidays>\n" | ||
|
||
for holiday in export_data: | ||
output += " <holiday>\n" | ||
|
||
for key in ["locale", "region", "date", "description", "type", "notes"]: | ||
value = holiday[key] if key in holiday else "" | ||
output += f" <{key}>{value if value is not None else ''}</{key}>\n" | ||
|
||
output += " </holiday>\n" | ||
output += XmlEmitter._format_xml(holiday) | ||
|
||
output += "</holidays>\n" | ||
return output |
Oops, something went wrong.