├── .nvmrc
├── .python-version
├── ocma_data
├── __init__.py
├── core
│ ├── date
│ │ ├── __init__.py
│ │ ├── logic.py
│ │ └── __main__.py
│ ├── fasting
│ │ ├── __init__.py
│ │ ├── __main__.py
│ │ ├── constants.py
│ │ └── logic.py
│ ├── pascha
│ │ ├── __init__.py
│ │ ├── logic.py
│ │ └── __main__.py
│ ├── weekday
│ │ ├── __init__.py
│ │ ├── logic.py
│ │ └── __main__.py
│ ├── lectionary
│ │ ├── __init__.py
│ │ ├── constants.py
│ │ ├── logic.py
│ │ └── __main__.py
│ ├── moon_phase
│ │ ├── __init__.py
│ │ ├── logic.py
│ │ └── __main__.py
│ └── pascha_distance
│ │ ├── __init__.py
│ │ ├── logic.py
│ │ └── __main__.py
├── __main__.py
├── constants.py
├── cli
│ ├── constants.py
│ └── main.py
└── utils
│ └── date_utils.py
├── .prettierignore
├── package.json
├── .gitignore
├── .github
└── workflows
│ ├── ruff.yml
│ ├── prettier.yml
│ ├── pytest.yml
│ └── deploy.yml
├── tests
└── core
│ ├── test_pascha.py
│ ├── test_moon_phase.py
│ ├── test_weekday.py
│ ├── test_date.py
│ ├── test_pascha_distance.py
│ ├── test_fasting_season_03_new.py
│ ├── test_fasting_season_03_old.py
│ ├── test_fasting_season_11_new.py
│ ├── test_fasting_season_11_old.py
│ ├── test_fasting_season_05_new.py
│ ├── test_fasting_season_09_new.py
│ ├── test_fasting_season_09_old.py
│ ├── test_fasting_season_02_old.py
│ ├── test_fasting_season_02_new.py
│ ├── test_fasting_season_01_new.py
│ ├── test_fasting_season_01_old.py
│ ├── test_fasting_season_06_new.py
│ ├── test_fasting_season_06_old.py
│ ├── test_fasting_season_07_new.py
│ ├── test_fasting_season_07_old.py
│ ├── test_fasting_season_05_old.py
│ ├── test_fasting_season_08_new.py
│ ├── test_fasting_season_04_old.py
│ ├── test_fasting_season_04_new.py
│ ├── test_fasting_season_08_old.py
│ ├── test_fasting_season_10_new.py
│ └── test_fasting_season_10_old.py
├── LICENSE
├── pyproject.toml
├── README.md
├── public
├── index.html
└── i18n
│ ├── en.json
│ ├── ru.json
│ ├── ro.json
│ └── el.json
└── uv.lock
/.nvmrc:
--------------------------------------------------------------------------------
1 | v24.11.1
2 |
--------------------------------------------------------------------------------
/.python-version:
--------------------------------------------------------------------------------
1 | 3.11
2 |
--------------------------------------------------------------------------------
/ocma_data/__init__.py:
--------------------------------------------------------------------------------
1 | """Package initialization file."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/date/__init__.py:
--------------------------------------------------------------------------------
1 | """Package initialization file."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/fasting/__init__.py:
--------------------------------------------------------------------------------
1 | """Package initialization file."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/pascha/__init__.py:
--------------------------------------------------------------------------------
1 | """Package initialization file."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/weekday/__init__.py:
--------------------------------------------------------------------------------
1 | """Package initialization file."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/lectionary/__init__.py:
--------------------------------------------------------------------------------
1 | """Package initialization file."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/moon_phase/__init__.py:
--------------------------------------------------------------------------------
1 | """Package initialization file."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/pascha_distance/__init__.py:
--------------------------------------------------------------------------------
1 | """Package initialization file."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/lectionary/constants.py:
--------------------------------------------------------------------------------
1 | """Constants defining Sunday Lectionary."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/lectionary/logic.py:
--------------------------------------------------------------------------------
1 | """Module for calculating Sunday Lectionary."""
2 |
--------------------------------------------------------------------------------
/ocma_data/core/lectionary/__main__.py:
--------------------------------------------------------------------------------
1 | """Subpackage for Sunday lectionary calculations."""
2 |
--------------------------------------------------------------------------------
/ocma_data/__main__.py:
--------------------------------------------------------------------------------
1 | """Main entry point for running the package as a CLI."""
2 |
3 | from ocma_data.cli.main import main
4 |
5 | if __name__ == "__main__":
6 | main()
7 |
--------------------------------------------------------------------------------
/.prettierignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | public/data/
3 | .git/
4 | .pytest_cache/
5 | .ruff_cache/
6 | .venv/
7 | LICENSE
8 | package-lock.json
9 | .gitignore
10 | .nvmrc
11 | .prettierignore
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "scripts": {
3 | "format": "prettier --check ."
4 | },
5 | "type": "module",
6 | "devDependencies": {
7 | "prettier": "^3.7.3"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Python-generated files
2 | __pycache__/
3 | *.py[oc]
4 | build/
5 | dist/
6 | wheels/
7 | *.egg-info
8 | ._*
9 | .ruff_cache/
10 | utils/__pycache__/
11 |
12 | # Virtual environments
13 | .venv
14 |
15 | .pytest_cache
16 | public/data/
17 | node_modules/
--------------------------------------------------------------------------------
/.github/workflows/ruff.yml:
--------------------------------------------------------------------------------
1 | name: Ruff
2 |
3 | on:
4 | pull_request:
5 | branches: [main]
6 |
7 | jobs:
8 | ruff:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v6
13 |
14 | - uses: astral-sh/ruff-action@v3
15 | with:
16 | args: check
17 |
18 | - uses: astral-sh/ruff-action@v3
19 | with:
20 | args: format --check
21 |
--------------------------------------------------------------------------------
/ocma_data/constants.py:
--------------------------------------------------------------------------------
1 | """Global constants."""
2 |
3 | from enum import IntEnum, StrEnum
4 |
5 |
6 | class CalendarStyles(StrEnum):
7 | """Enumeration for Calendar Styles."""
8 |
9 | OLD_CALENDAR = "old"
10 | NEW_CALENDAR = "new"
11 |
12 |
13 | class Weekdays(IntEnum):
14 | """Enumeration for Weekdays."""
15 |
16 | MON = 1
17 | TUE = 2
18 | WED = 3
19 | THU = 4
20 | FRI = 5
21 | SAT = 6
22 | SUN = 7
23 |
--------------------------------------------------------------------------------
/.github/workflows/prettier.yml:
--------------------------------------------------------------------------------
1 | name: Prettier
2 |
3 | on:
4 | pull_request:
5 | branches: [main]
6 |
7 | jobs:
8 | prettier:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v6
13 |
14 | - uses: actions/setup-node@v6
15 | with:
16 | node-version: 24
17 | cache: "npm"
18 |
19 | - run: npm ci
20 |
21 | - name: Run Prettier check
22 | run: npm run format
23 |
--------------------------------------------------------------------------------
/.github/workflows/pytest.yml:
--------------------------------------------------------------------------------
1 | name: Pytest
2 |
3 | on:
4 | pull_request:
5 | branches: [main]
6 |
7 | jobs:
8 | test:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - uses: actions/checkout@v6
13 |
14 | - name: Install uv
15 | uses: astral-sh/setup-uv@v7
16 |
17 | - name: Set up Python
18 | uses: actions/setup-python@v6
19 | with:
20 | python-version-file: "pyproject.toml"
21 |
22 | - name: Install the project
23 | run: uv sync --locked --all-extras --dev
24 |
25 | - name: Run tests
26 | run: uv run pytest
27 |
--------------------------------------------------------------------------------
/ocma_data/core/pascha_distance/logic.py:
--------------------------------------------------------------------------------
1 | """Module providing a function that calculate the moon phase for a given date."""
2 |
3 | from datetime import date
4 |
5 | from ocma_data.core.pascha.logic import calculate_pascha
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 |
9 | def pascha_distance(current_date: date, calendar_style: str) -> str:
10 | """Calculate the number of days between a given date and Pascha."""
11 | pascha_date = string_to_date(calculate_pascha(current_date.year, calendar_style))
12 | delta_date = current_date - pascha_date
13 |
14 | return str(delta_date.days)
15 |
--------------------------------------------------------------------------------
/ocma_data/core/weekday/logic.py:
--------------------------------------------------------------------------------
1 | """Module providing a function that calculate the moon phase for a given date."""
2 |
3 | from datetime import date
4 |
5 | from ocma_data.constants import CalendarStyles
6 | from ocma_data.utils.date_utils import modify_date
7 |
8 |
9 | def get_weekday(current_date: date, calendar_style: str) -> str:
10 | """Get the weekday (1-based index) for a given date."""
11 | if calendar_style == CalendarStyles.OLD_CALENDAR.value:
12 | return str(modify_date(current_date, 13).isoweekday())
13 |
14 | if calendar_style == CalendarStyles.NEW_CALENDAR.value:
15 | return str(current_date.isoweekday())
16 |
17 | return ""
18 |
--------------------------------------------------------------------------------
/tests/core/test_pascha.py:
--------------------------------------------------------------------------------
1 | """Test for pascha subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.pascha.logic import calculate_pascha
6 |
7 | test_cases = [
8 | (1923, "old", "1923-3-26"),
9 | (1923, "new", "1923-4-8"),
10 | (1924, "old", "1924-4-14"),
11 | (1924, "new", "1924-4-27"),
12 | (2010, "old", "2010-3-22"),
13 | (2010, "new", "2010-4-4"),
14 | (2025, "old", "2025-4-7"),
15 | (2025, "new", "2025-4-20"),
16 | (2078, "old", "2078-4-25"),
17 | (2078, "new", "2078-5-8"),
18 | (2099, "old", "2099-3-30"),
19 | (2099, "new", "2099-4-12"),
20 | ]
21 |
22 |
23 | @pytest.mark.parametrize("current_year, calendar_style, expected", test_cases)
24 | def test_calculate_pascha(current_year: int, calendar_style: str, expected: str) -> None:
25 | """Test for calculate_pascha."""
26 | assert calculate_pascha(current_year, calendar_style) == expected
27 |
--------------------------------------------------------------------------------
/ocma_data/core/date/logic.py:
--------------------------------------------------------------------------------
1 | """Module providing a function that calculate the moon phase for a given date."""
2 |
3 | from datetime import date
4 |
5 | from ocma_data.constants import CalendarStyles
6 | from ocma_data.utils.date_utils import date_to_string, modify_date
7 |
8 |
9 | def get_date(current_date: date, calendar_style: str) -> dict[str, str]:
10 | """Convert a given date to its equivalent in both the Old and New Calendars."""
11 | if calendar_style == CalendarStyles.OLD_CALENDAR.value:
12 | return {
13 | "date_old": f"{date_to_string(current_date)}",
14 | "date_new": f"{date_to_string(modify_date(current_date, 13))}",
15 | }
16 |
17 | if calendar_style == CalendarStyles.NEW_CALENDAR.value:
18 | return {
19 | "date_old": f"{date_to_string(modify_date(current_date, -13))}",
20 | "date_new": f"{date_to_string(current_date)}",
21 | }
22 |
23 | return {"date_old": "", "date_new": ""}
24 |
--------------------------------------------------------------------------------
/tests/core/test_moon_phase.py:
--------------------------------------------------------------------------------
1 | """Test for moon_phase subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.moon_phase.logic import get_moon_phase
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | ("1924-01-09", "old", "3"),
10 | ("1924-12-28", "old", "3"),
11 | ("2024-12-24", "old", "2"),
12 | ("2025-11-28", "old", "4"),
13 | ("2099-01-08", "old", "1"),
14 | ("2099-12-14", "old", "3"),
15 | ("2099-12-21", "old", "4"),
16 | ("2099-12-28", "old", "1"),
17 | ("1924-01-06", "new", "1"),
18 | ("1924-12-26", "new", "1"),
19 | ("2025-01-06", "new", "2"),
20 | ("2025-12-11", "new", "4"),
21 | ("2099-01-07", "new", "3"),
22 | ("2099-12-27", "new", "3"),
23 | ]
24 |
25 |
26 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
27 | def test_get_moon_phase(current_date: str, calendar_style: str, expected: str) -> None:
28 | """Test for get_moon_phase."""
29 | assert get_moon_phase(string_to_date(current_date), calendar_style) == expected
30 |
--------------------------------------------------------------------------------
/ocma_data/cli/constants.py:
--------------------------------------------------------------------------------
1 | """Constants used in the CLI."""
2 |
3 | from dataclasses import dataclass
4 | from enum import StrEnum
5 |
6 |
7 | @dataclass(frozen=True)
8 | class CLIConstants:
9 | """Immutable constants for CLI."""
10 |
11 | BUILD_FOLDER: str = "public/data"
12 | PASCHALION: str = "paschalion"
13 | YEAR_END: int = 2100
14 | YEAR_START: int = 1924
15 |
16 |
17 | class JsonKeys(StrEnum):
18 | """Immutable JsonKeys constants."""
19 |
20 | DATE_OLD = "date_old"
21 | DATE_NEW = "date_new"
22 | WEEKDAY_INDEX = "weekday_index"
23 | MOON_PHASE_INDEX = "moon_phase_index"
24 | PASCHA_DATE = "pascha_date"
25 | PASCHA_DISTANCE = "pascha_distance"
26 | FASTING_SEASON_INDEX = "fasting_season_index"
27 | FASTING_LAYMEN_INDEX = "fasting_laymen_index"
28 | FASTING_MONKS_INDEX = "fasting_monks_index"
29 | SUNDAY_DESCRIPTION_GR_INDEX = "sunday_description_gr_index"
30 | SUNDAY_DESCRIPTION_RO_INDEX = "sunday_description_ro_index"
31 | SUNDAY_DESCRIPTION_RU_INDEX = "sunday_description_ru_index"
32 | SUNDAY_LECTIONARY_INDEX = "sunday_lectionary_index"
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2025 ephmo
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/tests/core/test_weekday.py:
--------------------------------------------------------------------------------
1 | """Test for weekday subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.weekday.logic import get_weekday
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | ("1924-01-01", "old", "1"),
10 | ("1924-01-01", "new", "2"),
11 | ("1924-12-31", "old", "2"),
12 | ("1924-12-31", "new", "3"),
13 | ("2025-04-21", "new", "1"),
14 | ("2025-04-04", "old", "4"),
15 | ("2025-04-24", "new", "4"),
16 | ("2025-04-05", "old", "5"),
17 | ("2025-04-25", "new", "5"),
18 | ("2025-04-06", "old", "6"),
19 | ("2025-04-26", "new", "6"),
20 | ("2025-04-07", "old", "7"),
21 | ("2025-04-27", "new", "7"),
22 | ("2099-01-01", "old", "3"),
23 | ("2099-01-01", "new", "4"),
24 | ("2099-12-31", "old", "3"),
25 | ("2099-12-31", "new", "4"),
26 | ]
27 |
28 |
29 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
30 | def test_get_weekday(current_date: str, calendar_style: str, expected: str) -> None:
31 | """Test for get_weekday."""
32 | assert get_weekday(string_to_date(current_date), calendar_style) == expected
33 |
--------------------------------------------------------------------------------
/ocma_data/core/pascha/logic.py:
--------------------------------------------------------------------------------
1 | """Module providing a function to calculate the date of Pascha.
2 |
3 | This calculation uses the Jacques Oudin algorithm for the given year.
4 | """
5 |
6 | from datetime import date
7 |
8 | from ocma_data.constants import CalendarStyles
9 | from ocma_data.utils.date_utils import date_to_string, modify_date
10 |
11 |
12 | def calculate_pascha(current_year: int, calendar_style: str) -> str:
13 | """Calculate the date of Pascha for the given year."""
14 | golden_number = current_year % 19
15 | epact = (19 * golden_number + 15) % 30
16 | century_offset = (current_year + current_year // 4 + epact) % 7
17 | paschal_full_moon = epact - century_offset
18 | easter_month = 3 + (paschal_full_moon + 40) // 44
19 | easter_day = paschal_full_moon + 28 - 31 * (easter_month // 4)
20 |
21 | julian_pascha_date = date(current_year, easter_month, easter_day)
22 |
23 | if calendar_style == CalendarStyles.OLD_CALENDAR.value:
24 | return date_to_string(julian_pascha_date)
25 |
26 | if calendar_style == CalendarStyles.NEW_CALENDAR.value:
27 | return date_to_string(modify_date(julian_pascha_date, 13))
28 |
29 | return ""
30 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [project]
2 | name = "ocma-data"
3 | version = "0.7.16-alpha"
4 | description = "JSON data for each liturgical day of the year"
5 | readme = "README.md"
6 | requires-python = ">=3.11"
7 | dependencies = []
8 |
9 | [dependency-groups]
10 | dev = ["pytest>=8.3.5", "ruff>=0.11.1"]
11 |
12 | [tool.ruff]
13 | line-length = 120
14 | indent-width = 4
15 | target-version = "py311"
16 |
17 | [tool.ruff.lint]
18 | select = [
19 | "ARG", # unused arguments
20 | "B", # bugbear
21 | "C90", # mccabe complexity
22 | "D", # pydocstyle
23 | "E", # pycodestyle errors
24 | "F", # pyflakes
25 | "I", # isort
26 | "N", # pep8-naming
27 | "RUF", # ruff-specific rules
28 | "SIM", # simplify
29 | "UP", # pyupgrade
30 | "W", # pycodestyle warnings
31 | ]
32 | ignore = [
33 | "D203", # one-blank-line-before-class (conflicts with D211)
34 | "D211", # no-blank-line-before-class (conflicts with D203)
35 | "D213", # multi-line-summary-second-line (conflicts with D212)
36 | ]
37 |
38 | [tool.ruff.format]
39 | quote-style = "double"
40 | indent-style = "space"
41 |
42 | [tool.pytest.ini_options]
43 | pythonpath = "."
44 | testpaths = ["tests"]
45 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yml:
--------------------------------------------------------------------------------
1 | name: Deploy JSON API
2 |
3 | on:
4 | push:
5 | branches: [main]
6 |
7 | permissions:
8 | contents: read
9 | pages: write
10 | id-token: write
11 |
12 | jobs:
13 | build:
14 | runs-on: ubuntu-latest
15 |
16 | steps:
17 | - uses: actions/checkout@v6
18 |
19 | - uses: actions/configure-pages@v5
20 |
21 | - name: Install uv
22 | uses: astral-sh/setup-uv@v7
23 |
24 | - name: Set up Python
25 | uses: actions/setup-python@v6
26 | with:
27 | python-version-file: "pyproject.toml"
28 |
29 | - name: Install the project
30 | run: uv sync --locked --all-extras --dev
31 |
32 | - name: Build static files
33 | run: uv run -m ocma_data
34 |
35 | - name: Upload static files as artifact
36 | uses: actions/upload-pages-artifact@v4
37 | with:
38 | path: public/
39 |
40 | deploy:
41 | needs: build
42 |
43 | runs-on: ubuntu-latest
44 |
45 | environment:
46 | name: github-pages
47 | url: ${{ steps.deployment.outputs.page_url }}
48 |
49 | steps:
50 | - name: Deploy to GitHub Pages
51 | id: deployment
52 | uses: actions/deploy-pages@v4
53 |
--------------------------------------------------------------------------------
/tests/core/test_date.py:
--------------------------------------------------------------------------------
1 | """Test for date subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.date.logic import get_date
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | ("1924-01-01", "old", {"date_old": "1924-1-1", "date_new": "1924-1-14"}),
10 | ("1924-01-01", "new", {"date_old": "1923-12-19", "date_new": "1924-1-1"}),
11 | ("1924-12-31", "old", {"date_old": "1924-12-31", "date_new": "1925-1-13"}),
12 | ("1924-12-31", "new", {"date_old": "1924-12-18", "date_new": "1924-12-31"}),
13 | ("2025-04-29", "old", {"date_old": "2025-4-29", "date_new": "2025-5-12"}),
14 | ("2025-04-29", "new", {"date_old": "2025-4-16", "date_new": "2025-4-29"}),
15 | ("2099-01-01", "old", {"date_old": "2099-1-1", "date_new": "2099-1-14"}),
16 | ("2099-01-01", "new", {"date_old": "2098-12-19", "date_new": "2099-1-1"}),
17 | ("2099-12-31", "old", {"date_old": "2099-12-31", "date_new": "2100-1-13"}),
18 | ("2099-12-31", "new", {"date_old": "2099-12-18", "date_new": "2099-12-31"}),
19 | ]
20 |
21 |
22 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
23 | def test_get_date(current_date: str, calendar_style: str, expected: str) -> None:
24 | """Test for get_date."""
25 | assert get_date(string_to_date(current_date), calendar_style) == expected
26 |
--------------------------------------------------------------------------------
/tests/core/test_pascha_distance.py:
--------------------------------------------------------------------------------
1 | """Test for pascha_distance subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.pascha_distance.logic import pascha_distance
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | ("1923-01-01", "old", "-84"),
10 | ("1923-01-01", "new", "-97"),
11 | ("1923-12-31", "old", "280"),
12 | ("1923-12-31", "new", "267"),
13 | ("1924-01-01", "old", "-104"),
14 | ("1924-01-01", "new", "-117"),
15 | ("1924-12-31", "old", "261"),
16 | ("1924-12-31", "new", "248"),
17 | ("2025-04-21", "new", "1"),
18 | ("2025-04-04", "old", "-3"),
19 | ("2025-04-24", "new", "4"),
20 | ("2025-04-05", "old", "-2"),
21 | ("2025-04-25", "new", "5"),
22 | ("2025-04-06", "old", "-1"),
23 | ("2025-04-26", "new", "6"),
24 | ("2025-04-07", "old", "0"),
25 | ("2025-04-27", "new", "7"),
26 | ("2099-01-01", "old", "-88"),
27 | ("2099-01-01", "new", "-101"),
28 | ("2099-12-31", "old", "276"),
29 | ("2099-12-31", "new", "263"),
30 | ]
31 |
32 |
33 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
34 | def test_pascha_distance(current_date: str, calendar_style: str, expected: str) -> None:
35 | """Test for pascha_distance."""
36 | assert pascha_distance(string_to_date(current_date), calendar_style) == expected
37 |
--------------------------------------------------------------------------------
/ocma_data/core/date/__main__.py:
--------------------------------------------------------------------------------
1 | """Subpackage for calculating the date for Old and New Calendars."""
2 |
3 | import argparse
4 | from datetime import date
5 |
6 | from ocma_data.constants import CalendarStyles
7 | from ocma_data.core.date.logic import get_date
8 | from ocma_data.utils.date_utils import string_to_date
9 |
10 | OLD = CalendarStyles.OLD_CALENDAR.value
11 | NEW = CalendarStyles.NEW_CALENDAR.value
12 |
13 |
14 | def valid_date(value: str) -> date:
15 | """Validate the given year."""
16 | try:
17 | parsed_date = string_to_date(value)
18 |
19 | except ValueError as err:
20 | raise argparse.ArgumentTypeError(f"Invalid date format: '{value}'. Use YYYY-MM-DD.") from err
21 | return parsed_date
22 |
23 |
24 | def main():
25 | """Parse arguments and print the date for Old and New Calendars."""
26 | parser = argparse.ArgumentParser(description="Calculate the dates.")
27 | parser.add_argument(
28 | "-d",
29 | "--date",
30 | type=valid_date,
31 | required=True,
32 | help="Date to calculate according to the Old and New Calendars",
33 | )
34 | parser.add_argument(
35 | "-c",
36 | "--calendar",
37 | choices=[OLD, NEW],
38 | required=True,
39 | help=f"Calendar style: '{OLD}' or '{NEW}'",
40 | )
41 | args = parser.parse_args()
42 |
43 | print(get_date(args.date, args.calendar))
44 |
45 |
46 | if __name__ == "__main__":
47 | main()
48 |
--------------------------------------------------------------------------------
/ocma_data/core/weekday/__main__.py:
--------------------------------------------------------------------------------
1 | """Subpackage for calculating the weekday of a given date."""
2 |
3 | import argparse
4 | from datetime import date
5 |
6 | from ocma_data.constants import CalendarStyles
7 | from ocma_data.core.weekday.logic import get_weekday
8 | from ocma_data.utils.date_utils import string_to_date
9 |
10 | OLD = CalendarStyles.OLD_CALENDAR.value
11 | NEW = CalendarStyles.NEW_CALENDAR.value
12 |
13 |
14 | def valid_date(value: str) -> date:
15 | """Validate the given year."""
16 | try:
17 | parsed_date = string_to_date(value)
18 |
19 | except ValueError as err:
20 | raise argparse.ArgumentTypeError(f"Invalid date format: '{value}'. Use YYYY-MM-DD.") from err
21 | return parsed_date
22 |
23 |
24 | def main():
25 | """Parse arguments and print the weekday for the given date and calendar."""
26 | parser = argparse.ArgumentParser(description="Calculate the weekday.")
27 | parser.add_argument(
28 | "-d",
29 | "--date",
30 | type=valid_date,
31 | required=True,
32 | help="The date for which to calculate the weekday",
33 | )
34 | parser.add_argument(
35 | "-c",
36 | "--calendar",
37 | choices=[OLD, NEW],
38 | required=True,
39 | help=f"Calendar style: '{OLD}' or '{NEW}'",
40 | )
41 | args = parser.parse_args()
42 |
43 | print(get_weekday(args.date, args.calendar))
44 |
45 |
46 | if __name__ == "__main__":
47 | main()
48 |
--------------------------------------------------------------------------------
/ocma_data/core/pascha/__main__.py:
--------------------------------------------------------------------------------
1 | """Subpackage for Paschal date calculations."""
2 |
3 | import argparse
4 |
5 | from ocma_data.cli.constants import CLIConstants
6 | from ocma_data.constants import CalendarStyles
7 | from ocma_data.core.pascha.logic import calculate_pascha
8 |
9 | YEAR_START = CLIConstants().YEAR_START
10 | YEAR_END = CLIConstants().YEAR_END - 1
11 | OLD = CalendarStyles.OLD_CALENDAR.value
12 | NEW = CalendarStyles.NEW_CALENDAR.value
13 |
14 |
15 | def valid_year(value: str) -> int:
16 | """Validate the given year."""
17 | parsed_year = int(value)
18 | if not (YEAR_START - 1 <= parsed_year <= YEAR_END):
19 | raise argparse.ArgumentTypeError(f"Year must be between {YEAR_START - 1} and {YEAR_END}.")
20 | return parsed_year
21 |
22 |
23 | def main():
24 | """Parse arguments and print the Paschal date for the given year and calendar."""
25 | parser = argparse.ArgumentParser(description="Calculate the Paschal date.")
26 | parser.add_argument(
27 | "-y",
28 | "--year",
29 | type=valid_year,
30 | required=True,
31 | help=f"Year to calculate Pascha ({YEAR_START - 1} - {YEAR_END})",
32 | )
33 | parser.add_argument(
34 | "-c",
35 | "--calendar",
36 | choices=[OLD, NEW],
37 | required=True,
38 | help=f"Calendar style: '{OLD}' or '{NEW}'",
39 | )
40 | args = parser.parse_args()
41 |
42 | print(calculate_pascha(args.year, args.calendar))
43 |
44 |
45 | if __name__ == "__main__":
46 | main()
47 |
--------------------------------------------------------------------------------
/ocma_data/core/moon_phase/logic.py:
--------------------------------------------------------------------------------
1 | """Module providing a function that calculate the moon phase for a given date."""
2 |
3 | from datetime import date
4 |
5 | from ocma_data.constants import CalendarStyles
6 | from ocma_data.core.moon_phase.constants import MOON_PHASES
7 | from ocma_data.utils.date_utils import modify_date
8 |
9 |
10 | def get_moon_phase(current_date: date, calendar_style: str) -> str:
11 | """Calculate the moon phase for a given date."""
12 |
13 | def find_moon_phase(
14 | modified_year: int,
15 | modified_month: int,
16 | modified_day: int,
17 | ) -> str:
18 | """Determine the moon phase for a given date."""
19 | phase_map = {
20 | "new_moon": "1",
21 | "first_quarter": "2",
22 | "full_moon": "3",
23 | "last_quarter": "4",
24 | }
25 | date_key = f"{modified_month}-{modified_day}"
26 |
27 | for phase, value in phase_map.items():
28 | if date_key in MOON_PHASES.get(str(modified_year), {}).get(phase, []):
29 | return value
30 |
31 | return ""
32 |
33 | if calendar_style == CalendarStyles.OLD_CALENDAR.value:
34 | modified_date = modify_date(current_date, 13)
35 | return find_moon_phase(
36 | modified_date.year,
37 | modified_date.month,
38 | modified_date.day,
39 | )
40 |
41 | if calendar_style == CalendarStyles.NEW_CALENDAR.value:
42 | return find_moon_phase(
43 | current_date.year,
44 | current_date.month,
45 | current_date.day,
46 | )
47 |
48 | return ""
49 |
--------------------------------------------------------------------------------
/ocma_data/core/fasting/__main__.py:
--------------------------------------------------------------------------------
1 | """Subpackage for fasting rules calculations."""
2 |
3 | import argparse
4 | from datetime import date
5 |
6 | from ocma_data.cli.constants import CLIConstants
7 | from ocma_data.constants import CalendarStyles
8 | from ocma_data.core.fasting.logic import get_fasting
9 | from ocma_data.utils.date_utils import string_to_date
10 |
11 | YEAR_START = CLIConstants().YEAR_START
12 | YEAR_END = CLIConstants().YEAR_END - 1
13 | OLD = CalendarStyles.OLD_CALENDAR.value
14 | NEW = CalendarStyles.NEW_CALENDAR.value
15 |
16 |
17 | def valid_date(value: str) -> date:
18 | """Validate the given year."""
19 | try:
20 | parsed_date = string_to_date(value)
21 |
22 | except ValueError as err:
23 | raise argparse.ArgumentTypeError(f"Invalid date format: '{value}'. Use YYYY-MM-DD.") from err
24 |
25 | if not (YEAR_START <= parsed_date.year <= YEAR_END):
26 | raise argparse.ArgumentTypeError(f"Year must be between {YEAR_START} and {YEAR_END}.")
27 | return parsed_date
28 |
29 |
30 | def main():
31 | """Parse arguments and print the fasting rules for the given date and calendar."""
32 | parser = argparse.ArgumentParser(description="Calculate the fasting rules.")
33 | parser.add_argument(
34 | "-d",
35 | "--date",
36 | type=valid_date,
37 | required=True,
38 | help="Date to calculate the fasting rules",
39 | )
40 | parser.add_argument(
41 | "-c",
42 | "--calendar",
43 | choices=[OLD, NEW],
44 | required=True,
45 | help=f"Calendar style: '{OLD}' or '{NEW}'",
46 | )
47 | args = parser.parse_args()
48 |
49 | print(get_fasting(args.date, args.calendar))
50 |
51 |
52 | if __name__ == "__main__":
53 | main()
54 |
--------------------------------------------------------------------------------
/ocma_data/core/moon_phase/__main__.py:
--------------------------------------------------------------------------------
1 | """Subpackage for moon phase calculations."""
2 |
3 | import argparse
4 | from datetime import date
5 |
6 | from ocma_data.cli.constants import CLIConstants
7 | from ocma_data.constants import CalendarStyles
8 | from ocma_data.core.moon_phase.logic import get_moon_phase
9 | from ocma_data.utils.date_utils import string_to_date
10 |
11 | YEAR_START = CLIConstants().YEAR_START
12 | YEAR_END = CLIConstants().YEAR_END - 1
13 | OLD = CalendarStyles.OLD_CALENDAR.value
14 | NEW = CalendarStyles.NEW_CALENDAR.value
15 |
16 |
17 | def valid_date(value: str) -> date:
18 | """Validate the given year."""
19 | try:
20 | parsed_date = string_to_date(value)
21 |
22 | except ValueError as err:
23 | raise argparse.ArgumentTypeError(f"Invalid date format: '{value}'. Use YYYY-MM-DD.") from err
24 |
25 | if not (YEAR_START <= parsed_date.year <= YEAR_END):
26 | raise argparse.ArgumentTypeError(f"Year must be between {YEAR_START} and {YEAR_END}.")
27 | return parsed_date
28 |
29 |
30 | def main():
31 | """Parse arguments and print the moon phase for the given date and calendar."""
32 | parser = argparse.ArgumentParser(description="Calculate the moon phase date.")
33 | parser.add_argument(
34 | "-d",
35 | "--date",
36 | type=valid_date,
37 | required=True,
38 | help="Date to calculate the moon phase",
39 | )
40 | parser.add_argument(
41 | "-c",
42 | "--calendar",
43 | choices=[OLD, NEW],
44 | required=True,
45 | help=f"Calendar style: '{OLD}' or '{NEW}'",
46 | )
47 | args = parser.parse_args()
48 |
49 | print(get_moon_phase(args.date, args.calendar))
50 |
51 |
52 | if __name__ == "__main__":
53 | main()
54 |
--------------------------------------------------------------------------------
/ocma_data/core/pascha_distance/__main__.py:
--------------------------------------------------------------------------------
1 | """Subpackage for calculating the distance from Pascha."""
2 |
3 | import argparse
4 | from datetime import date
5 |
6 | from ocma_data.cli.constants import CLIConstants
7 | from ocma_data.constants import CalendarStyles
8 | from ocma_data.core.pascha_distance.logic import pascha_distance
9 | from ocma_data.utils.date_utils import string_to_date
10 |
11 | YEAR_START = CLIConstants().YEAR_START
12 | YEAR_END = CLIConstants().YEAR_END - 1
13 | OLD = CalendarStyles.OLD_CALENDAR.value
14 | NEW = CalendarStyles.NEW_CALENDAR.value
15 |
16 |
17 | def valid_date(value: str) -> date:
18 | """Validate the given year."""
19 | try:
20 | parsed_date = string_to_date(value)
21 |
22 | except ValueError as err:
23 | raise argparse.ArgumentTypeError(f"Invalid date format: '{value}'. Use YYYY-MM-DD.") from err
24 |
25 | if not (YEAR_START - 1 <= parsed_date.year <= YEAR_END):
26 | raise argparse.ArgumentTypeError(f"Year must be between {YEAR_START - 1} and {YEAR_END}.")
27 | return parsed_date
28 |
29 |
30 | def main():
31 | """Parse arguments and print the days between a date and Pascha."""
32 | parser = argparse.ArgumentParser(description="Days between given date and Pascha.")
33 | parser.add_argument(
34 | "-d",
35 | "--date",
36 | type=valid_date,
37 | required=True,
38 | help="The date for which to calculate the Pascha distance",
39 | )
40 | parser.add_argument(
41 | "-c",
42 | "--calendar",
43 | choices=[OLD, NEW],
44 | required=True,
45 | help=f"Calendar style: '{OLD}' or '{NEW}'",
46 | )
47 | args = parser.parse_args()
48 |
49 | print(pascha_distance(args.date, args.calendar))
50 |
51 |
52 | if __name__ == "__main__":
53 | main()
54 |
--------------------------------------------------------------------------------
/ocma_data/utils/date_utils.py:
--------------------------------------------------------------------------------
1 | """Utility functions for date manipulation, validation, comparison, and formatting."""
2 |
3 | from datetime import date, datetime, timedelta
4 |
5 |
6 | def date_to_string(current_date: date) -> str:
7 | """Convert a given date into a string."""
8 | return current_date.strftime("%Y-%m-%d").replace("-0", "-")
9 |
10 |
11 | def is_date_before(current_date: date, comparison_date: date) -> bool:
12 | """Check if the given date comes before another specified date."""
13 | return current_date < comparison_date
14 |
15 |
16 | def is_date_in_range(current_date: date, start_date: date, end_date: date) -> bool:
17 | """Check if the given date is within the specified range."""
18 | if start_date.year > end_date.year:
19 | return False
20 |
21 | if start_date.year < end_date.year:
22 | return start_date.year <= current_date.year < end_date.year
23 |
24 | return start_date <= current_date <= end_date
25 |
26 |
27 | def is_date_in_tuple(month_day: str, comparison_tuple: tuple[str, ...]) -> bool:
28 | """Check if the given date is in a given tuple."""
29 | return month_day in comparison_tuple
30 |
31 |
32 | def is_valid_date(current_year: int, current_month: int, current_day: int) -> bool:
33 | """Check if the provided date string is a valid date."""
34 | try:
35 | date(current_year, current_month, current_day)
36 | except ValueError:
37 | return False
38 | else:
39 | return True
40 |
41 |
42 | def modify_date(current_date: date, number_days: int) -> date:
43 | """Modify a given date by adding or subtracting a number of days."""
44 | return current_date + timedelta(days=number_days)
45 |
46 |
47 | def string_to_date(current_date: str) -> date:
48 | """Convert a given string into a date."""
49 | return datetime.strptime(current_date, "%Y-%m-%d").date()
50 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_03_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 3: ["New Calendar", "2-15", "3-22"]
12 | (
13 | "2010-2-15",
14 | "new",
15 | {
16 | "fasting_season_index": "3",
17 | "fasting_laymen_index": "6",
18 | "fasting_monks_index": "6",
19 | },
20 | ), # Monday
21 | (
22 | "2010-2-16",
23 | "new",
24 | {
25 | "fasting_season_index": "3",
26 | "fasting_laymen_index": "6",
27 | "fasting_monks_index": "6",
28 | },
29 | ), # Tuesday
30 | (
31 | "2041-3-4",
32 | "new",
33 | {
34 | "fasting_season_index": "3",
35 | "fasting_laymen_index": "6",
36 | "fasting_monks_index": "6",
37 | },
38 | ), # Monday
39 | (
40 | "2041-3-5",
41 | "new",
42 | {
43 | "fasting_season_index": "3",
44 | "fasting_laymen_index": "6",
45 | "fasting_monks_index": "6",
46 | },
47 | ), # Tuesday
48 | (
49 | "2078-3-21",
50 | "new",
51 | {
52 | "fasting_season_index": "3",
53 | "fasting_laymen_index": "6",
54 | "fasting_monks_index": "6",
55 | },
56 | ), # Monday
57 | (
58 | "2078-3-22",
59 | "new",
60 | {
61 | "fasting_season_index": "3",
62 | "fasting_laymen_index": "6",
63 | "fasting_monks_index": "6",
64 | },
65 | ), # Tuesday
66 | ]
67 |
68 |
69 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
70 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
71 | """Test for get_fasting."""
72 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
73 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_03_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 3: ["Old Calendar", "2-2", "3-9"]
12 | (
13 | "2010-2-2",
14 | "old",
15 | {
16 | "fasting_season_index": "3",
17 | "fasting_laymen_index": "5",
18 | "fasting_monks_index": "5",
19 | },
20 | ), # FISH_ALLOWED, Monday
21 | (
22 | "2010-2-3",
23 | "old",
24 | {
25 | "fasting_season_index": "3",
26 | "fasting_laymen_index": "6",
27 | "fasting_monks_index": "6",
28 | },
29 | ), # Tuesday
30 | (
31 | "2041-2-19",
32 | "old",
33 | {
34 | "fasting_season_index": "3",
35 | "fasting_laymen_index": "6",
36 | "fasting_monks_index": "6",
37 | },
38 | ), # Monday
39 | (
40 | "2041-2-20",
41 | "old",
42 | {
43 | "fasting_season_index": "3",
44 | "fasting_laymen_index": "6",
45 | "fasting_monks_index": "6",
46 | },
47 | ), # Tuesday
48 | (
49 | "2078-3-8",
50 | "old",
51 | {
52 | "fasting_season_index": "3",
53 | "fasting_laymen_index": "6",
54 | "fasting_monks_index": "6",
55 | },
56 | ), # Monday
57 | (
58 | "2078-3-9",
59 | "old",
60 | {
61 | "fasting_season_index": "3",
62 | "fasting_laymen_index": "6",
63 | "fasting_monks_index": "6",
64 | },
65 | ), # Tuesday
66 | ]
67 |
68 |
69 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
70 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
71 | """Test for get_fasting."""
72 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
73 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_11_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 11: ["New Calendar", "12-25", "1-6"]
12 | (
13 | "1924-12-25",
14 | "new",
15 | {
16 | "fasting_season_index": "11",
17 | "fasting_laymen_index": "1",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Thursday
21 | (
22 | "1924-1-5",
23 | "new",
24 | {
25 | "fasting_season_index": "11",
26 | "fasting_laymen_index": "4",
27 | "fasting_monks_index": "4",
28 | },
29 | ), # STRICT_FAST, Saturday
30 | (
31 | "1924-1-6",
32 | "new",
33 | {
34 | "fasting_season_index": "11",
35 | "fasting_laymen_index": "1",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Sunday
39 | (
40 | "2099-12-25",
41 | "new",
42 | {
43 | "fasting_season_index": "11",
44 | "fasting_laymen_index": "1",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Friday
48 | (
49 | "2099-1-5",
50 | "new",
51 | {
52 | "fasting_season_index": "11",
53 | "fasting_laymen_index": "5",
54 | "fasting_monks_index": "5",
55 | },
56 | ), # STRICT_FAST, Monday
57 | (
58 | "2099-1-6",
59 | "new",
60 | {
61 | "fasting_season_index": "11",
62 | "fasting_laymen_index": "1",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Tuesday
66 | ]
67 |
68 |
69 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
70 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
71 | """Test for get_fasting."""
72 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
73 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_11_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 11: ["Old Calendar", "12-25", "1-6"]
12 | (
13 | "1924-12-25",
14 | "old",
15 | {
16 | "fasting_season_index": "11",
17 | "fasting_laymen_index": "1",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Wednesday
21 | (
22 | "1924-1-5",
23 | "old",
24 | {
25 | "fasting_season_index": "11",
26 | "fasting_laymen_index": "5",
27 | "fasting_monks_index": "5",
28 | },
29 | ), # STRICT_FAST, Friday
30 | (
31 | "1924-1-6",
32 | "old",
33 | {
34 | "fasting_season_index": "11",
35 | "fasting_laymen_index": "1",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Saturday
39 | (
40 | "2099-12-25",
41 | "old",
42 | {
43 | "fasting_season_index": "11",
44 | "fasting_laymen_index": "1",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Thursday
48 | (
49 | "2099-1-5",
50 | "old",
51 | {
52 | "fasting_season_index": "11",
53 | "fasting_laymen_index": "4",
54 | "fasting_monks_index": "4",
55 | },
56 | ), # STRICT_FAST, Sunday
57 | (
58 | "2099-1-6",
59 | "old",
60 | {
61 | "fasting_season_index": "11",
62 | "fasting_laymen_index": "1",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Monday
66 | ]
67 |
68 |
69 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
70 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
71 | """Test for get_fasting."""
72 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
73 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # OCMA-Data
2 |
3 | 
4 | 
5 | 
6 |
7 | ## Overview
8 |
9 | **OCMA-Data** is a structured **Orthodox Calendar** dataset that provides JSON files for each liturgical day of the year, supporting both the **Old Calendar (Julian)** and the **New Calendar (Revised Julian)** from 1924 to 2099. This dataset serves as the foundation for the [OCMA](https://github.com/ephmo/ocma) application, offering comprehensive liturgical details.
10 |
11 | ## Features
12 |
13 | - **Feasts**: Movable and Fixed Feasts.
14 | - **Saints**: Common and Additional Saints.
15 | - **Fasting**: Information on Fasting Seasons and Levels.
16 | - **Lectionary**: Tone, Matins Gospel, Epistle and Gospel for each Sunday.
17 | - **Paschalion**: Pascha date for each year.
18 | - **Moon Phases**: Primary lunar phases relevant to the Calendar.
19 |
20 | ## Multilingual Support & Translation-Friendly Structure
21 |
22 | **OCMA-Data** is available in **English, Greek, Romanian and Russian**, with each language stored in **separate JSON files**. This separation ensures a clear distinction between **liturgical logic** and **language**, allowing easy expansion to additional languages in the future.
23 |
24 | ## Usage
25 |
26 | The JSON files provide structured and reusable data, making **OCMA-Data** a valuable resource for:
27 |
28 | - **Developers** building applications with Orthodox liturgical content.
29 | - **Researchers** studying Orthodox calendars and feast calculations.
30 | - **Faithful** who want easy access to detailed liturgical information.
31 |
32 | **OCMA-Data** provides a structured and reusable resource for developers, researchers, and Orthodox faithful, facilitating access to comprehensive liturgical information.
33 |
34 | ## Contributing
35 |
36 | Contributions are welcome! If you wish to add additional languages or improve the dataset, feel free to submit a pull request.
37 |
38 | ## License
39 |
40 | This application is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
41 |
42 | ## Reporting Issues
43 |
44 | If you encounter any issues or have suggestions for improvements, please [create an issue](https://github.com/ephmo/ocma-data/issues) on our issue tracker. We appreciate your feedback!
45 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_05_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 5: ["New Calendar", "3-29", "5-7"]
12 | (
13 | "2010-3-29",
14 | "new",
15 | {
16 | "fasting_season_index": "5",
17 | "fasting_laymen_index": "5",
18 | "fasting_monks_index": "5",
19 | },
20 | ), # Monday
21 | (
22 | "2010-3-30",
23 | "new",
24 | {
25 | "fasting_season_index": "5",
26 | "fasting_laymen_index": "5",
27 | "fasting_monks_index": "5",
28 | },
29 | ), # Tuesday
30 | (
31 | "2010-3-31",
32 | "new",
33 | {
34 | "fasting_season_index": "5",
35 | "fasting_laymen_index": "5",
36 | "fasting_monks_index": "5",
37 | },
38 | ), # Wednesday
39 | (
40 | "2010-4-1",
41 | "new",
42 | {
43 | "fasting_season_index": "5",
44 | "fasting_laymen_index": "5",
45 | "fasting_monks_index": "5",
46 | },
47 | ), # Thursday
48 | (
49 | "2010-4-2",
50 | "new",
51 | {
52 | "fasting_season_index": "5",
53 | "fasting_laymen_index": "6",
54 | "fasting_monks_index": "6",
55 | },
56 | ), # Friday
57 | (
58 | "2010-4-3",
59 | "new",
60 | {
61 | "fasting_season_index": "5",
62 | "fasting_laymen_index": "5",
63 | "fasting_monks_index": "5",
64 | },
65 | ), # Saturday
66 | (
67 | "2041-4-15",
68 | "new",
69 | {
70 | "fasting_season_index": "5",
71 | "fasting_laymen_index": "5",
72 | "fasting_monks_index": "5",
73 | },
74 | ), # Monday
75 | (
76 | "2041-4-20",
77 | "new",
78 | {
79 | "fasting_season_index": "5",
80 | "fasting_laymen_index": "5",
81 | "fasting_monks_index": "5",
82 | },
83 | ), # Saturday
84 | (
85 | "2078-5-2",
86 | "new",
87 | {
88 | "fasting_season_index": "5",
89 | "fasting_laymen_index": "5",
90 | "fasting_monks_index": "5",
91 | },
92 | ), # Monday
93 | (
94 | "2078-5-7",
95 | "new",
96 | {
97 | "fasting_season_index": "5",
98 | "fasting_laymen_index": "5",
99 | "fasting_monks_index": "5",
100 | },
101 | ), # Saturday
102 | ]
103 |
104 |
105 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
106 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
107 | """Test for get_fasting."""
108 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
109 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_09_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 9: ["New Calendar", "8-1", "8-14"]
12 | (
13 | "1924-8-1",
14 | "new",
15 | {
16 | "fasting_season_index": "9",
17 | "fasting_laymen_index": "5",
18 | "fasting_monks_index": "5",
19 | },
20 | ), # Friday
21 | (
22 | "1924-8-3",
23 | "new",
24 | {
25 | "fasting_season_index": "9",
26 | "fasting_laymen_index": "4",
27 | "fasting_monks_index": "4",
28 | },
29 | ), # Sunday
30 | (
31 | "1924-8-6",
32 | "new",
33 | {
34 | "fasting_season_index": "9",
35 | "fasting_laymen_index": "3",
36 | "fasting_monks_index": "3",
37 | },
38 | ), # FISH_ALLOWED, Wednesday
39 | (
40 | "1924-8-14",
41 | "new",
42 | {
43 | "fasting_season_index": "9",
44 | "fasting_laymen_index": "5",
45 | "fasting_monks_index": "5",
46 | },
47 | ), # Thursday
48 | (
49 | "2099-8-1",
50 | "new",
51 | {
52 | "fasting_season_index": "9",
53 | "fasting_laymen_index": "4",
54 | "fasting_monks_index": "4",
55 | },
56 | ), # Saturday
57 | (
58 | "2099-8-3",
59 | "new",
60 | {
61 | "fasting_season_index": "9",
62 | "fasting_laymen_index": "5",
63 | "fasting_monks_index": "5",
64 | },
65 | ), # Monday
66 | (
67 | "2099-8-4",
68 | "new",
69 | {
70 | "fasting_season_index": "9",
71 | "fasting_laymen_index": "5",
72 | "fasting_monks_index": "5",
73 | },
74 | ), # Tuesday
75 | (
76 | "2099-8-5",
77 | "new",
78 | {
79 | "fasting_season_index": "9",
80 | "fasting_laymen_index": "5",
81 | "fasting_monks_index": "5",
82 | },
83 | ), # Wednesday
84 | (
85 | "2099-8-6",
86 | "new",
87 | {
88 | "fasting_season_index": "9",
89 | "fasting_laymen_index": "3",
90 | "fasting_monks_index": "3",
91 | },
92 | ), # FISH_ALLOWED, Thursday
93 | (
94 | "2099-8-14",
95 | "new",
96 | {
97 | "fasting_season_index": "9",
98 | "fasting_laymen_index": "5",
99 | "fasting_monks_index": "5",
100 | },
101 | ), # Friday
102 | ]
103 |
104 |
105 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
106 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
107 | """Test for get_fasting."""
108 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
109 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_09_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 9: ["Old Calendar", "8-1", "8-14"]
12 | (
13 | "1924-8-1",
14 | "old",
15 | {
16 | "fasting_season_index": "9",
17 | "fasting_laymen_index": "5",
18 | "fasting_monks_index": "5",
19 | },
20 | ), # Thursday
21 | (
22 | "1924-8-6",
23 | "old",
24 | {
25 | "fasting_season_index": "9",
26 | "fasting_laymen_index": "3",
27 | "fasting_monks_index": "3",
28 | },
29 | ), # FISH_ALLOWED, Tuesday
30 | (
31 | "1924-8-10",
32 | "old",
33 | {
34 | "fasting_season_index": "9",
35 | "fasting_laymen_index": "4",
36 | "fasting_monks_index": "4",
37 | },
38 | ), # Saturday
39 | (
40 | "1924-8-11",
41 | "old",
42 | {
43 | "fasting_season_index": "9",
44 | "fasting_laymen_index": "4",
45 | "fasting_monks_index": "4",
46 | },
47 | ), # Sunday
48 | (
49 | "1924-8-12",
50 | "old",
51 | {
52 | "fasting_season_index": "9",
53 | "fasting_laymen_index": "5",
54 | "fasting_monks_index": "5",
55 | },
56 | ), # Monday
57 | (
58 | "1924-8-13",
59 | "old",
60 | {
61 | "fasting_season_index": "9",
62 | "fasting_laymen_index": "5",
63 | "fasting_monks_index": "5",
64 | },
65 | ), # Tuesday
66 | (
67 | "1924-8-14",
68 | "old",
69 | {
70 | "fasting_season_index": "9",
71 | "fasting_laymen_index": "5",
72 | "fasting_monks_index": "5",
73 | },
74 | ), # Wednesday
75 | (
76 | "2099-8-1",
77 | "old",
78 | {
79 | "fasting_season_index": "9",
80 | "fasting_laymen_index": "5",
81 | "fasting_monks_index": "5",
82 | },
83 | ), # Friday
84 | (
85 | "2099-8-6",
86 | "old",
87 | {
88 | "fasting_season_index": "9",
89 | "fasting_laymen_index": "3",
90 | "fasting_monks_index": "3",
91 | },
92 | ), # FISH_ALLOWED, Wednesday
93 | (
94 | "2099-8-14",
95 | "old",
96 | {
97 | "fasting_season_index": "9",
98 | "fasting_laymen_index": "5",
99 | "fasting_monks_index": "5",
100 | },
101 | ), # Thursday
102 | ]
103 |
104 |
105 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
106 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
107 | """Test for get_fasting."""
108 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
109 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_02_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 2: ["Old Calendar", "1-26", "3-7"]
12 | (
13 | "2010-1-26",
14 | "old",
15 | {
16 | "fasting_season_index": "2",
17 | "fasting_laymen_index": "2",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Monday
21 | (
22 | "2010-1-27",
23 | "old",
24 | {
25 | "fasting_season_index": "2",
26 | "fasting_laymen_index": "2",
27 | "fasting_monks_index": "2",
28 | },
29 | ), # Tuesday
30 | (
31 | "2010-2-1",
32 | "old",
33 | {
34 | "fasting_season_index": "2",
35 | "fasting_laymen_index": "2",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Sunday
39 | (
40 | "2041-2-12",
41 | "old",
42 | {
43 | "fasting_season_index": "2",
44 | "fasting_laymen_index": "2",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Monday
48 | (
49 | "2041-2-14",
50 | "old",
51 | {
52 | "fasting_season_index": "2",
53 | "fasting_laymen_index": "2",
54 | "fasting_monks_index": "2",
55 | },
56 | ), # Wednesday
57 | (
58 | "2041-2-15",
59 | "old",
60 | {
61 | "fasting_season_index": "2",
62 | "fasting_laymen_index": "2",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Thursday
66 | (
67 | "2041-2-18",
68 | "old",
69 | {
70 | "fasting_season_index": "2",
71 | "fasting_laymen_index": "2",
72 | "fasting_monks_index": "2",
73 | },
74 | ), # Sunday
75 | (
76 | "2078-3-1",
77 | "old",
78 | {
79 | "fasting_season_index": "2",
80 | "fasting_laymen_index": "2",
81 | "fasting_monks_index": "2",
82 | },
83 | ), # Monday
84 | (
85 | "2078-3-5",
86 | "old",
87 | {
88 | "fasting_season_index": "2",
89 | "fasting_laymen_index": "2",
90 | "fasting_monks_index": "2",
91 | },
92 | ), # Friday
93 | (
94 | "2078-3-6",
95 | "old",
96 | {
97 | "fasting_season_index": "2",
98 | "fasting_laymen_index": "2",
99 | "fasting_monks_index": "2",
100 | },
101 | ), # Saturday
102 | (
103 | "2078-3-7",
104 | "old",
105 | {
106 | "fasting_season_index": "2",
107 | "fasting_laymen_index": "2",
108 | "fasting_monks_index": "2",
109 | },
110 | ), # Sunday
111 | ]
112 |
113 |
114 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
115 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
116 | """Test for get_fasting."""
117 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
118 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_02_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 2: ["New Calendar", "2-8", "3-20"]
12 | (
13 | "2010-2-8",
14 | "new",
15 | {
16 | "fasting_season_index": "2",
17 | "fasting_laymen_index": "2",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Monday
21 | (
22 | "2010-2-9",
23 | "new",
24 | {
25 | "fasting_season_index": "2",
26 | "fasting_laymen_index": "2",
27 | "fasting_monks_index": "2",
28 | },
29 | ), # Tuesday
30 | (
31 | "2010-2-14",
32 | "new",
33 | {
34 | "fasting_season_index": "2",
35 | "fasting_laymen_index": "2",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Sunday
39 | (
40 | "2041-2-25",
41 | "new",
42 | {
43 | "fasting_season_index": "2",
44 | "fasting_laymen_index": "2",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Monday
48 | (
49 | "2041-2-27",
50 | "new",
51 | {
52 | "fasting_season_index": "2",
53 | "fasting_laymen_index": "2",
54 | "fasting_monks_index": "2",
55 | },
56 | ), # Wednesday
57 | (
58 | "2041-2-28",
59 | "new",
60 | {
61 | "fasting_season_index": "2",
62 | "fasting_laymen_index": "2",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Thursday
66 | (
67 | "2041-3-3",
68 | "new",
69 | {
70 | "fasting_season_index": "2",
71 | "fasting_laymen_index": "2",
72 | "fasting_monks_index": "2",
73 | },
74 | ), # Sunday
75 | (
76 | "2078-3-14",
77 | "new",
78 | {
79 | "fasting_season_index": "2",
80 | "fasting_laymen_index": "2",
81 | "fasting_monks_index": "2",
82 | },
83 | ), # Monday
84 | (
85 | "2078-3-18",
86 | "new",
87 | {
88 | "fasting_season_index": "2",
89 | "fasting_laymen_index": "2",
90 | "fasting_monks_index": "2",
91 | },
92 | ), # Friday
93 | (
94 | "2078-3-19",
95 | "new",
96 | {
97 | "fasting_season_index": "2",
98 | "fasting_laymen_index": "2",
99 | "fasting_monks_index": "2",
100 | },
101 | ), # Saturday
102 | (
103 | "2078-3-20",
104 | "new",
105 | {
106 | "fasting_season_index": "2",
107 | "fasting_laymen_index": "2",
108 | "fasting_monks_index": "2",
109 | },
110 | ), # Sunday
111 | ]
112 |
113 |
114 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
115 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
116 | """Test for get_fasting."""
117 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
118 |
--------------------------------------------------------------------------------
/ocma_data/core/fasting/constants.py:
--------------------------------------------------------------------------------
1 | """Constants related to Orthodox fasting rules."""
2 |
3 | from enum import IntEnum, StrEnum
4 |
5 |
6 | class FastingLevels(IntEnum):
7 | """Enumeration for fasting levels."""
8 |
9 | NO_FASTING = 1
10 | DAIRY_PRODUCTS_ALLOWED = 2
11 | FISH_ALLOWED = 3
12 | WINE_AND_OLIVE_OIL_ALLOWED = 4
13 | STRICT_FAST = 5
14 | ABSOLUTE_FAST = 6
15 |
16 |
17 | class FastingSeasons(IntEnum):
18 | """Enumeration for fasting seasons."""
19 |
20 | FIRST_WEEK_OF_THE_TRIODION = 1
21 | CHEESEFARE_WEEK = 2
22 | THREE_DAY_FAST = 3
23 | GREAT_LENT = 4
24 | HOLY_WEEK = 5
25 | BRIGHT_WEEK = 6
26 | WEEK_OF_THE_HOLY_SPIRIT = 7
27 | APOSTLES_FAST = 8
28 | DORMITION_FAST = 9
29 | NATIVITY_FAST = 10
30 | TWELVE_DAY_FAST_FREE = 11
31 | REGULAR_SEASON = 12
32 |
33 |
34 | class PaschaDistanceBefore(IntEnum):
35 | """Enumeration for PaschaDistanceBefore."""
36 |
37 | FIRST_WEEK_OF_THE_TRIODION = -70
38 | CHEESEFARE_WEEK = -55
39 | THREE_DAY_FAST = -48
40 | GREAT_LENT = -46
41 | HOLY_WEEK = -6
42 | BRIGHT_WEEK = 0
43 | WEEK_OF_THE_HOLY_SPIRIT = 49
44 | APOSTLES_FAST = 57
45 |
46 |
47 | class PaschaDistanceAfter(IntEnum):
48 | """Enumeration for PaschaDistanceAfter."""
49 |
50 | FIRST_WEEK_OF_THE_TRIODION = -63
51 | CHEESEFARE_WEEK = -49
52 | THREE_DAY_FAST = -47
53 | GREAT_LENT = -7
54 | HOLY_WEEK = -1
55 | BRIGHT_WEEK = 7
56 | WEEK_OF_THE_HOLY_SPIRIT = 56
57 |
58 |
59 | class DateMovable(IntEnum):
60 | """Enumeration for DateMovable."""
61 |
62 | PALM_SUNDAY = -7
63 | MIDFEAST_OF_PENTECOST = 24
64 | LEAVETAKING_OF_PASCHA = 38
65 |
66 |
67 | class DateFixed(StrEnum):
68 | """Enumeration for DateFixed."""
69 |
70 | TWELVE_DAY_FAST_FREE_AFTER = "1-6"
71 | NATIVITY_OF_THE_BAPTIST = "6-24"
72 | APOSTLES_FAST = "6-29"
73 | DORMITION_FAST_BEFORE = "8-1"
74 | DORMITION_FAST_AFTER = "8-14"
75 | NATIVITY_FAST_BEFORE = "11-15"
76 | THE_ENTRANCE_OF_THE_THEOTOKOS = "11-21"
77 | SAINT_SPYRIDON = "12-12"
78 | NATIVITY_FAST_AFTER = "12-24"
79 | TWELVE_DAY_FAST_FREE_BEFORE = "12-25"
80 |
81 |
82 | STRICT_FAST = ("1-5", "8-29", "9-14", "12-24")
83 | WINE_AND_OLIVE_OIL_ALLOWED = (
84 | "1-11",
85 | "1-16",
86 | "1-17",
87 | "1-18",
88 | "1-20",
89 | "1-22",
90 | "1-25",
91 | "1-27",
92 | "1-30",
93 | "2-8",
94 | "2-10",
95 | "2-11",
96 | "2-17",
97 | "2-24",
98 | "3-9",
99 | "4-23",
100 | "4-25",
101 | "4-30",
102 | "5-2",
103 | "5-8",
104 | "5-15",
105 | "5-21",
106 | "5-25",
107 | "6-8",
108 | "6-11",
109 | "6-30",
110 | "7-1",
111 | "7-2",
112 | "7-17",
113 | "7-20",
114 | "7-22",
115 | "7-25",
116 | "7-26",
117 | "7-27",
118 | "8-31",
119 | "9-1",
120 | "9-9",
121 | "9-13",
122 | "9-20",
123 | "9-23",
124 | "9-26",
125 | "10-6",
126 | "10-18",
127 | "10-23",
128 | "10-26",
129 | "11-1",
130 | "11-8",
131 | "11-12",
132 | "11-13",
133 | "11-16",
134 | "11-25",
135 | "11-30",
136 | "12-4",
137 | "12-5",
138 | "12-6",
139 | "12-9",
140 | "12-12",
141 | "12-15",
142 | "12-17",
143 | "12-20",
144 | )
145 | FISH_ALLOWED = (
146 | "1-7",
147 | "2-2",
148 | "3-25",
149 | "6-24",
150 | "6-29",
151 | "8-6",
152 | "8-15",
153 | "9-8",
154 | "11-14",
155 | "11-21",
156 | )
157 |
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | OCMA-Data API
7 |
46 |
47 |
48 | OCMA-Data API
49 |
50 |
51 |
52 | OCMA-Data is a structured Orthodox Calendar dataset
53 | providing JSON files for each liturgical day from
54 | 1924 to 2099 , supporting both the
55 | Old Calendar (Julian) and
56 | New Calendar (Revised Julian) .
57 |
58 |
59 |
60 | It includes Feasts, Saints, Fasting rules, Lectionary information,
61 | Paschalion calculations, and Moon Phases. Data is fully
62 | multilingual with separate JSON files for English,
63 | Greek, Romanian and Russian.
64 |
65 |
66 |
67 | API Examples
68 | Below are example endpoints for accessing the dataset.
69 |
70 |
71 |
Daily Calendar (by Year)
72 |
Example:
73 |
83 |
84 |
85 |
86 |
Multilingual Files
87 |
Each calendar day supports multiple languages.
88 |
97 |
98 |
99 |
100 |
Paschalion
101 |
111 |
112 |
113 |
114 | OCMA-Data • MIT Licensed
115 | • See
116 | README
119 | for full documentation
120 |
121 |
122 |
123 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_01_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 1: ["New Calendar", "1-24", "3-6"]
12 | (
13 | "2010-1-24",
14 | "new",
15 | {
16 | "fasting_season_index": "1",
17 | "fasting_laymen_index": "1",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Sunday
21 | (
22 | "2010-1-25",
23 | "new",
24 | {
25 | "fasting_season_index": "1",
26 | "fasting_laymen_index": "1",
27 | "fasting_monks_index": "2",
28 | },
29 | ), # Monday
30 | (
31 | "2010-1-26",
32 | "new",
33 | {
34 | "fasting_season_index": "1",
35 | "fasting_laymen_index": "1",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Tuesday
39 | (
40 | "2010-1-31",
41 | "new",
42 | {
43 | "fasting_season_index": "1",
44 | "fasting_laymen_index": "1",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Sunday
48 | (
49 | "2041-2-10",
50 | "new",
51 | {
52 | "fasting_season_index": "1",
53 | "fasting_laymen_index": "1",
54 | "fasting_monks_index": "2",
55 | },
56 | ), # Sunday
57 | (
58 | "2041-2-13",
59 | "new",
60 | {
61 | "fasting_season_index": "1",
62 | "fasting_laymen_index": "1",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Wednesday
66 | (
67 | "2041-2-14",
68 | "new",
69 | {
70 | "fasting_season_index": "1",
71 | "fasting_laymen_index": "1",
72 | "fasting_monks_index": "2",
73 | },
74 | ), # Thursday
75 | (
76 | "2041-2-17",
77 | "new",
78 | {
79 | "fasting_season_index": "1",
80 | "fasting_laymen_index": "1",
81 | "fasting_monks_index": "2",
82 | },
83 | ), # Sunday
84 | (
85 | "2078-2-27",
86 | "new",
87 | {
88 | "fasting_season_index": "1",
89 | "fasting_laymen_index": "1",
90 | "fasting_monks_index": "2",
91 | },
92 | ), # Sunday
93 | (
94 | "2078-3-4",
95 | "new",
96 | {
97 | "fasting_season_index": "1",
98 | "fasting_laymen_index": "1",
99 | "fasting_monks_index": "2",
100 | },
101 | ), # Friday
102 | (
103 | "2078-3-5",
104 | "new",
105 | {
106 | "fasting_season_index": "1",
107 | "fasting_laymen_index": "1",
108 | "fasting_monks_index": "2",
109 | },
110 | ), # Saturday
111 | (
112 | "2078-3-6",
113 | "new",
114 | {
115 | "fasting_season_index": "1",
116 | "fasting_laymen_index": "1",
117 | "fasting_monks_index": "2",
118 | },
119 | ), # Sunday
120 | ]
121 |
122 |
123 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
124 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
125 | """Test for get_fasting."""
126 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
127 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_01_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 1: ["Old Calendar", "1-11", "2-21"]
12 | (
13 | "2010-1-11",
14 | "old",
15 | {
16 | "fasting_season_index": "1",
17 | "fasting_laymen_index": "1",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Sunday
21 | (
22 | "2010-1-12",
23 | "old",
24 | {
25 | "fasting_season_index": "1",
26 | "fasting_laymen_index": "1",
27 | "fasting_monks_index": "2",
28 | },
29 | ), # Monday
30 | (
31 | "2010-1-13",
32 | "old",
33 | {
34 | "fasting_season_index": "1",
35 | "fasting_laymen_index": "1",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Tuesday
39 | (
40 | "2010-1-18",
41 | "old",
42 | {
43 | "fasting_season_index": "1",
44 | "fasting_laymen_index": "1",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Sunday
48 | (
49 | "2041-1-28",
50 | "old",
51 | {
52 | "fasting_season_index": "1",
53 | "fasting_laymen_index": "1",
54 | "fasting_monks_index": "2",
55 | },
56 | ), # Sunday
57 | (
58 | "2041-1-31",
59 | "old",
60 | {
61 | "fasting_season_index": "1",
62 | "fasting_laymen_index": "1",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Wednesday
66 | (
67 | "2041-2-1",
68 | "old",
69 | {
70 | "fasting_season_index": "1",
71 | "fasting_laymen_index": "1",
72 | "fasting_monks_index": "2",
73 | },
74 | ), # Thursday
75 | (
76 | "2041-2-4",
77 | "old",
78 | {
79 | "fasting_season_index": "1",
80 | "fasting_laymen_index": "1",
81 | "fasting_monks_index": "2",
82 | },
83 | ), # Sunday
84 | (
85 | "2078-2-14",
86 | "old",
87 | {
88 | "fasting_season_index": "1",
89 | "fasting_laymen_index": "1",
90 | "fasting_monks_index": "2",
91 | },
92 | ), # Sunday
93 | (
94 | "2078-2-19",
95 | "old",
96 | {
97 | "fasting_season_index": "1",
98 | "fasting_laymen_index": "1",
99 | "fasting_monks_index": "2",
100 | },
101 | ), # Friday
102 | (
103 | "2078-2-20",
104 | "old",
105 | {
106 | "fasting_season_index": "1",
107 | "fasting_laymen_index": "1",
108 | "fasting_monks_index": "2",
109 | },
110 | ), # Saturday
111 | (
112 | "2078-2-21",
113 | "old",
114 | {
115 | "fasting_season_index": "1",
116 | "fasting_laymen_index": "1",
117 | "fasting_monks_index": "2",
118 | },
119 | ), # Sunday
120 | ]
121 |
122 |
123 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
124 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
125 | """Test for get_fasting."""
126 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
127 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_06_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 6: ["New Calendar", "4-4", "5-15"]
12 | (
13 | "2010-4-4",
14 | "new",
15 | {
16 | "fasting_season_index": "6",
17 | "fasting_laymen_index": "1",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Sunday
21 | (
22 | "2010-4-5",
23 | "new",
24 | {
25 | "fasting_season_index": "6",
26 | "fasting_laymen_index": "1",
27 | "fasting_monks_index": "2",
28 | },
29 | ), # Monday
30 | (
31 | "2010-4-6",
32 | "new",
33 | {
34 | "fasting_season_index": "6",
35 | "fasting_laymen_index": "1",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Tuesday
39 | (
40 | "2010-4-11",
41 | "new",
42 | {
43 | "fasting_season_index": "6",
44 | "fasting_laymen_index": "1",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Sunday
48 | (
49 | "2041-4-21",
50 | "new",
51 | {
52 | "fasting_season_index": "6",
53 | "fasting_laymen_index": "1",
54 | "fasting_monks_index": "2",
55 | },
56 | ), # Sunday
57 | (
58 | "2041-4-24",
59 | "new",
60 | {
61 | "fasting_season_index": "6",
62 | "fasting_laymen_index": "1",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Wednesday
66 | (
67 | "2041-4-25",
68 | "new",
69 | {
70 | "fasting_season_index": "6",
71 | "fasting_laymen_index": "1",
72 | "fasting_monks_index": "2",
73 | },
74 | ), # Thursday
75 | (
76 | "2041-4-28",
77 | "new",
78 | {
79 | "fasting_season_index": "6",
80 | "fasting_laymen_index": "1",
81 | "fasting_monks_index": "2",
82 | },
83 | ), # Sunday
84 | (
85 | "2078-5-8",
86 | "new",
87 | {
88 | "fasting_season_index": "6",
89 | "fasting_laymen_index": "1",
90 | "fasting_monks_index": "2",
91 | },
92 | ), # Sunday
93 | (
94 | "2078-5-13",
95 | "new",
96 | {
97 | "fasting_season_index": "6",
98 | "fasting_laymen_index": "1",
99 | "fasting_monks_index": "2",
100 | },
101 | ), # Friday
102 | (
103 | "2078-5-14",
104 | "new",
105 | {
106 | "fasting_season_index": "6",
107 | "fasting_laymen_index": "1",
108 | "fasting_monks_index": "2",
109 | },
110 | ), # Saturday
111 | (
112 | "2078-5-15",
113 | "new",
114 | {
115 | "fasting_season_index": "6",
116 | "fasting_laymen_index": "1",
117 | "fasting_monks_index": "2",
118 | },
119 | ), # Sunday
120 | ]
121 |
122 |
123 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
124 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
125 | """Test for get_fasting."""
126 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
127 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_06_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 6: ["Old Calendar", "3-22", "5-2"]
12 | (
13 | "2010-3-22",
14 | "old",
15 | {
16 | "fasting_season_index": "6",
17 | "fasting_laymen_index": "1",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Sunday
21 | (
22 | "2010-3-23",
23 | "old",
24 | {
25 | "fasting_season_index": "6",
26 | "fasting_laymen_index": "1",
27 | "fasting_monks_index": "2",
28 | },
29 | ), # Monday
30 | (
31 | "2010-3-24",
32 | "old",
33 | {
34 | "fasting_season_index": "6",
35 | "fasting_laymen_index": "1",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Tuesday
39 | (
40 | "2010-3-29",
41 | "old",
42 | {
43 | "fasting_season_index": "6",
44 | "fasting_laymen_index": "1",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Sunday
48 | (
49 | "2041-4-8",
50 | "old",
51 | {
52 | "fasting_season_index": "6",
53 | "fasting_laymen_index": "1",
54 | "fasting_monks_index": "2",
55 | },
56 | ), # Sunday
57 | (
58 | "2041-4-11",
59 | "old",
60 | {
61 | "fasting_season_index": "6",
62 | "fasting_laymen_index": "1",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Wednesday
66 | (
67 | "2041-4-12",
68 | "old",
69 | {
70 | "fasting_season_index": "6",
71 | "fasting_laymen_index": "1",
72 | "fasting_monks_index": "2",
73 | },
74 | ), # Thursday
75 | (
76 | "2041-4-15",
77 | "old",
78 | {
79 | "fasting_season_index": "6",
80 | "fasting_laymen_index": "1",
81 | "fasting_monks_index": "2",
82 | },
83 | ), # Sunday
84 | (
85 | "2078-4-25",
86 | "old",
87 | {
88 | "fasting_season_index": "6",
89 | "fasting_laymen_index": "1",
90 | "fasting_monks_index": "2",
91 | },
92 | ), # Sunday
93 | (
94 | "2078-4-30",
95 | "old",
96 | {
97 | "fasting_season_index": "6",
98 | "fasting_laymen_index": "1",
99 | "fasting_monks_index": "2",
100 | },
101 | ), # Friday
102 | (
103 | "2078-5-1",
104 | "old",
105 | {
106 | "fasting_season_index": "6",
107 | "fasting_laymen_index": "1",
108 | "fasting_monks_index": "2",
109 | },
110 | ), # Saturday
111 | (
112 | "2078-5-2",
113 | "old",
114 | {
115 | "fasting_season_index": "6",
116 | "fasting_laymen_index": "1",
117 | "fasting_monks_index": "2",
118 | },
119 | ), # Sunday
120 | ]
121 |
122 |
123 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
124 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
125 | """Test for get_fasting."""
126 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
127 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_07_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 7: ["New Calendar", "5-23", "7-3"]
12 | (
13 | "2010-5-23",
14 | "new",
15 | {
16 | "fasting_season_index": "7",
17 | "fasting_laymen_index": "1",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Sunday
21 | (
22 | "2010-5-24",
23 | "new",
24 | {
25 | "fasting_season_index": "7",
26 | "fasting_laymen_index": "1",
27 | "fasting_monks_index": "2",
28 | },
29 | ), # Monday
30 | (
31 | "2010-5-25",
32 | "new",
33 | {
34 | "fasting_season_index": "7",
35 | "fasting_laymen_index": "1",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Tuesday
39 | (
40 | "2010-5-30",
41 | "new",
42 | {
43 | "fasting_season_index": "7",
44 | "fasting_laymen_index": "1",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Sunday
48 | (
49 | "2041-6-9",
50 | "new",
51 | {
52 | "fasting_season_index": "7",
53 | "fasting_laymen_index": "1",
54 | "fasting_monks_index": "2",
55 | },
56 | ), # Sunday
57 | (
58 | "2041-6-12",
59 | "new",
60 | {
61 | "fasting_season_index": "7",
62 | "fasting_laymen_index": "1",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Wednesday
66 | (
67 | "2041-6-13",
68 | "new",
69 | {
70 | "fasting_season_index": "7",
71 | "fasting_laymen_index": "1",
72 | "fasting_monks_index": "2",
73 | },
74 | ), # Thursday
75 | (
76 | "2041-6-16",
77 | "new",
78 | {
79 | "fasting_season_index": "7",
80 | "fasting_laymen_index": "1",
81 | "fasting_monks_index": "2",
82 | },
83 | ), # Sunday
84 | (
85 | "2078-6-26",
86 | "new",
87 | {
88 | "fasting_season_index": "7",
89 | "fasting_laymen_index": "1",
90 | "fasting_monks_index": "2",
91 | },
92 | ), # Sunday
93 | (
94 | "2078-7-1",
95 | "new",
96 | {
97 | "fasting_season_index": "7",
98 | "fasting_laymen_index": "1",
99 | "fasting_monks_index": "2",
100 | },
101 | ), # Friday
102 | (
103 | "2078-7-2",
104 | "new",
105 | {
106 | "fasting_season_index": "7",
107 | "fasting_laymen_index": "1",
108 | "fasting_monks_index": "2",
109 | },
110 | ), # Saturday
111 | (
112 | "2078-7-3",
113 | "new",
114 | {
115 | "fasting_season_index": "7",
116 | "fasting_laymen_index": "1",
117 | "fasting_monks_index": "2",
118 | },
119 | ), # Sunday
120 | ]
121 |
122 |
123 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
124 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
125 | """Test for get_fasting."""
126 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
127 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_07_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 7: ["Old Calendar", "5-10", "6-20"]
12 | (
13 | "2010-5-10",
14 | "old",
15 | {
16 | "fasting_season_index": "7",
17 | "fasting_laymen_index": "1",
18 | "fasting_monks_index": "2",
19 | },
20 | ), # Sunday
21 | (
22 | "2010-5-11",
23 | "old",
24 | {
25 | "fasting_season_index": "7",
26 | "fasting_laymen_index": "1",
27 | "fasting_monks_index": "2",
28 | },
29 | ), # Monday
30 | (
31 | "2010-5-12",
32 | "old",
33 | {
34 | "fasting_season_index": "7",
35 | "fasting_laymen_index": "1",
36 | "fasting_monks_index": "2",
37 | },
38 | ), # Tuesday
39 | (
40 | "2010-5-17",
41 | "old",
42 | {
43 | "fasting_season_index": "7",
44 | "fasting_laymen_index": "1",
45 | "fasting_monks_index": "2",
46 | },
47 | ), # Sunday
48 | (
49 | "2041-5-27",
50 | "old",
51 | {
52 | "fasting_season_index": "7",
53 | "fasting_laymen_index": "1",
54 | "fasting_monks_index": "2",
55 | },
56 | ), # Sunday
57 | (
58 | "2041-5-30",
59 | "old",
60 | {
61 | "fasting_season_index": "7",
62 | "fasting_laymen_index": "1",
63 | "fasting_monks_index": "2",
64 | },
65 | ), # Wednesday
66 | (
67 | "2041-5-31",
68 | "old",
69 | {
70 | "fasting_season_index": "7",
71 | "fasting_laymen_index": "1",
72 | "fasting_monks_index": "2",
73 | },
74 | ), # Thursday
75 | (
76 | "2041-6-3",
77 | "old",
78 | {
79 | "fasting_season_index": "7",
80 | "fasting_laymen_index": "1",
81 | "fasting_monks_index": "2",
82 | },
83 | ), # Sunday
84 | (
85 | "2078-6-13",
86 | "old",
87 | {
88 | "fasting_season_index": "7",
89 | "fasting_laymen_index": "1",
90 | "fasting_monks_index": "2",
91 | },
92 | ), # Sunday
93 | (
94 | "2078-6-18",
95 | "old",
96 | {
97 | "fasting_season_index": "7",
98 | "fasting_laymen_index": "1",
99 | "fasting_monks_index": "2",
100 | },
101 | ), # Friday
102 | (
103 | "2078-6-19",
104 | "old",
105 | {
106 | "fasting_season_index": "7",
107 | "fasting_laymen_index": "1",
108 | "fasting_monks_index": "2",
109 | },
110 | ), # Saturday
111 | (
112 | "2078-6-20",
113 | "old",
114 | {
115 | "fasting_season_index": "7",
116 | "fasting_laymen_index": "1",
117 | "fasting_monks_index": "2",
118 | },
119 | ), # Sunday
120 | ]
121 |
122 |
123 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
124 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
125 | """Test for get_fasting."""
126 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
127 |
--------------------------------------------------------------------------------
/public/i18n/en.json:
--------------------------------------------------------------------------------
1 | {
2 | "descriptions": {
3 | "language": "Language",
4 | "language_en": "English",
5 | "language_gr": "Ελληνικά",
6 | "language_ro": "Română",
7 | "language_ru": "Русский",
8 | "calendar_style": "Calendar Style",
9 | "old_calendar": "Old Calendar",
10 | "new_calendar": "New Calendar",
11 | "fasting_rules": "Fasting Rules",
12 | "monastic": "Monastic",
13 | "laypeople": "Laypeople",
14 | "lectionary": "Lectionary",
15 | "tone": "Tone",
16 | "matins_gospel": "Matins Gospel",
17 | "epistle": "Epistle",
18 | "gospel": "Gospel",
19 | "paschalion": "Paschalion"
20 | },
21 | "months": {
22 | "1": {
23 | "short": "Jan",
24 | "long": "January"
25 | },
26 | "2": {
27 | "short": "Feb",
28 | "long": "February"
29 | },
30 | "3": {
31 | "short": "Mar",
32 | "long": "March"
33 | },
34 | "4": {
35 | "short": "Apr",
36 | "long": "April"
37 | },
38 | "5": {
39 | "short": "May",
40 | "long": "May"
41 | },
42 | "6": {
43 | "short": "Jun",
44 | "long": "June"
45 | },
46 | "7": {
47 | "short": "Jul",
48 | "long": "July"
49 | },
50 | "8": {
51 | "short": "Aug",
52 | "long": "August"
53 | },
54 | "9": {
55 | "short": "Sep",
56 | "long": "September"
57 | },
58 | "10": {
59 | "short": "Oct",
60 | "long": "October"
61 | },
62 | "11": {
63 | "short": "Nov",
64 | "long": "November"
65 | },
66 | "12": {
67 | "short": "Dec",
68 | "long": "December"
69 | }
70 | },
71 | "weekdays": {
72 | "1": {
73 | "short": "Mon",
74 | "long": "Monday"
75 | },
76 | "2": {
77 | "short": "Tue",
78 | "long": "Tuesday"
79 | },
80 | "3": {
81 | "short": "Wed",
82 | "long": "Wednesday"
83 | },
84 | "4": {
85 | "short": "Thu",
86 | "long": "Thursday"
87 | },
88 | "5": {
89 | "short": "Fri",
90 | "long": "Friday"
91 | },
92 | "6": {
93 | "short": "Sat",
94 | "long": "Saturday"
95 | },
96 | "7": {
97 | "short": "Sun",
98 | "long": "Sunday"
99 | }
100 | },
101 | "moon_phases": {
102 | "1": "New Moon",
103 | "2": "First Quarter",
104 | "3": "Full Moon",
105 | "4": "Last Quarter"
106 | },
107 | "fastings": {
108 | "seasons": {
109 | "1": "First Week of the Triodion",
110 | "2": "Cheesefare Week",
111 | "3": "Three-Day Fast",
112 | "4": "Great Lent",
113 | "5": "Holy Week",
114 | "6": "Bright Week",
115 | "7": "Week of the Holy Spirit",
116 | "8": "Apostles' Fast",
117 | "9": "Dormition Fast",
118 | "10": "Nativity Fast",
119 | "11": "Twelve-Day Fast-free",
120 | "12": "Regular Season"
121 | },
122 | "levels": {
123 | "1": "No Fasting",
124 | "2": "Dairy Products Allowed",
125 | "3": "Fish Allowed",
126 | "4": "Wine and Olive Oil Allowed",
127 | "5": "Strict Fast",
128 | "6": "Absolute Fast"
129 | }
130 | },
131 | "sundays": {
132 | "descriptions": {
133 | "": ""
134 | },
135 | "lectionaries": {
136 | "1": {
137 | "tone": "",
138 | "matins_gospel": "",
139 | "epistle": "",
140 | "gospel": ""
141 | }
142 | }
143 | },
144 | "feasts": {
145 | "movable": {
146 | "major_feasts": {
147 | "": []
148 | },
149 | "minor_feasts": {
150 | "": []
151 | }
152 | },
153 | "fixed": {
154 | "great_feasts": {
155 | "": []
156 | },
157 | "middle_feasts": {
158 | "": []
159 | },
160 | "lesser_feasts": {
161 | "": []
162 | }
163 | }
164 | },
165 | "saints": {
166 | "common_saints": {
167 | "": []
168 | },
169 | "additional_saints": {
170 | "": []
171 | },
172 | "calendar_specific_saints": {
173 | "": []
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/public/i18n/ru.json:
--------------------------------------------------------------------------------
1 | {
2 | "descriptions": {
3 | "language": "Язык",
4 | "language_en": "English",
5 | "language_gr": "Ελληνικά",
6 | "language_ro": "Română",
7 | "language_ru": "Русский",
8 | "calendar_style": "Стиль календаря",
9 | "old_calendar": "Старый стиль",
10 | "new_calendar": "Новый стиль",
11 | "fasting_rules": "Правила поста",
12 | "monastic": "Монашеский",
13 | "laypeople": "Мирской",
14 | "lectionary": "Чтения",
15 | "tone": "Глас",
16 | "matins_gospel": "Евангелие на утрене",
17 | "epistle": "Апостол",
18 | "gospel": "Евангелие",
19 | "paschalion": "Пасхалия"
20 | },
21 | "months": {
22 | "1": {
23 | "short": "Янв",
24 | "long": "Январь"
25 | },
26 | "2": {
27 | "short": "Фев",
28 | "long": "Февраль"
29 | },
30 | "3": {
31 | "short": "Мар",
32 | "long": "Март"
33 | },
34 | "4": {
35 | "short": "Апр",
36 | "long": "Апрель"
37 | },
38 | "5": {
39 | "short": "Май",
40 | "long": "Май"
41 | },
42 | "6": {
43 | "short": "Июн",
44 | "long": "Июнь"
45 | },
46 | "7": {
47 | "short": "Июл",
48 | "long": "Июль"
49 | },
50 | "8": {
51 | "short": "Авг",
52 | "long": "Август"
53 | },
54 | "9": {
55 | "short": "Сен",
56 | "long": "Сентябрь"
57 | },
58 | "10": {
59 | "short": "Окт",
60 | "long": "Октябрь"
61 | },
62 | "11": {
63 | "short": "Ноя",
64 | "long": "Ноябрь"
65 | },
66 | "12": {
67 | "short": "Дек",
68 | "long": "Декабрь"
69 | }
70 | },
71 | "weekdays": {
72 | "1": {
73 | "short": "Пн",
74 | "long": "Понедельник"
75 | },
76 | "2": {
77 | "short": "Вт",
78 | "long": "Вторник"
79 | },
80 | "3": {
81 | "short": "Ср",
82 | "long": "Среда"
83 | },
84 | "4": {
85 | "short": "Чт",
86 | "long": "Четверг"
87 | },
88 | "5": {
89 | "short": "Пт",
90 | "long": "Пятница"
91 | },
92 | "6": {
93 | "short": "Сб",
94 | "long": "Суббота"
95 | },
96 | "7": {
97 | "short": "Вс",
98 | "long": "Воскресенье"
99 | }
100 | },
101 | "moon_phases": {
102 | "1": "Новолуние",
103 | "2": "Первая четверть",
104 | "3": "Полнолуние",
105 | "4": "Последняя четверть"
106 | },
107 | "fastings": {
108 | "seasons": {
109 | "1": "Первая седмица Триоди",
110 | "2": "Сырная седмица",
111 | "3": "Трёхдневный пост",
112 | "4": "Великий пост",
113 | "5": "Страстная седмица",
114 | "6": "Светлая седмица",
115 | "7": "Седмица Святого Духа",
116 | "8": "Петров пост",
117 | "9": "Успенский пост",
118 | "10": "Рождественский пост",
119 | "11": "Двенадцатидневие (сплошная седмица)",
120 | "12": "Обычное время"
121 | },
122 | "levels": {
123 | "1": "Поста нет",
124 | "2": "Разрешается молоко",
125 | "3": "Разрешается рыба",
126 | "4": "Разрешается вино и елей",
127 | "5": "Строгий пост",
128 | "6": "Полное воздержание"
129 | }
130 | },
131 | "sundays": {
132 | "descriptions": {
133 | "": ""
134 | },
135 | "lectionaries": {
136 | "1": {
137 | "tone": "",
138 | "matins_gospel": "",
139 | "epistle": "",
140 | "gospel": ""
141 | }
142 | }
143 | },
144 | "feasts": {
145 | "movable": {
146 | "major_feasts": {
147 | "": []
148 | },
149 | "minor_feasts": {
150 | "": []
151 | }
152 | },
153 | "fixed": {
154 | "great_feasts": {
155 | "": []
156 | },
157 | "middle_feasts": {
158 | "": []
159 | },
160 | "lesser_feasts": {
161 | "": []
162 | }
163 | }
164 | },
165 | "saints": {
166 | "common_saints": {
167 | "": []
168 | },
169 | "additional_saints": {
170 | "": []
171 | },
172 | "calendar_specific_saints": {
173 | "": []
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/public/i18n/ro.json:
--------------------------------------------------------------------------------
1 | {
2 | "descriptions": {
3 | "language": "Limba",
4 | "language_en": "English",
5 | "language_gr": "Ελληνικά",
6 | "language_ro": "Română",
7 | "language_ru": "Русский",
8 | "calendar_style": "Stil calendar",
9 | "old_calendar": "Stil vechi",
10 | "new_calendar": "Stil nou",
11 | "fasting_rules": "Reguli de postire",
12 | "monastic": "Monahale",
13 | "laypeople": "Pentru mireni",
14 | "lectionary": "Evanghelistar",
15 | "tone": "Glas",
16 | "matins_gospel": "Voscreasnă",
17 | "epistle": "Apostol",
18 | "gospel": "Evanghelie",
19 | "paschalion": "Pascalie"
20 | },
21 | "months": {
22 | "1": {
23 | "short": "Ian",
24 | "long": "Ianuarie"
25 | },
26 | "2": {
27 | "short": "Feb",
28 | "long": "Februarie"
29 | },
30 | "3": {
31 | "short": "Mar",
32 | "long": "Martie"
33 | },
34 | "4": {
35 | "short": "Apr",
36 | "long": "Aprilie"
37 | },
38 | "5": {
39 | "short": "Mai",
40 | "long": "Mai"
41 | },
42 | "6": {
43 | "short": "Iun",
44 | "long": "Iunie"
45 | },
46 | "7": {
47 | "short": "Iul",
48 | "long": "Iulie"
49 | },
50 | "8": {
51 | "short": "Aug",
52 | "long": "August"
53 | },
54 | "9": {
55 | "short": "Sep",
56 | "long": "Septembrie"
57 | },
58 | "10": {
59 | "short": "Oct",
60 | "long": "Octombrie"
61 | },
62 | "11": {
63 | "short": "Nov",
64 | "long": "Noiembrie"
65 | },
66 | "12": {
67 | "short": "Dec",
68 | "long": "Decembrie"
69 | }
70 | },
71 | "weekdays": {
72 | "1": {
73 | "short": "Lun",
74 | "long": "Luni"
75 | },
76 | "2": {
77 | "short": "Mar",
78 | "long": "Marți"
79 | },
80 | "3": {
81 | "short": "Mie",
82 | "long": "Miercuri"
83 | },
84 | "4": {
85 | "short": "Joi",
86 | "long": "Joi"
87 | },
88 | "5": {
89 | "short": "Vin",
90 | "long": "Vineri"
91 | },
92 | "6": {
93 | "short": "Sâm",
94 | "long": "Sâmbătă"
95 | },
96 | "7": {
97 | "short": "Dum",
98 | "long": "Duminică"
99 | }
100 | },
101 | "moon_phases": {
102 | "1": "Lună nouă",
103 | "2": "Primul pătrar",
104 | "3": "Lună plină",
105 | "4": "Ultimul pătrar"
106 | },
107 | "fastings": {
108 | "seasons": {
109 | "1": "Prima săptămână a Triodului",
110 | "2": "Săptămâna brânzei",
111 | "3": "Postul de trei zile",
112 | "4": "Postul Mare",
113 | "5": "Săptămâna Mare",
114 | "6": "Săptămâna Luminată",
115 | "7": "Săptămâna Sfântului Duh",
116 | "8": "Postul Sfinților Apostoli",
117 | "9": "Postul Adormirii Maicii Domnului",
118 | "10": "Postul Nașterii Domnului",
119 | "11": "Doisprezece zile de harți",
120 | "12": "Perioadă obișnuită"
121 | },
122 | "levels": {
123 | "1": "Dezlegare la toate",
124 | "2": "Dezlegare la produse lactate",
125 | "3": "Dezlegare la pește",
126 | "4": "Dezlegare la vin și untdelemn",
127 | "5": "Post aspru",
128 | "6": "Post total"
129 | }
130 | },
131 | "sundays": {
132 | "descriptions": {
133 | "": ""
134 | },
135 | "lectionaries": {
136 | "1": {
137 | "tone": "",
138 | "matins_gospel": "",
139 | "epistle": "",
140 | "gospel": ""
141 | }
142 | }
143 | },
144 | "feasts": {
145 | "movable": {
146 | "major_feasts": {
147 | "": []
148 | },
149 | "minor_feasts": {
150 | "": []
151 | }
152 | },
153 | "fixed": {
154 | "great_feasts": {
155 | "": []
156 | },
157 | "middle_feasts": {
158 | "": []
159 | },
160 | "lesser_feasts": {
161 | "": []
162 | }
163 | }
164 | },
165 | "saints": {
166 | "common_saints": {
167 | "": []
168 | },
169 | "additional_saints": {
170 | "": []
171 | },
172 | "calendar_specific_saints": {
173 | "": []
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/public/i18n/el.json:
--------------------------------------------------------------------------------
1 | {
2 | "descriptions": {
3 | "language": "Γλώσσα",
4 | "language_en": "English",
5 | "language_gr": "Ελληνικά",
6 | "language_ro": "Română",
7 | "language_ru": "Русский",
8 | "calendar_style": "Τύπος Ημερολογίου",
9 | "old_calendar": "Παλαιό Ημερολόγιο",
10 | "new_calendar": "Νέο Ημερολόγιο",
11 | "fasting_rules": "Κανόνες νηστείας",
12 | "monastic": "Μοναστικοί",
13 | "laypeople": "Λαϊκοί",
14 | "lectionary": "Κυριακοδρόμιο",
15 | "tone": "Ήχος",
16 | "matins_gospel": "Εωθινό",
17 | "epistle": "Απόστολος",
18 | "gospel": "Ευαγγέλιο",
19 | "paschalion": "Πασχάλιον"
20 | },
21 | "months": {
22 | "1": {
23 | "short": "Ιαν",
24 | "long": "Ιανουάριος"
25 | },
26 | "2": {
27 | "short": "Φεβ",
28 | "long": "Φεβρουάριος"
29 | },
30 | "3": {
31 | "short": "Μαρ",
32 | "long": "Μάρτιος"
33 | },
34 | "4": {
35 | "short": "Απρ",
36 | "long": "Απρίλιος"
37 | },
38 | "5": {
39 | "short": "Μάι",
40 | "long": "Μάιος"
41 | },
42 | "6": {
43 | "short": "Ιούν",
44 | "long": "Ιούνιος"
45 | },
46 | "7": {
47 | "short": "Ιούλ",
48 | "long": "Ιούλιος"
49 | },
50 | "8": {
51 | "short": "Αύγ",
52 | "long": "Αύγουστος"
53 | },
54 | "9": {
55 | "short": "Σεπ",
56 | "long": "Σεπτέμβριος"
57 | },
58 | "10": {
59 | "short": "Οκτ",
60 | "long": "Οκτώβριος"
61 | },
62 | "11": {
63 | "short": "Νοε",
64 | "long": "Νοέμβριος"
65 | },
66 | "12": {
67 | "short": "Δεκ",
68 | "long": "Δεκέμβριος"
69 | }
70 | },
71 | "weekdays": {
72 | "1": {
73 | "short": "Δευ",
74 | "long": "Δευτέρα"
75 | },
76 | "2": {
77 | "short": "Τρι",
78 | "long": "Τρίτη"
79 | },
80 | "3": {
81 | "short": "Τετ",
82 | "long": "Τετάρτη"
83 | },
84 | "4": {
85 | "short": "Πεμ",
86 | "long": "Πέμπτη"
87 | },
88 | "5": {
89 | "short": "Παρ",
90 | "long": "Παρασκευή"
91 | },
92 | "6": {
93 | "short": "Σαβ",
94 | "long": "Σάββατο"
95 | },
96 | "7": {
97 | "short": "Κυρ",
98 | "long": "Κυριακή"
99 | }
100 | },
101 | "moon_phases": {
102 | "1": "Νέα Σελήνη",
103 | "2": "Πρώτο Τέταρτο",
104 | "3": "Πανσέληνος",
105 | "4": "Τελευταίο Τέταρτο"
106 | },
107 | "fastings": {
108 | "seasons": {
109 | "1": "Αʼ Εβδομάδα του Τριωδίου",
110 | "2": "Εβδομάδα της Τυροφάγου",
111 | "3": "Τριήμερο",
112 | "4": "Μεγάλη Τεσσαρακοστή",
113 | "5": "Μεγάλη Εβδομάδα",
114 | "6": "Διακαινήσιμος Εβδομάδα",
115 | "7": "Εβδομάδα του Αγίου Πνεύματος",
116 | "8": "Νηστεία Αγίων Αποστόλων",
117 | "9": "Νηστεία Δεκαπενταυγούστου",
118 | "10": "Νηστεία Χριστουγέννων",
119 | "11": "Δωδεκαήμερο",
120 | "12": "Κανονική Περίοδος"
121 | },
122 | "levels": {
123 | "1": "Κατάλυσις εις πάντα",
124 | "2": "Κατάλυσις γαλακτοκομικών",
125 | "3": "Κατάλυσις ιχθύος",
126 | "4": "Κατάλυσις οίνου και ελαίου",
127 | "5": "Ξηροφαγία",
128 | "6": "Απόλυτη Νηστεία"
129 | }
130 | },
131 | "sundays": {
132 | "descriptions": {
133 | "": ""
134 | },
135 | "lectionaries": {
136 | "1": {
137 | "tone": "",
138 | "matins_gospel": "",
139 | "epistle": "",
140 | "gospel": ""
141 | }
142 | }
143 | },
144 | "feasts": {
145 | "movable": {
146 | "major_feasts": {
147 | "": []
148 | },
149 | "minor_feasts": {
150 | "": []
151 | }
152 | },
153 | "fixed": {
154 | "great_feasts": {
155 | "": []
156 | },
157 | "middle_feasts": {
158 | "": []
159 | },
160 | "lesser_feasts": {
161 | "": []
162 | }
163 | }
164 | },
165 | "saints": {
166 | "common_saints": {
167 | "": []
168 | },
169 | "additional_saints": {
170 | "": []
171 | },
172 | "calendar_specific_saints": {
173 | "": []
174 | }
175 | }
176 | }
177 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_05_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 5: ["Old Calendar", "3-16", "4-24"]
12 | (
13 | "2031-3-25",
14 | "old",
15 | {
16 | "fasting_season_index": "5",
17 | "fasting_laymen_index": "4",
18 | "fasting_monks_index": "4",
19 | },
20 | ), # FISH_ALLOWED, Monday
21 | (
22 | "2015-3-25",
23 | "old",
24 | {
25 | "fasting_season_index": "5",
26 | "fasting_laymen_index": "4",
27 | "fasting_monks_index": "4",
28 | },
29 | ), # FISH_ALLOWED, Tuesday
30 | (
31 | "2004-3-25",
32 | "old",
33 | {
34 | "fasting_season_index": "5",
35 | "fasting_laymen_index": "4",
36 | "fasting_monks_index": "4",
37 | },
38 | ), # FISH_ALLOWED, Wednesday
39 | (
40 | "2061-3-25",
41 | "old",
42 | {
43 | "fasting_season_index": "5",
44 | "fasting_laymen_index": "4",
45 | "fasting_monks_index": "4",
46 | },
47 | ), # FISH_ALLOWED, Thursday
48 | (
49 | "2034-3-25",
50 | "old",
51 | {
52 | "fasting_season_index": "5",
53 | "fasting_laymen_index": "5",
54 | "fasting_monks_index": "5",
55 | },
56 | ), # FISH_ALLOWED, Friday
57 | (
58 | "2018-3-25",
59 | "old",
60 | {
61 | "fasting_season_index": "5",
62 | "fasting_laymen_index": "5",
63 | "fasting_monks_index": "5",
64 | },
65 | ), # FISH_ALLOWED, Saturday
66 | (
67 | "2010-3-16",
68 | "old",
69 | {
70 | "fasting_season_index": "5",
71 | "fasting_laymen_index": "5",
72 | "fasting_monks_index": "5",
73 | },
74 | ), # Monday
75 | (
76 | "2010-3-17",
77 | "old",
78 | {
79 | "fasting_season_index": "5",
80 | "fasting_laymen_index": "5",
81 | "fasting_monks_index": "5",
82 | },
83 | ), # Tuesday
84 | (
85 | "2010-3-18",
86 | "old",
87 | {
88 | "fasting_season_index": "5",
89 | "fasting_laymen_index": "5",
90 | "fasting_monks_index": "5",
91 | },
92 | ), # Wednesday
93 | (
94 | "2010-3-19",
95 | "old",
96 | {
97 | "fasting_season_index": "5",
98 | "fasting_laymen_index": "5",
99 | "fasting_monks_index": "5",
100 | },
101 | ), # Thursday
102 | (
103 | "2010-3-20",
104 | "old",
105 | {
106 | "fasting_season_index": "5",
107 | "fasting_laymen_index": "6",
108 | "fasting_monks_index": "6",
109 | },
110 | ), # Friday
111 | (
112 | "2010-3-21",
113 | "old",
114 | {
115 | "fasting_season_index": "5",
116 | "fasting_laymen_index": "5",
117 | "fasting_monks_index": "5",
118 | },
119 | ), # Saturday
120 | (
121 | "2041-4-2",
122 | "old",
123 | {
124 | "fasting_season_index": "5",
125 | "fasting_laymen_index": "5",
126 | "fasting_monks_index": "5",
127 | },
128 | ), # Monday
129 | (
130 | "2041-4-7",
131 | "old",
132 | {
133 | "fasting_season_index": "5",
134 | "fasting_laymen_index": "5",
135 | "fasting_monks_index": "5",
136 | },
137 | ), # Saturday
138 | (
139 | "2078-4-19",
140 | "old",
141 | {
142 | "fasting_season_index": "5",
143 | "fasting_laymen_index": "5",
144 | "fasting_monks_index": "5",
145 | },
146 | ), # Monday
147 | (
148 | "2078-4-24",
149 | "old",
150 | {
151 | "fasting_season_index": "5",
152 | "fasting_laymen_index": "5",
153 | "fasting_monks_index": "5",
154 | },
155 | ), # Saturday
156 | ]
157 |
158 |
159 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
160 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
161 | """Test for get_fasting."""
162 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
163 |
--------------------------------------------------------------------------------
/ocma_data/cli/main.py:
--------------------------------------------------------------------------------
1 | """Module providing a function generating JSON data for Calendar."""
2 |
3 | import json
4 | from datetime import date
5 | from pathlib import Path
6 |
7 | from ocma_data.cli.constants import CLIConstants, JsonKeys
8 | from ocma_data.constants import CalendarStyles
9 | from ocma_data.core.date.logic import get_date
10 | from ocma_data.core.moon_phase.logic import get_moon_phase
11 | from ocma_data.core.pascha.logic import calculate_pascha
12 | from ocma_data.core.pascha_distance.logic import pascha_distance
13 | from ocma_data.core.weekday.logic import get_weekday
14 | from ocma_data.utils.date_utils import is_valid_date, string_to_date
15 |
16 |
17 | def main() -> None:
18 | """Generate JSON data for the Calendar."""
19 | Path(CLIConstants().BUILD_FOLDER).mkdir(parents=True, exist_ok=True)
20 | Path(f"{CLIConstants().BUILD_FOLDER}/{CLIConstants().PASCHALION}").mkdir(parents=True, exist_ok=True)
21 |
22 | for calendar_style in CalendarStyles:
23 | Path(f"{CLIConstants().BUILD_FOLDER}/{calendar_style.value}").mkdir(parents=True, exist_ok=True)
24 | paschalion_data = {}
25 | paschalion_data[JsonKeys.PASCHA_DATE.value] = {}
26 |
27 | for current_year in range(CLIConstants().YEAR_START, CLIConstants().YEAR_END):
28 | paschalion_data[JsonKeys.PASCHA_DATE.value][current_year] = calculate_pascha(
29 | current_year, calendar_style.value
30 | )
31 |
32 | with Path(
33 | f"{CLIConstants().BUILD_FOLDER}/{CLIConstants().PASCHALION}/{calendar_style.value}.json",
34 | ).open("w", encoding="utf-8") as json_file:
35 | json.dump(paschalion_data, json_file, indent=2)
36 |
37 | calendar_data = {}
38 | calendar_data[str(current_year)] = {}
39 |
40 | for current_month in range(1, 13):
41 | calendar_data[str(current_year)][str(current_month)] = {}
42 |
43 | for current_day in range(1, 32):
44 | if is_valid_date(current_year, current_month, current_day):
45 | current_date = date(current_year, current_month, current_day)
46 | date_old_new = get_date(current_date, calendar_style.value)
47 |
48 | calendar_data[str(current_year)][str(current_month)][str(current_day)] = {}
49 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
50 | JsonKeys.DATE_OLD.value
51 | ] = date_old_new[JsonKeys.DATE_OLD.value]
52 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
53 | JsonKeys.DATE_NEW.value
54 | ] = date_old_new[JsonKeys.DATE_NEW.value]
55 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
56 | JsonKeys.WEEKDAY_INDEX.value
57 | ] = get_weekday(current_date, calendar_style.value)
58 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
59 | JsonKeys.MOON_PHASE_INDEX.value
60 | ] = get_moon_phase(
61 | string_to_date(f"{current_year}-{current_month}-{current_day}"),
62 | calendar_style.value,
63 | )
64 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
65 | JsonKeys.PASCHA_DISTANCE.value
66 | ] = pascha_distance(current_date, calendar_style.value)
67 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
68 | JsonKeys.FASTING_SEASON_INDEX.value
69 | ] = ""
70 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
71 | JsonKeys.FASTING_LAYMEN_INDEX.value
72 | ] = ""
73 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
74 | JsonKeys.FASTING_MONKS_INDEX.value
75 | ] = ""
76 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
77 | JsonKeys.SUNDAY_DESCRIPTION_GR_INDEX.value
78 | ] = ""
79 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
80 | JsonKeys.SUNDAY_DESCRIPTION_RO_INDEX.value
81 | ] = ""
82 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
83 | JsonKeys.SUNDAY_DESCRIPTION_RU_INDEX.value
84 | ] = ""
85 | calendar_data[str(current_year)][str(current_month)][str(current_day)][
86 | JsonKeys.SUNDAY_LECTIONARY_INDEX.value
87 | ] = ""
88 |
89 | with Path(
90 | f"{CLIConstants().BUILD_FOLDER}/{calendar_style.value}/{current_year}.json",
91 | ).open("w", encoding="utf-8") as json_file:
92 | json.dump(calendar_data, json_file, indent=2)
93 |
94 |
95 | if __name__ == "__main__":
96 | main()
97 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_08_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 8: ["New Calendar", "5-31", "6-28"]
12 | (
13 | "2010-5-31",
14 | "new",
15 | {
16 | "fasting_season_index": "8",
17 | "fasting_laymen_index": "5",
18 | "fasting_monks_index": "5",
19 | },
20 | ), # Monday
21 | (
22 | "2010-6-5",
23 | "new",
24 | {
25 | "fasting_season_index": "8",
26 | "fasting_laymen_index": "3",
27 | "fasting_monks_index": "3",
28 | },
29 | ), # Saturday
30 | (
31 | "2010-6-6",
32 | "new",
33 | {
34 | "fasting_season_index": "8",
35 | "fasting_laymen_index": "3",
36 | "fasting_monks_index": "3",
37 | },
38 | ), # Sunday
39 | (
40 | "2010-6-8",
41 | "new",
42 | {
43 | "fasting_season_index": "8",
44 | "fasting_laymen_index": "4",
45 | "fasting_monks_index": "4",
46 | },
47 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Tuesday
48 | (
49 | "2010-6-11",
50 | "new",
51 | {
52 | "fasting_season_index": "8",
53 | "fasting_laymen_index": "4",
54 | "fasting_monks_index": "4",
55 | },
56 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Friday
57 | (
58 | "2010-6-19",
59 | "new",
60 | {
61 | "fasting_season_index": "8",
62 | "fasting_laymen_index": "3",
63 | "fasting_monks_index": "3",
64 | },
65 | ), # Saturday
66 | (
67 | "2010-6-20",
68 | "new",
69 | {
70 | "fasting_season_index": "8",
71 | "fasting_laymen_index": "3",
72 | "fasting_monks_index": "3",
73 | },
74 | ), # Sunday
75 | (
76 | "2010-6-24",
77 | "new",
78 | {
79 | "fasting_season_index": "8",
80 | "fasting_laymen_index": "3",
81 | "fasting_monks_index": "3",
82 | },
83 | ), # FISH_ALLOWED, Thursday
84 | (
85 | "2010-6-26",
86 | "new",
87 | {
88 | "fasting_season_index": "8",
89 | "fasting_laymen_index": "4",
90 | "fasting_monks_index": "4",
91 | },
92 | ), # Saturday
93 | (
94 | "2010-6-27",
95 | "new",
96 | {
97 | "fasting_season_index": "8",
98 | "fasting_laymen_index": "4",
99 | "fasting_monks_index": "4",
100 | },
101 | ), # Sunday
102 | (
103 | "2010-6-28",
104 | "new",
105 | {
106 | "fasting_season_index": "8",
107 | "fasting_laymen_index": "5",
108 | "fasting_monks_index": "5",
109 | },
110 | ), # Monday
111 | (
112 | "2041-6-17",
113 | "new",
114 | {
115 | "fasting_season_index": "8",
116 | "fasting_laymen_index": "5",
117 | "fasting_monks_index": "5",
118 | },
119 | ), # Monday
120 | (
121 | "2041-6-18",
122 | "new",
123 | {
124 | "fasting_season_index": "8",
125 | "fasting_laymen_index": "4",
126 | "fasting_monks_index": "4",
127 | },
128 | ), # Tuesday
129 | (
130 | "2041-6-19",
131 | "new",
132 | {
133 | "fasting_season_index": "8",
134 | "fasting_laymen_index": "5",
135 | "fasting_monks_index": "5",
136 | },
137 | ), # Wednesday
138 | (
139 | "2041-6-20",
140 | "new",
141 | {
142 | "fasting_season_index": "8",
143 | "fasting_laymen_index": "4",
144 | "fasting_monks_index": "4",
145 | },
146 | ), # Thursday
147 | (
148 | "2041-6-22",
149 | "new",
150 | {
151 | "fasting_season_index": "8",
152 | "fasting_laymen_index": "3",
153 | "fasting_monks_index": "3",
154 | },
155 | ), # Saturday
156 | (
157 | "2041-6-23",
158 | "new",
159 | {
160 | "fasting_season_index": "8",
161 | "fasting_laymen_index": "3",
162 | "fasting_monks_index": "3",
163 | },
164 | ), # Sunday
165 | (
166 | "2041-6-24",
167 | "new",
168 | {
169 | "fasting_season_index": "8",
170 | "fasting_laymen_index": "3",
171 | "fasting_monks_index": "3",
172 | },
173 | ), # FISH_ALLOWED, Monday
174 | (
175 | "2041-6-28",
176 | "new",
177 | {
178 | "fasting_season_index": "8",
179 | "fasting_laymen_index": "5",
180 | "fasting_monks_index": "5",
181 | },
182 | ), # Friday
183 | ]
184 |
185 |
186 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
187 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
188 | """Test for get_fasting."""
189 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
190 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_04_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 4: ["Old Calendar", "2-4", "4-18"]
12 | (
13 | "2024-3-25",
14 | "old",
15 | {
16 | "fasting_season_index": "4",
17 | "fasting_laymen_index": "3",
18 | "fasting_monks_index": "3",
19 | },
20 | ), # FISH_ALLOWED, Sunday
21 | (
22 | "2059-3-25",
23 | "old",
24 | {
25 | "fasting_season_index": "4",
26 | "fasting_laymen_index": "3",
27 | "fasting_monks_index": "3",
28 | },
29 | ), # FISH_ALLOWED, Monday
30 | (
31 | "2043-3-25",
32 | "old",
33 | {
34 | "fasting_season_index": "4",
35 | "fasting_laymen_index": "3",
36 | "fasting_monks_index": "3",
37 | },
38 | ), # FISH_ALLOWED, Tuesday
39 | (
40 | "2021-3-25",
41 | "old",
42 | {
43 | "fasting_season_index": "4",
44 | "fasting_laymen_index": "3",
45 | "fasting_monks_index": "3",
46 | },
47 | ), # FISH_ALLOWED, Wednesday
48 | (
49 | "2005-3-25",
50 | "old",
51 | {
52 | "fasting_season_index": "4",
53 | "fasting_laymen_index": "3",
54 | "fasting_monks_index": "3",
55 | },
56 | ), # FISH_ALLOWED, Thursday
57 | (
58 | "2000-3-25",
59 | "old",
60 | {
61 | "fasting_season_index": "4",
62 | "fasting_laymen_index": "3",
63 | "fasting_monks_index": "3",
64 | },
65 | ), # FISH_ALLOWED, Friday
66 | (
67 | "2035-3-25",
68 | "old",
69 | {
70 | "fasting_season_index": "4",
71 | "fasting_laymen_index": "3",
72 | "fasting_monks_index": "3",
73 | },
74 | ), # FISH_ALLOWED, Saturday
75 | (
76 | "2010-2-4",
77 | "old",
78 | {
79 | "fasting_season_index": "4",
80 | "fasting_laymen_index": "5",
81 | "fasting_monks_index": "5",
82 | },
83 | ), # Wednesday
84 | (
85 | "2010-2-7",
86 | "old",
87 | {
88 | "fasting_season_index": "4",
89 | "fasting_laymen_index": "4",
90 | "fasting_monks_index": "4",
91 | },
92 | ), # Saturday
93 | (
94 | "2010-2-8",
95 | "old",
96 | {
97 | "fasting_season_index": "4",
98 | "fasting_laymen_index": "4",
99 | "fasting_monks_index": "4",
100 | },
101 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Sunday
102 | (
103 | "2010-3-9",
104 | "old",
105 | {
106 | "fasting_season_index": "4",
107 | "fasting_laymen_index": "4",
108 | "fasting_monks_index": "4",
109 | },
110 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
111 | (
112 | "2010-3-15",
113 | "old",
114 | {
115 | "fasting_season_index": "4",
116 | "fasting_laymen_index": "3",
117 | "fasting_monks_index": "3",
118 | },
119 | ), # PALM_SUNDAY, Sunday
120 | (
121 | "2041-2-21",
122 | "old",
123 | {
124 | "fasting_season_index": "4",
125 | "fasting_laymen_index": "5",
126 | "fasting_monks_index": "5",
127 | },
128 | ), # Wednesday
129 | (
130 | "2041-2-24",
131 | "old",
132 | {
133 | "fasting_season_index": "4",
134 | "fasting_laymen_index": "4",
135 | "fasting_monks_index": "4",
136 | },
137 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
138 | (
139 | "2041-2-25",
140 | "old",
141 | {
142 | "fasting_season_index": "4",
143 | "fasting_laymen_index": "4",
144 | "fasting_monks_index": "4",
145 | },
146 | ), # Sunday
147 | (
148 | "2041-2-26",
149 | "old",
150 | {
151 | "fasting_season_index": "4",
152 | "fasting_laymen_index": "5",
153 | "fasting_monks_index": "5",
154 | },
155 | ), # Monday
156 | (
157 | "2041-2-27",
158 | "old",
159 | {
160 | "fasting_season_index": "4",
161 | "fasting_laymen_index": "5",
162 | "fasting_monks_index": "5",
163 | },
164 | ), # Tuesday
165 | (
166 | "2041-3-1",
167 | "old",
168 | {
169 | "fasting_season_index": "4",
170 | "fasting_laymen_index": "5",
171 | "fasting_monks_index": "5",
172 | },
173 | ), # Thursday
174 | (
175 | "2041-3-2",
176 | "old",
177 | {
178 | "fasting_season_index": "4",
179 | "fasting_laymen_index": "5",
180 | "fasting_monks_index": "5",
181 | },
182 | ), # Friday
183 | (
184 | "2041-4-1",
185 | "old",
186 | {
187 | "fasting_season_index": "4",
188 | "fasting_laymen_index": "3",
189 | "fasting_monks_index": "3",
190 | },
191 | ), # PALM_SUNDAY, Sunday
192 | (
193 | "2078-3-10",
194 | "old",
195 | {
196 | "fasting_season_index": "4",
197 | "fasting_laymen_index": "5",
198 | "fasting_monks_index": "5",
199 | },
200 | ), # Wednesday
201 | (
202 | "2078-4-18",
203 | "old",
204 | {
205 | "fasting_season_index": "4",
206 | "fasting_laymen_index": "3",
207 | "fasting_monks_index": "3",
208 | },
209 | ), # PALM_SUNDAY, Sunday
210 | ]
211 |
212 |
213 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
214 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
215 | """Test for get_fasting."""
216 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
217 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_04_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 4: ["New Calendar", "2-17", "5-1"]
12 | (
13 | "2040-3-25",
14 | "new",
15 | {
16 | "fasting_season_index": "4",
17 | "fasting_laymen_index": "3",
18 | "fasting_monks_index": "3",
19 | },
20 | ), # FISH_ALLOWED, Sunday
21 | (
22 | "2013-3-25",
23 | "new",
24 | {
25 | "fasting_season_index": "4",
26 | "fasting_laymen_index": "3",
27 | "fasting_monks_index": "3",
28 | },
29 | ), # FISH_ALLOWED, Monday
30 | (
31 | "2070-3-25",
32 | "new",
33 | {
34 | "fasting_season_index": "4",
35 | "fasting_laymen_index": "3",
36 | "fasting_monks_index": "3",
37 | },
38 | ), # FISH_ALLOWED, Tuesday
39 | (
40 | "2054-3-25",
41 | "new",
42 | {
43 | "fasting_season_index": "4",
44 | "fasting_laymen_index": "3",
45 | "fasting_monks_index": "3",
46 | },
47 | ), # FISH_ALLOWED, Wednesday
48 | (
49 | "2027-3-25",
50 | "new",
51 | {
52 | "fasting_season_index": "4",
53 | "fasting_laymen_index": "3",
54 | "fasting_monks_index": "3",
55 | },
56 | ), # FISH_ALLOWED, Thursday
57 | (
58 | "2016-3-25",
59 | "new",
60 | {
61 | "fasting_season_index": "4",
62 | "fasting_laymen_index": "3",
63 | "fasting_monks_index": "3",
64 | },
65 | ), # FISH_ALLOWED, Friday
66 | (
67 | "2062-3-25",
68 | "new",
69 | {
70 | "fasting_season_index": "4",
71 | "fasting_laymen_index": "3",
72 | "fasting_monks_index": "3",
73 | },
74 | ), # FISH_ALLOWED, Saturday
75 | (
76 | "2010-2-17",
77 | "new",
78 | {
79 | "fasting_season_index": "4",
80 | "fasting_laymen_index": "4",
81 | "fasting_monks_index": "4",
82 | },
83 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Wednesday
84 | (
85 | "2010-3-9",
86 | "new",
87 | {
88 | "fasting_season_index": "4",
89 | "fasting_laymen_index": "4",
90 | "fasting_monks_index": "4",
91 | },
92 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Tuesday
93 | (
94 | "2010-3-28",
95 | "new",
96 | {
97 | "fasting_season_index": "4",
98 | "fasting_laymen_index": "3",
99 | "fasting_monks_index": "3",
100 | },
101 | ), # PALM_SUNDAY, Sunday
102 | (
103 | "2041-3-6",
104 | "new",
105 | {
106 | "fasting_season_index": "4",
107 | "fasting_laymen_index": "5",
108 | "fasting_monks_index": "5",
109 | },
110 | ), # Wednesday
111 | (
112 | "2041-3-9",
113 | "new",
114 | {
115 | "fasting_season_index": "4",
116 | "fasting_laymen_index": "4",
117 | "fasting_monks_index": "4",
118 | },
119 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
120 | (
121 | "2041-3-10",
122 | "new",
123 | {
124 | "fasting_season_index": "4",
125 | "fasting_laymen_index": "4",
126 | "fasting_monks_index": "4",
127 | },
128 | ), # Sunday
129 | (
130 | "2041-3-11",
131 | "new",
132 | {
133 | "fasting_season_index": "4",
134 | "fasting_laymen_index": "5",
135 | "fasting_monks_index": "5",
136 | },
137 | ), # Monday
138 | (
139 | "2041-3-12",
140 | "new",
141 | {
142 | "fasting_season_index": "4",
143 | "fasting_laymen_index": "5",
144 | "fasting_monks_index": "5",
145 | },
146 | ), # Tuesday
147 | (
148 | "2041-3-13",
149 | "new",
150 | {
151 | "fasting_season_index": "4",
152 | "fasting_laymen_index": "5",
153 | "fasting_monks_index": "5",
154 | },
155 | ), # Wednesday
156 | (
157 | "2041-3-14",
158 | "new",
159 | {
160 | "fasting_season_index": "4",
161 | "fasting_laymen_index": "5",
162 | "fasting_monks_index": "5",
163 | },
164 | ), # Thursday
165 | (
166 | "2041-3-15",
167 | "new",
168 | {
169 | "fasting_season_index": "4",
170 | "fasting_laymen_index": "5",
171 | "fasting_monks_index": "5",
172 | },
173 | ), # Friday
174 | (
175 | "2041-3-16",
176 | "new",
177 | {
178 | "fasting_season_index": "4",
179 | "fasting_laymen_index": "4",
180 | "fasting_monks_index": "4",
181 | },
182 | ), # Saturday
183 | (
184 | "2041-4-14",
185 | "new",
186 | {
187 | "fasting_season_index": "4",
188 | "fasting_laymen_index": "3",
189 | "fasting_monks_index": "3",
190 | },
191 | ), # PALM_SUNDAY, Sunday
192 | (
193 | "2078-3-23",
194 | "new",
195 | {
196 | "fasting_season_index": "4",
197 | "fasting_laymen_index": "5",
198 | "fasting_monks_index": "5",
199 | },
200 | ), # Wednesday
201 | (
202 | "2078-4-23",
203 | "new",
204 | {
205 | "fasting_season_index": "4",
206 | "fasting_laymen_index": "4",
207 | "fasting_monks_index": "4",
208 | },
209 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
210 | (
211 | "2078-4-30",
212 | "new",
213 | {
214 | "fasting_season_index": "4",
215 | "fasting_laymen_index": "4",
216 | "fasting_monks_index": "4",
217 | },
218 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
219 | (
220 | "2078-5-1",
221 | "new",
222 | {
223 | "fasting_season_index": "4",
224 | "fasting_laymen_index": "3",
225 | "fasting_monks_index": "3",
226 | },
227 | ), # PALM_SUNDAY, Sunday
228 | ]
229 |
230 |
231 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
232 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
233 | """Test for get_fasting."""
234 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
235 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_08_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 8: ["Old Calendar", "5-18", "6-28"]
12 | (
13 | "2010-5-18",
14 | "old",
15 | {
16 | "fasting_season_index": "8",
17 | "fasting_laymen_index": "5",
18 | "fasting_monks_index": "5",
19 | },
20 | ), # Monday
21 | (
22 | "2010-5-19",
23 | "old",
24 | {
25 | "fasting_season_index": "8",
26 | "fasting_laymen_index": "4",
27 | "fasting_monks_index": "4",
28 | },
29 | ), # Tuesday
30 | (
31 | "2010-5-20",
32 | "old",
33 | {
34 | "fasting_season_index": "8",
35 | "fasting_laymen_index": "5",
36 | "fasting_monks_index": "5",
37 | },
38 | ), # Wednesday
39 | (
40 | "2010-5-21",
41 | "old",
42 | {
43 | "fasting_season_index": "8",
44 | "fasting_laymen_index": "4",
45 | "fasting_monks_index": "4",
46 | },
47 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Thursday
48 | (
49 | "2010-5-22",
50 | "old",
51 | {
52 | "fasting_season_index": "8",
53 | "fasting_laymen_index": "5",
54 | "fasting_monks_index": "5",
55 | },
56 | ), # Friday
57 | (
58 | "2010-5-23",
59 | "old",
60 | {
61 | "fasting_season_index": "8",
62 | "fasting_laymen_index": "3",
63 | "fasting_monks_index": "3",
64 | },
65 | ), # Saturday
66 | (
67 | "2010-5-24",
68 | "old",
69 | {
70 | "fasting_season_index": "8",
71 | "fasting_laymen_index": "3",
72 | "fasting_monks_index": "3",
73 | },
74 | ), # Sunday
75 | (
76 | "2010-5-25",
77 | "old",
78 | {
79 | "fasting_season_index": "8",
80 | "fasting_laymen_index": "4",
81 | "fasting_monks_index": "4",
82 | },
83 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
84 | (
85 | "2010-6-8",
86 | "old",
87 | {
88 | "fasting_season_index": "8",
89 | "fasting_laymen_index": "4",
90 | "fasting_monks_index": "4",
91 | },
92 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
93 | (
94 | "2010-6-11",
95 | "old",
96 | {
97 | "fasting_season_index": "8",
98 | "fasting_laymen_index": "4",
99 | "fasting_monks_index": "4",
100 | },
101 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Thursday
102 | (
103 | "2010-6-20",
104 | "old",
105 | {
106 | "fasting_season_index": "8",
107 | "fasting_laymen_index": "3",
108 | "fasting_monks_index": "3",
109 | },
110 | ), # Saturday
111 | (
112 | "2010-6-21",
113 | "old",
114 | {
115 | "fasting_season_index": "8",
116 | "fasting_laymen_index": "3",
117 | "fasting_monks_index": "3",
118 | },
119 | ), # Sunday
120 | (
121 | "2010-6-24",
122 | "old",
123 | {
124 | "fasting_season_index": "8",
125 | "fasting_laymen_index": "3",
126 | "fasting_monks_index": "3",
127 | },
128 | ), # FISH_ALLOWED, Wednesday
129 | (
130 | "2010-6-27",
131 | "old",
132 | {
133 | "fasting_season_index": "8",
134 | "fasting_laymen_index": "4",
135 | "fasting_monks_index": "4",
136 | },
137 | ), # Saturday
138 | (
139 | "2010-6-28",
140 | "old",
141 | {
142 | "fasting_season_index": "8",
143 | "fasting_laymen_index": "4",
144 | "fasting_monks_index": "4",
145 | },
146 | ), # Sunday
147 | (
148 | "2041-6-4",
149 | "old",
150 | {
151 | "fasting_season_index": "8",
152 | "fasting_laymen_index": "5",
153 | "fasting_monks_index": "5",
154 | },
155 | ), # Monday
156 | (
157 | "2041-6-8",
158 | "old",
159 | {
160 | "fasting_season_index": "8",
161 | "fasting_laymen_index": "4",
162 | "fasting_monks_index": "4",
163 | },
164 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Friday
165 | (
166 | "2041-6-9",
167 | "old",
168 | {
169 | "fasting_season_index": "8",
170 | "fasting_laymen_index": "3",
171 | "fasting_monks_index": "3",
172 | },
173 | ), # Saturday
174 | (
175 | "2041-6-10",
176 | "old",
177 | {
178 | "fasting_season_index": "8",
179 | "fasting_laymen_index": "3",
180 | "fasting_monks_index": "3",
181 | },
182 | ), # Sunday
183 | (
184 | "2041-6-11",
185 | "old",
186 | {
187 | "fasting_season_index": "8",
188 | "fasting_laymen_index": "4",
189 | "fasting_monks_index": "4",
190 | },
191 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
192 | (
193 | "2041-6-23",
194 | "old",
195 | {
196 | "fasting_season_index": "8",
197 | "fasting_laymen_index": "3",
198 | "fasting_monks_index": "3",
199 | },
200 | ), # Saturday
201 | (
202 | "2041-6-24",
203 | "old",
204 | {
205 | "fasting_season_index": "8",
206 | "fasting_laymen_index": "3",
207 | "fasting_monks_index": "3",
208 | },
209 | ), # FISH_ALLOWED, Sunday
210 | (
211 | "2041-6-28",
212 | "old",
213 | {
214 | "fasting_season_index": "8",
215 | "fasting_laymen_index": "4",
216 | "fasting_monks_index": "4",
217 | },
218 | ), # Thursday
219 | (
220 | "2078-6-21",
221 | "old",
222 | {
223 | "fasting_season_index": "8",
224 | "fasting_laymen_index": "5",
225 | "fasting_monks_index": "5",
226 | },
227 | ), # Monday
228 | (
229 | "2078-6-24",
230 | "old",
231 | {
232 | "fasting_season_index": "8",
233 | "fasting_laymen_index": "3",
234 | "fasting_monks_index": "3",
235 | },
236 | ), # FISH_ALLOWED, Thursday
237 | (
238 | "2078-6-26",
239 | "old",
240 | {
241 | "fasting_season_index": "8",
242 | "fasting_laymen_index": "4",
243 | "fasting_monks_index": "4",
244 | },
245 | ), # Saturday
246 | (
247 | "2078-6-27",
248 | "old",
249 | {
250 | "fasting_season_index": "8",
251 | "fasting_laymen_index": "4",
252 | "fasting_monks_index": "4",
253 | },
254 | ), # Sunday
255 | (
256 | "2078-6-28",
257 | "old",
258 | {
259 | "fasting_season_index": "8",
260 | "fasting_laymen_index": "5",
261 | "fasting_monks_index": "5",
262 | },
263 | ), # Monday
264 | ]
265 |
266 |
267 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
268 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
269 | """Test for get_fasting."""
270 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
271 |
--------------------------------------------------------------------------------
/uv.lock:
--------------------------------------------------------------------------------
1 | version = 1
2 | revision = 1
3 | requires-python = ">=3.11"
4 |
5 | [[package]]
6 | name = "colorama"
7 | version = "0.4.6"
8 | source = { registry = "https://pypi.org/simple" }
9 | sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
10 | wheels = [
11 | { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
12 | ]
13 |
14 | [[package]]
15 | name = "iniconfig"
16 | version = "2.1.0"
17 | source = { registry = "https://pypi.org/simple" }
18 | sdist = { url = "https://files.pythonhosted.org/packages/f2/97/ebf4da567aa6827c909642694d71c9fcf53e5b504f2d96afea02718862f3/iniconfig-2.1.0.tar.gz", hash = "sha256:3abbd2e30b36733fee78f9c7f7308f2d0050e88f0087fd25c2645f63c773e1c7", size = 4793 }
19 | wheels = [
20 | { url = "https://files.pythonhosted.org/packages/2c/e1/e6716421ea10d38022b952c159d5161ca1193197fb744506875fbb87ea7b/iniconfig-2.1.0-py3-none-any.whl", hash = "sha256:9deba5723312380e77435581c6bf4935c94cbfab9b1ed33ef8d238ea168eb760", size = 6050 },
21 | ]
22 |
23 | [[package]]
24 | name = "ocma-data"
25 | version = "0.7.16a0"
26 | source = { virtual = "." }
27 |
28 | [package.dev-dependencies]
29 | dev = [
30 | { name = "pytest" },
31 | { name = "ruff" },
32 | ]
33 |
34 | [package.metadata]
35 |
36 | [package.metadata.requires-dev]
37 | dev = [
38 | { name = "pytest", specifier = ">=8.3.5" },
39 | { name = "ruff", specifier = ">=0.11.1" },
40 | ]
41 |
42 | [[package]]
43 | name = "packaging"
44 | version = "25.0"
45 | source = { registry = "https://pypi.org/simple" }
46 | sdist = { url = "https://files.pythonhosted.org/packages/a1/d4/1fc4078c65507b51b96ca8f8c3ba19e6a61c8253c72794544580a7b6c24d/packaging-25.0.tar.gz", hash = "sha256:d443872c98d677bf60f6a1f2f8c1cb748e8fe762d2bf9d3148b5599295b0fc4f", size = 165727 }
47 | wheels = [
48 | { url = "https://files.pythonhosted.org/packages/20/12/38679034af332785aac8774540895e234f4d07f7545804097de4b666afd8/packaging-25.0-py3-none-any.whl", hash = "sha256:29572ef2b1f17581046b3a2227d5c611fb25ec70ca1ba8554b24b0e69331a484", size = 66469 },
49 | ]
50 |
51 | [[package]]
52 | name = "pluggy"
53 | version = "1.5.0"
54 | source = { registry = "https://pypi.org/simple" }
55 | sdist = { url = "https://files.pythonhosted.org/packages/96/2d/02d4312c973c6050a18b314a5ad0b3210edb65a906f868e31c111dede4a6/pluggy-1.5.0.tar.gz", hash = "sha256:2cffa88e94fdc978c4c574f15f9e59b7f4201d439195c3715ca9e2486f1d0cf1", size = 67955 }
56 | wheels = [
57 | { url = "https://files.pythonhosted.org/packages/88/5f/e351af9a41f866ac3f1fac4ca0613908d9a41741cfcf2228f4ad853b697d/pluggy-1.5.0-py3-none-any.whl", hash = "sha256:44e1ad92c8ca002de6377e165f3e0f1be63266ab4d554740532335b9d75ea669", size = 20556 },
58 | ]
59 |
60 | [[package]]
61 | name = "pytest"
62 | version = "8.3.5"
63 | source = { registry = "https://pypi.org/simple" }
64 | dependencies = [
65 | { name = "colorama", marker = "sys_platform == 'win32'" },
66 | { name = "iniconfig" },
67 | { name = "packaging" },
68 | { name = "pluggy" },
69 | ]
70 | sdist = { url = "https://files.pythonhosted.org/packages/ae/3c/c9d525a414d506893f0cd8a8d0de7706446213181570cdbd766691164e40/pytest-8.3.5.tar.gz", hash = "sha256:f4efe70cc14e511565ac476b57c279e12a855b11f48f212af1080ef2263d3845", size = 1450891 }
71 | wheels = [
72 | { url = "https://files.pythonhosted.org/packages/30/3d/64ad57c803f1fa1e963a7946b6e0fea4a70df53c1a7fed304586539c2bac/pytest-8.3.5-py3-none-any.whl", hash = "sha256:c69214aa47deac29fad6c2a4f590b9c4a9fdb16a403176fe154b79c0b4d4d820", size = 343634 },
73 | ]
74 |
75 | [[package]]
76 | name = "ruff"
77 | version = "0.11.1"
78 | source = { registry = "https://pypi.org/simple" }
79 | sdist = { url = "https://files.pythonhosted.org/packages/a0/ab/be73f46695c350147ffbfd98d42f34bfe370e6b4146bb0bfdc5b3c636fc7/ruff-0.11.1.tar.gz", hash = "sha256:f2e209a283c9fa423e268cad015ec4fb249178608f755fb67491ff175ecbffbf", size = 3850423 }
80 | wheels = [
81 | { url = "https://files.pythonhosted.org/packages/de/09/fa72b32fb7a1f670c8d3cb925910447c496117850c6aa1601365ad11cd00/ruff-0.11.1-py3-none-linux_armv6l.whl", hash = "sha256:9c833671aaefcbe280aa54da387264402ffbb1e513ff3420c9c7265ea56d6c5c", size = 10114855 },
82 | { url = "https://files.pythonhosted.org/packages/fa/c2/544da7ee0bf0a28117218065ccac69683d8d7a885544e6c84076cf72351b/ruff-0.11.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:a5a57cd457764228c73066b832040728b02a3837c53c8a781a960b68129c4e0b", size = 10870287 },
83 | { url = "https://files.pythonhosted.org/packages/ee/56/0d1c0a3f3ab644e813f365c7eee5d0f9b75e755c5642400cd6ad7070f730/ruff-0.11.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:da91da0d42e70cd8bda8e6687fab2afd28513a3cc9434539f4149610e63baf8f", size = 10223642 },
84 | { url = "https://files.pythonhosted.org/packages/d6/7c/88f06c23018ca02b8b0f78b62e4c701faa2ef6413d9f5e82277bc094d2e9/ruff-0.11.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:429a2e533e3a0dba2ba7e0608a736e728150aa9b6d179245aa11a1339baa968b", size = 10384382 },
85 | { url = "https://files.pythonhosted.org/packages/67/55/15d2a355e4c64a712938df4c650d7239128865981eb8314a49782a53ef45/ruff-0.11.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:6bbcc2984a4d5cbc0f7b10409e74a00a043be45d813e5e81eb58e707455df7f1", size = 9965192 },
86 | { url = "https://files.pythonhosted.org/packages/39/e8/4312a514fac213e90e5b45120339dbaa95a396f4cf3ab740e757fed4fa35/ruff-0.11.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:88d9c283ebc88faa5bc23fa33f399b6d47a93f5980c92edcddf1f2127aa376b3", size = 11563312 },
87 | { url = "https://files.pythonhosted.org/packages/69/ff/4da09bea58e1d784f45169fd281643422727a1c0776802cd6d4b42a0037d/ruff-0.11.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:1f2b03d504516d6b22065ce7fac2564dac15d79a6a776452dabfdd7673a45b07", size = 12223815 },
88 | { url = "https://files.pythonhosted.org/packages/ec/ba/7e3550f1478a53c4f7efcb0c31a03256eb032a871026da61324ad06dcd0b/ruff-0.11.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:52b95a9071f5ad8552af890bd814c6a04daf5b27297ac1054e3667016f3ab739", size = 11662551 },
89 | { url = "https://files.pythonhosted.org/packages/7d/69/7e2f1c8a0c76de18f9c054382d31c6d5f9b55a3e476b2630fefe289d31a6/ruff-0.11.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:28e2d89e7ba8a1525cdb50bc86c07aba35e7bbeef86dad93781b14ad94dc732c", size = 13792940 },
90 | { url = "https://files.pythonhosted.org/packages/02/4b/14f54bbf8f8dd03cb9a8a0294e7de1dc41f3be1806af07b8a104761db597/ruff-0.11.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e76be5a98dc6c29d85dfa72eb419e8d9276ee96ccf5c33f2b6828001907dcb17", size = 11328700 },
91 | { url = "https://files.pythonhosted.org/packages/f5/0c/40c1ba7e9a8c2c7325267c214746b80da1b54fb7e70a9265608213f4b2fb/ruff-0.11.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:441f94c44fe250691c92382ef84f40acef290766fb3e819a9035e83eadd4dbbe", size = 10272650 },
92 | { url = "https://files.pythonhosted.org/packages/a3/27/f86b786e0153c5b52768370f395086c4523dee1af37757a8ef73779b9fbd/ruff-0.11.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:62882a4cc7c0a48c2f34189bd4c7ba45f3d0efb990e02413eeb180aa042a39ca", size = 9973454 },
93 | { url = "https://files.pythonhosted.org/packages/ab/46/47bf0c7ee80b0476226865d724ee7682323a2cb20aa5d134b8a665f58793/ruff-0.11.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:111dbad1706d8200a7138237b4766b45ba7ee45cc8299c02102f4327624f86a2", size = 10975303 },
94 | { url = "https://files.pythonhosted.org/packages/9d/9d/deea8ece99f31aef07f378f83b5571baca74dff0d28fec720e54b171b862/ruff-0.11.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:e2df41763d7a9fd438b6b7bde7b75eb3a92ef2f4682ed2d8e4b997b5f0c76ca9", size = 11402605 },
95 | { url = "https://files.pythonhosted.org/packages/b8/8c/f11e21f4a5ce0858c35745d43e9a5abd870ed33e151ada23c124bf433e71/ruff-0.11.1-py3-none-win32.whl", hash = "sha256:e17b85919d461583aa7e0171bb4f419a6545b261ca080984db49b1f8dced1d4b", size = 10335500 },
96 | { url = "https://files.pythonhosted.org/packages/21/c9/ac2784bad48c7a8702d2f13851b69b2a08534b59657ee2b724f42aaaf861/ruff-0.11.1-py3-none-win_amd64.whl", hash = "sha256:caa872941b876f7ad73abc60144f9a7f6efb575e4f91c4fc1517f0339bcea01e", size = 11365097 },
97 | { url = "https://files.pythonhosted.org/packages/9e/7d/f37251c102cf155cdf664322f1f362f4fd8fe1eb6870e9154255d3cbf0d8/ruff-0.11.1-py3-none-win_arm64.whl", hash = "sha256:7aa939fa57ef6770d18bd5cf0d6de77198dd762a559bd0d4a8763bdae4c8cc16", size = 10510306 },
98 | ]
99 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_10_new.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 10: ["New Calendar", "11-15", "12-24"]
12 | (
13 | "1924-11-15",
14 | "new",
15 | {
16 | "fasting_season_index": "10",
17 | "fasting_laymen_index": "4",
18 | "fasting_monks_index": "4",
19 | },
20 | ), # Saturday
21 | (
22 | "1924-11-16",
23 | "new",
24 | {
25 | "fasting_season_index": "10",
26 | "fasting_laymen_index": "4",
27 | "fasting_monks_index": "4",
28 | },
29 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Sunday
30 | (
31 | "1924-11-17",
32 | "new",
33 | {
34 | "fasting_season_index": "10",
35 | "fasting_laymen_index": "5",
36 | "fasting_monks_index": "5",
37 | },
38 | ), # Monday
39 | (
40 | "1924-11-18",
41 | "new",
42 | {
43 | "fasting_season_index": "10",
44 | "fasting_laymen_index": "4",
45 | "fasting_monks_index": "4",
46 | },
47 | ), # Tuesday
48 | (
49 | "1924-11-19",
50 | "new",
51 | {
52 | "fasting_season_index": "10",
53 | "fasting_laymen_index": "5",
54 | "fasting_monks_index": "5",
55 | },
56 | ), # Wednesday
57 | (
58 | "1924-11-20",
59 | "new",
60 | {
61 | "fasting_season_index": "10",
62 | "fasting_laymen_index": "4",
63 | "fasting_monks_index": "4",
64 | },
65 | ), # Thursday
66 | (
67 | "1924-11-21",
68 | "new",
69 | {
70 | "fasting_season_index": "10",
71 | "fasting_laymen_index": "3",
72 | "fasting_monks_index": "3",
73 | },
74 | ), # FISH_ALLOWED, Friday
75 | (
76 | "1924-11-22",
77 | "new",
78 | {
79 | "fasting_season_index": "10",
80 | "fasting_laymen_index": "3",
81 | "fasting_monks_index": "3",
82 | },
83 | ), # Saturday
84 | (
85 | "1924-11-23",
86 | "new",
87 | {
88 | "fasting_season_index": "10",
89 | "fasting_laymen_index": "3",
90 | "fasting_monks_index": "3",
91 | },
92 | ), # Sunday
93 | (
94 | "1924-11-25",
95 | "new",
96 | {
97 | "fasting_season_index": "10",
98 | "fasting_laymen_index": "4",
99 | "fasting_monks_index": "4",
100 | },
101 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Tuesday
102 | (
103 | "1924-11-30",
104 | "new",
105 | {
106 | "fasting_season_index": "10",
107 | "fasting_laymen_index": "3",
108 | "fasting_monks_index": "3",
109 | },
110 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Sunday
111 | (
112 | "1924-12-4",
113 | "new",
114 | {
115 | "fasting_season_index": "10",
116 | "fasting_laymen_index": "4",
117 | "fasting_monks_index": "4",
118 | },
119 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Thursday
120 | (
121 | "1924-12-5",
122 | "new",
123 | {
124 | "fasting_season_index": "10",
125 | "fasting_laymen_index": "4",
126 | "fasting_monks_index": "4",
127 | },
128 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Friday
129 | (
130 | "1924-12-6",
131 | "new",
132 | {
133 | "fasting_season_index": "10",
134 | "fasting_laymen_index": "3",
135 | "fasting_monks_index": "3",
136 | },
137 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
138 | (
139 | "1924-12-7",
140 | "new",
141 | {
142 | "fasting_season_index": "10",
143 | "fasting_laymen_index": "3",
144 | "fasting_monks_index": "3",
145 | },
146 | ), # Sunday
147 | (
148 | "1924-12-9",
149 | "new",
150 | {
151 | "fasting_season_index": "10",
152 | "fasting_laymen_index": "4",
153 | "fasting_monks_index": "4",
154 | },
155 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Tuesday
156 | (
157 | "1924-12-12",
158 | "new",
159 | {
160 | "fasting_season_index": "10",
161 | "fasting_laymen_index": "4",
162 | "fasting_monks_index": "4",
163 | },
164 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Friday
165 | (
166 | "1924-12-13",
167 | "new",
168 | {
169 | "fasting_season_index": "10",
170 | "fasting_laymen_index": "4",
171 | "fasting_monks_index": "4",
172 | },
173 | ), # Saturday
174 | (
175 | "1924-12-14",
176 | "new",
177 | {
178 | "fasting_season_index": "10",
179 | "fasting_laymen_index": "4",
180 | "fasting_monks_index": "4",
181 | },
182 | ), # Sunday
183 | (
184 | "1924-12-15",
185 | "new",
186 | {
187 | "fasting_season_index": "10",
188 | "fasting_laymen_index": "4",
189 | "fasting_monks_index": "4",
190 | },
191 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
192 | (
193 | "1924-12-17",
194 | "new",
195 | {
196 | "fasting_season_index": "10",
197 | "fasting_laymen_index": "4",
198 | "fasting_monks_index": "4",
199 | },
200 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Wednesday
201 | (
202 | "1924-12-20",
203 | "new",
204 | {
205 | "fasting_season_index": "10",
206 | "fasting_laymen_index": "4",
207 | "fasting_monks_index": "4",
208 | },
209 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
210 | (
211 | "1924-12-21",
212 | "new",
213 | {
214 | "fasting_season_index": "10",
215 | "fasting_laymen_index": "4",
216 | "fasting_monks_index": "4",
217 | },
218 | ), # Sunday
219 | (
220 | "1924-12-24",
221 | "new",
222 | {
223 | "fasting_season_index": "10",
224 | "fasting_laymen_index": "5",
225 | "fasting_monks_index": "5",
226 | },
227 | ), # STRICT_FAST, Wednesday
228 | (
229 | "2099-11-15",
230 | "new",
231 | {
232 | "fasting_season_index": "10",
233 | "fasting_laymen_index": "4",
234 | "fasting_monks_index": "4",
235 | },
236 | ), # Sunday
237 | (
238 | "2099-11-16",
239 | "new",
240 | {
241 | "fasting_season_index": "10",
242 | "fasting_laymen_index": "4",
243 | "fasting_monks_index": "4",
244 | },
245 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
246 | (
247 | "2099-11-21",
248 | "new",
249 | {
250 | "fasting_season_index": "10",
251 | "fasting_laymen_index": "3",
252 | "fasting_monks_index": "3",
253 | },
254 | ), # FISH_ALLOWED, Saturday
255 | (
256 | "2099-11-22",
257 | "new",
258 | {
259 | "fasting_season_index": "10",
260 | "fasting_laymen_index": "3",
261 | "fasting_monks_index": "3",
262 | },
263 | ), # Sunday
264 | (
265 | "2099-11-25",
266 | "new",
267 | {
268 | "fasting_season_index": "10",
269 | "fasting_laymen_index": "4",
270 | "fasting_monks_index": "4",
271 | },
272 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Wednesday
273 | (
274 | "2099-11-30",
275 | "new",
276 | {
277 | "fasting_season_index": "10",
278 | "fasting_laymen_index": "4",
279 | "fasting_monks_index": "4",
280 | },
281 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
282 | (
283 | "2099-12-4",
284 | "new",
285 | {
286 | "fasting_season_index": "10",
287 | "fasting_laymen_index": "4",
288 | "fasting_monks_index": "4",
289 | },
290 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Friday
291 | (
292 | "2099-12-5",
293 | "new",
294 | {
295 | "fasting_season_index": "10",
296 | "fasting_laymen_index": "3",
297 | "fasting_monks_index": "3",
298 | },
299 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
300 | (
301 | "2099-12-6",
302 | "new",
303 | {
304 | "fasting_season_index": "10",
305 | "fasting_laymen_index": "3",
306 | "fasting_monks_index": "3",
307 | },
308 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Sunday
309 | (
310 | "2099-12-9",
311 | "new",
312 | {
313 | "fasting_season_index": "10",
314 | "fasting_laymen_index": "4",
315 | "fasting_monks_index": "4",
316 | },
317 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Wednesday
318 | (
319 | "2099-12-11",
320 | "new",
321 | {
322 | "fasting_season_index": "10",
323 | "fasting_laymen_index": "5",
324 | "fasting_monks_index": "5",
325 | },
326 | ), # Friday
327 | (
328 | "2099-12-12",
329 | "new",
330 | {
331 | "fasting_season_index": "10",
332 | "fasting_laymen_index": "3",
333 | "fasting_monks_index": "3",
334 | },
335 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
336 | (
337 | "2099-12-13",
338 | "new",
339 | {
340 | "fasting_season_index": "10",
341 | "fasting_laymen_index": "4",
342 | "fasting_monks_index": "4",
343 | },
344 | ), # Sunday
345 | (
346 | "2099-12-15",
347 | "new",
348 | {
349 | "fasting_season_index": "10",
350 | "fasting_laymen_index": "4",
351 | "fasting_monks_index": "4",
352 | },
353 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Tuesday
354 | (
355 | "2099-12-17",
356 | "new",
357 | {
358 | "fasting_season_index": "10",
359 | "fasting_laymen_index": "4",
360 | "fasting_monks_index": "4",
361 | },
362 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Thursday
363 | (
364 | "2099-12-19",
365 | "new",
366 | {
367 | "fasting_season_index": "10",
368 | "fasting_laymen_index": "4",
369 | "fasting_monks_index": "4",
370 | },
371 | ), # Saturday
372 | (
373 | "2099-12-20",
374 | "new",
375 | {
376 | "fasting_season_index": "10",
377 | "fasting_laymen_index": "4",
378 | "fasting_monks_index": "4",
379 | },
380 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Sunday
381 | (
382 | "2018-12-24",
383 | "new",
384 | {
385 | "fasting_season_index": "10",
386 | "fasting_laymen_index": "5",
387 | "fasting_monks_index": "5",
388 | },
389 | ), # STRICT_FAST, Monday
390 | (
391 | "2019-12-24",
392 | "new",
393 | {
394 | "fasting_season_index": "10",
395 | "fasting_laymen_index": "5",
396 | "fasting_monks_index": "5",
397 | },
398 | ), # STRICT_FAST, Tuesday
399 | (
400 | "2099-12-24",
401 | "new",
402 | {
403 | "fasting_season_index": "10",
404 | "fasting_laymen_index": "5",
405 | "fasting_monks_index": "5",
406 | },
407 | ), # STRICT_FAST, Thursday
408 | (
409 | "2021-12-24",
410 | "new",
411 | {
412 | "fasting_season_index": "10",
413 | "fasting_laymen_index": "5",
414 | "fasting_monks_index": "5",
415 | },
416 | ), # STRICT_FAST, Friday
417 | (
418 | "2022-12-24",
419 | "new",
420 | {
421 | "fasting_season_index": "10",
422 | "fasting_laymen_index": "4",
423 | "fasting_monks_index": "4",
424 | },
425 | ), # STRICT_FAST, Saturday
426 | (
427 | "2023-12-24",
428 | "new",
429 | {
430 | "fasting_season_index": "10",
431 | "fasting_laymen_index": "4",
432 | "fasting_monks_index": "4",
433 | },
434 | ), # STRICT_FAST, Sunday
435 | ]
436 |
437 |
438 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
439 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
440 | """Test for get_fasting."""
441 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
442 |
--------------------------------------------------------------------------------
/tests/core/test_fasting_season_10_old.py:
--------------------------------------------------------------------------------
1 | """Test for fasting subpackage."""
2 |
3 | import pytest
4 |
5 | from ocma_data.core.fasting.logic import get_fasting
6 | from ocma_data.utils.date_utils import string_to_date
7 |
8 | test_cases = [
9 | # Maximum range for each fasting season
10 | # Format: ["Calendar", "Start Month-Day", "End Month-Day"]
11 | # Fasting season 10: ["Old Calendar", "11-15", "12-24"]
12 | (
13 | "1924-11-15",
14 | "old",
15 | {
16 | "fasting_season_index": "10",
17 | "fasting_laymen_index": "5",
18 | "fasting_monks_index": "5",
19 | },
20 | ), # Friday
21 | (
22 | "1924-11-16",
23 | "old",
24 | {
25 | "fasting_season_index": "10",
26 | "fasting_laymen_index": "4",
27 | "fasting_monks_index": "4",
28 | },
29 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
30 | (
31 | "1924-11-17",
32 | "old",
33 | {
34 | "fasting_season_index": "10",
35 | "fasting_laymen_index": "4",
36 | "fasting_monks_index": "4",
37 | },
38 | ), # Sunday
39 | (
40 | "1924-11-18",
41 | "old",
42 | {
43 | "fasting_season_index": "10",
44 | "fasting_laymen_index": "5",
45 | "fasting_monks_index": "5",
46 | },
47 | ), # Monday
48 | (
49 | "1924-11-19",
50 | "old",
51 | {
52 | "fasting_season_index": "10",
53 | "fasting_laymen_index": "4",
54 | "fasting_monks_index": "4",
55 | },
56 | ), # Tuesday
57 | (
58 | "1924-11-20",
59 | "old",
60 | {
61 | "fasting_season_index": "10",
62 | "fasting_laymen_index": "5",
63 | "fasting_monks_index": "5",
64 | },
65 | ), # Wednesday
66 | (
67 | "1924-11-21",
68 | "old",
69 | {
70 | "fasting_season_index": "10",
71 | "fasting_laymen_index": "3",
72 | "fasting_monks_index": "3",
73 | },
74 | ), # FISH_ALLOWED, Thursday
75 | (
76 | "1924-11-23",
77 | "old",
78 | {
79 | "fasting_season_index": "10",
80 | "fasting_laymen_index": "3",
81 | "fasting_monks_index": "3",
82 | },
83 | ), # Saturday
84 | (
85 | "1924-11-24",
86 | "old",
87 | {
88 | "fasting_season_index": "10",
89 | "fasting_laymen_index": "3",
90 | "fasting_monks_index": "3",
91 | },
92 | ), # Sunday
93 | (
94 | "1924-11-25",
95 | "old",
96 | {
97 | "fasting_season_index": "10",
98 | "fasting_laymen_index": "4",
99 | "fasting_monks_index": "4",
100 | },
101 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
102 | (
103 | "1924-11-30",
104 | "old",
105 | {
106 | "fasting_season_index": "10",
107 | "fasting_laymen_index": "3",
108 | "fasting_monks_index": "3",
109 | },
110 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
111 | (
112 | "1924-12-4",
113 | "old",
114 | {
115 | "fasting_season_index": "10",
116 | "fasting_laymen_index": "4",
117 | "fasting_monks_index": "4",
118 | },
119 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Wednesday
120 | (
121 | "1924-12-5",
122 | "old",
123 | {
124 | "fasting_season_index": "10",
125 | "fasting_laymen_index": "4",
126 | "fasting_monks_index": "4",
127 | },
128 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Thursday
129 | (
130 | "1924-12-6",
131 | "old",
132 | {
133 | "fasting_season_index": "10",
134 | "fasting_laymen_index": "4",
135 | "fasting_monks_index": "4",
136 | },
137 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Friday
138 | (
139 | "1924-12-7",
140 | "old",
141 | {
142 | "fasting_season_index": "10",
143 | "fasting_laymen_index": "3",
144 | "fasting_monks_index": "3",
145 | },
146 | ), # Saturday
147 | (
148 | "1924-12-8",
149 | "old",
150 | {
151 | "fasting_season_index": "10",
152 | "fasting_laymen_index": "3",
153 | "fasting_monks_index": "3",
154 | },
155 | ), # Sunday
156 | (
157 | "1924-12-9",
158 | "old",
159 | {
160 | "fasting_season_index": "10",
161 | "fasting_laymen_index": "4",
162 | "fasting_monks_index": "4",
163 | },
164 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
165 | (
166 | "1924-12-12",
167 | "old",
168 | {
169 | "fasting_season_index": "10",
170 | "fasting_laymen_index": "4",
171 | "fasting_monks_index": "4",
172 | },
173 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Thursday
174 | (
175 | "1924-12-14",
176 | "old",
177 | {
178 | "fasting_season_index": "10",
179 | "fasting_laymen_index": "4",
180 | "fasting_monks_index": "4",
181 | },
182 | ), # Saturday
183 | (
184 | "1924-12-15",
185 | "old",
186 | {
187 | "fasting_season_index": "10",
188 | "fasting_laymen_index": "4",
189 | "fasting_monks_index": "4",
190 | },
191 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Sunday
192 | (
193 | "1924-12-17",
194 | "old",
195 | {
196 | "fasting_season_index": "10",
197 | "fasting_laymen_index": "4",
198 | "fasting_monks_index": "4",
199 | },
200 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Tuesday
201 | (
202 | "1924-12-20",
203 | "old",
204 | {
205 | "fasting_season_index": "10",
206 | "fasting_laymen_index": "4",
207 | "fasting_monks_index": "4",
208 | },
209 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Friday
210 | (
211 | "1924-12-21",
212 | "old",
213 | {
214 | "fasting_season_index": "10",
215 | "fasting_laymen_index": "4",
216 | "fasting_monks_index": "4",
217 | },
218 | ), # Saturday
219 | (
220 | "1924-12-22",
221 | "old",
222 | {
223 | "fasting_season_index": "10",
224 | "fasting_laymen_index": "4",
225 | "fasting_monks_index": "4",
226 | },
227 | ), # Sunday
228 | (
229 | "1924-12-24",
230 | "old",
231 | {
232 | "fasting_season_index": "10",
233 | "fasting_laymen_index": "5",
234 | "fasting_monks_index": "5",
235 | },
236 | ), # STRICT_FAST, Tuesday
237 | (
238 | "2099-11-15",
239 | "old",
240 | {
241 | "fasting_season_index": "10",
242 | "fasting_laymen_index": "4",
243 | "fasting_monks_index": "4",
244 | },
245 | ), # Saturday
246 | (
247 | "2099-11-16",
248 | "old",
249 | {
250 | "fasting_season_index": "10",
251 | "fasting_laymen_index": "4",
252 | "fasting_monks_index": "4",
253 | },
254 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Sunday
255 | (
256 | "2099-11-20",
257 | "old",
258 | {
259 | "fasting_season_index": "10",
260 | "fasting_laymen_index": "4",
261 | "fasting_monks_index": "4",
262 | },
263 | ), # Thursday
264 | (
265 | "2099-11-21",
266 | "old",
267 | {
268 | "fasting_season_index": "10",
269 | "fasting_laymen_index": "3",
270 | "fasting_monks_index": "3",
271 | },
272 | ), # FISH_ALLOWED, Friday
273 | (
274 | "2099-11-22",
275 | "old",
276 | {
277 | "fasting_season_index": "10",
278 | "fasting_laymen_index": "3",
279 | "fasting_monks_index": "3",
280 | },
281 | ), # Saturday
282 | (
283 | "2099-11-23",
284 | "old",
285 | {
286 | "fasting_season_index": "10",
287 | "fasting_laymen_index": "3",
288 | "fasting_monks_index": "3",
289 | },
290 | ), # Sunday
291 | (
292 | "2099-11-25",
293 | "old",
294 | {
295 | "fasting_season_index": "10",
296 | "fasting_laymen_index": "4",
297 | "fasting_monks_index": "4",
298 | },
299 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Tuesday
300 | (
301 | "2099-11-30",
302 | "old",
303 | {
304 | "fasting_season_index": "10",
305 | "fasting_laymen_index": "3",
306 | "fasting_monks_index": "3",
307 | },
308 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Sunday
309 | (
310 | "2099-12-4",
311 | "old",
312 | {
313 | "fasting_season_index": "10",
314 | "fasting_laymen_index": "4",
315 | "fasting_monks_index": "4",
316 | },
317 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Thursday
318 | (
319 | "2099-12-5",
320 | "old",
321 | {
322 | "fasting_season_index": "10",
323 | "fasting_laymen_index": "4",
324 | "fasting_monks_index": "4",
325 | },
326 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Friday
327 | (
328 | "2099-12-6",
329 | "old",
330 | {
331 | "fasting_season_index": "10",
332 | "fasting_laymen_index": "3",
333 | "fasting_monks_index": "3",
334 | },
335 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
336 | (
337 | "2099-12-7",
338 | "old",
339 | {
340 | "fasting_season_index": "10",
341 | "fasting_laymen_index": "3",
342 | "fasting_monks_index": "3",
343 | },
344 | ), # Sunday
345 | (
346 | "2099-12-9",
347 | "old",
348 | {
349 | "fasting_season_index": "10",
350 | "fasting_laymen_index": "4",
351 | "fasting_monks_index": "4",
352 | },
353 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Tuesday
354 | (
355 | "2099-12-12",
356 | "old",
357 | {
358 | "fasting_season_index": "10",
359 | "fasting_laymen_index": "4",
360 | "fasting_monks_index": "4",
361 | },
362 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Friday
363 | (
364 | "2099-12-13",
365 | "old",
366 | {
367 | "fasting_season_index": "10",
368 | "fasting_laymen_index": "4",
369 | "fasting_monks_index": "4",
370 | },
371 | ), # Saturday
372 | (
373 | "2099-12-14",
374 | "old",
375 | {
376 | "fasting_season_index": "10",
377 | "fasting_laymen_index": "4",
378 | "fasting_monks_index": "4",
379 | },
380 | ), # Sunday
381 | (
382 | "2099-12-15",
383 | "old",
384 | {
385 | "fasting_season_index": "10",
386 | "fasting_laymen_index": "4",
387 | "fasting_monks_index": "4",
388 | },
389 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Monday
390 | (
391 | "2099-12-17",
392 | "old",
393 | {
394 | "fasting_season_index": "10",
395 | "fasting_laymen_index": "4",
396 | "fasting_monks_index": "4",
397 | },
398 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Wednesday
399 | (
400 | "2099-12-20",
401 | "old",
402 | {
403 | "fasting_season_index": "10",
404 | "fasting_laymen_index": "4",
405 | "fasting_monks_index": "4",
406 | },
407 | ), # WINE_AND_OLIVE_OIL_ALLOWED, Saturday
408 | (
409 | "2099-12-21",
410 | "old",
411 | {
412 | "fasting_season_index": "10",
413 | "fasting_laymen_index": "4",
414 | "fasting_monks_index": "4",
415 | },
416 | ), # Sunday
417 | (
418 | "2024-12-24",
419 | "old",
420 | {
421 | "fasting_season_index": "10",
422 | "fasting_laymen_index": "5",
423 | "fasting_monks_index": "5",
424 | },
425 | ), # STRICT_FAST, Monday
426 | (
427 | "2099-12-24",
428 | "old",
429 | {
430 | "fasting_season_index": "10",
431 | "fasting_laymen_index": "5",
432 | "fasting_monks_index": "5",
433 | },
434 | ), # STRICT_FAST, Wednesday
435 | (
436 | "2027-12-24",
437 | "old",
438 | {
439 | "fasting_season_index": "10",
440 | "fasting_laymen_index": "5",
441 | "fasting_monks_index": "5",
442 | },
443 | ), # STRICT_FAST, Thursday
444 | (
445 | "2033-12-24",
446 | "old",
447 | {
448 | "fasting_season_index": "10",
449 | "fasting_laymen_index": "5",
450 | "fasting_monks_index": "5",
451 | },
452 | ), # STRICT_FAST, Friday
453 | (
454 | "2028-12-24",
455 | "old",
456 | {
457 | "fasting_season_index": "10",
458 | "fasting_laymen_index": "4",
459 | "fasting_monks_index": "4",
460 | },
461 | ), # STRICT_FAST, Saturday
462 | (
463 | "2029-12-24",
464 | "old",
465 | {
466 | "fasting_season_index": "10",
467 | "fasting_laymen_index": "4",
468 | "fasting_monks_index": "4",
469 | },
470 | ), # STRICT_FAST, Sunday
471 | ]
472 |
473 |
474 | @pytest.mark.parametrize("current_date, calendar_style, expected", test_cases)
475 | def test_get_fasting(current_date: str, calendar_style: str, expected: str) -> None:
476 | """Test for get_fasting."""
477 | assert get_fasting(string_to_date(current_date), calendar_style) == expected
478 |
--------------------------------------------------------------------------------
/ocma_data/core/fasting/logic.py:
--------------------------------------------------------------------------------
1 | """Module for calculating fasting seasons and levels."""
2 |
3 | from datetime import date
4 | from typing import TypedDict
5 |
6 | from ocma_data.constants import Weekdays
7 | from ocma_data.core.fasting.constants import (
8 | FISH_ALLOWED,
9 | STRICT_FAST,
10 | WINE_AND_OLIVE_OIL_ALLOWED,
11 | DateFixed,
12 | DateMovable,
13 | FastingLevels,
14 | FastingSeasons,
15 | PaschaDistanceAfter,
16 | PaschaDistanceBefore,
17 | )
18 | from ocma_data.core.pascha_distance.logic import pascha_distance
19 | from ocma_data.core.weekday.logic import get_weekday
20 | from ocma_data.utils.date_utils import (
21 | is_date_before,
22 | is_date_in_range,
23 | is_date_in_tuple,
24 | )
25 |
26 |
27 | class FastingResult(TypedDict):
28 | """Type for fasting result."""
29 |
30 | fasting_season_index: str
31 | fasting_laymen_index: str
32 | fasting_monks_index: str
33 |
34 |
35 | class FastingContext:
36 | """Context object for fasting calculations."""
37 |
38 | def __init__(self, current_date: date, calendar_style: str):
39 | """Initialize fasting context with date and calendar style."""
40 | self.current_date = current_date
41 | self.calendar_style = calendar_style
42 | self.month_day = f"{current_date.month}-{current_date.day}"
43 | self.pascha_distance = int(pascha_distance(current_date, calendar_style))
44 | self.weekday = int(get_weekday(current_date, calendar_style))
45 |
46 | def comparison_date(self, fasting_season_date: str) -> date:
47 | """Get the comparison date for fixed date ranges."""
48 | current_year = self.current_date.year
49 | if fasting_season_date == DateFixed.TWELVE_DAY_FAST_FREE_AFTER.value:
50 | current_year += 1
51 |
52 | parts = fasting_season_date.split("-")
53 | return date(current_year, int(parts[0]), int(parts[1]))
54 |
55 | def is_weekend(self) -> bool:
56 | """Check if current day is weekend."""
57 | return self.weekday in (Weekdays.SAT.value, Weekdays.SUN.value)
58 |
59 | def is_mon_wed_fri(self) -> bool:
60 | """Check if current day is Monday, Wednesday or Friday."""
61 | return self.weekday in (Weekdays.MON.value, Weekdays.WED.value, Weekdays.FRI.value)
62 |
63 | def create_result(self, season: int, laymen: int, monks: int) -> FastingResult:
64 | """Create a fasting result dictionary."""
65 | return {
66 | "fasting_season_index": str(season),
67 | "fasting_laymen_index": str(laymen),
68 | "fasting_monks_index": str(monks),
69 | }
70 |
71 |
72 | # Strategy functions for each fasting period
73 |
74 |
75 | def check_first_week_triodion(ctx: FastingContext) -> FastingResult | None:
76 | """Check if date is in First Week of Triodion."""
77 | if (
78 | PaschaDistanceBefore.FIRST_WEEK_OF_THE_TRIODION.value
79 | <= ctx.pascha_distance
80 | <= PaschaDistanceAfter.FIRST_WEEK_OF_THE_TRIODION.value
81 | ):
82 | return ctx.create_result(
83 | FastingSeasons.FIRST_WEEK_OF_THE_TRIODION.value,
84 | FastingLevels.NO_FASTING.value,
85 | FastingLevels.DAIRY_PRODUCTS_ALLOWED.value,
86 | )
87 | return None
88 |
89 |
90 | def check_cheesefare_week(ctx: FastingContext) -> FastingResult | None:
91 | """Check if date is in Cheesefare Week."""
92 | if PaschaDistanceBefore.CHEESEFARE_WEEK.value <= ctx.pascha_distance <= PaschaDistanceAfter.CHEESEFARE_WEEK.value:
93 | return ctx.create_result(
94 | FastingSeasons.CHEESEFARE_WEEK.value,
95 | FastingLevels.DAIRY_PRODUCTS_ALLOWED.value,
96 | FastingLevels.DAIRY_PRODUCTS_ALLOWED.value,
97 | )
98 | return None
99 |
100 |
101 | def check_three_day_fast(ctx: FastingContext) -> FastingResult | None:
102 | """Check if date is in Three-Day Fast."""
103 | if PaschaDistanceBefore.THREE_DAY_FAST.value <= ctx.pascha_distance <= PaschaDistanceAfter.THREE_DAY_FAST.value:
104 | level = (
105 | FastingLevels.STRICT_FAST.value
106 | if is_date_in_tuple(ctx.month_day, FISH_ALLOWED)
107 | else FastingLevels.ABSOLUTE_FAST.value
108 | )
109 | return ctx.create_result(FastingSeasons.THREE_DAY_FAST.value, level, level)
110 | return None
111 |
112 |
113 | def check_great_lent(ctx: FastingContext) -> FastingResult | None:
114 | """Check if date is in Great Lent."""
115 | if not (PaschaDistanceBefore.GREAT_LENT.value <= ctx.pascha_distance <= PaschaDistanceAfter.GREAT_LENT.value):
116 | return None
117 |
118 | # Fish allowed days
119 | if is_date_in_tuple(ctx.month_day, FISH_ALLOWED) or ctx.pascha_distance == DateMovable.PALM_SUNDAY.value:
120 | level = FastingLevels.FISH_ALLOWED.value
121 | # Wine and oil allowed days
122 | elif is_date_in_tuple(ctx.month_day, WINE_AND_OLIVE_OIL_ALLOWED) or ctx.is_weekend():
123 | level = FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
124 | # Strict fast days
125 | else:
126 | level = FastingLevels.STRICT_FAST.value
127 |
128 | return ctx.create_result(FastingSeasons.GREAT_LENT.value, level, level)
129 |
130 |
131 | def check_holy_week(ctx: FastingContext) -> FastingResult | None:
132 | """Check if date is in Holy Week."""
133 | if not (PaschaDistanceBefore.HOLY_WEEK.value <= ctx.pascha_distance <= PaschaDistanceAfter.HOLY_WEEK.value):
134 | return None
135 |
136 | is_saturday = ctx.weekday == Weekdays.SAT.value
137 | is_friday = ctx.weekday == Weekdays.FRI.value
138 | fish_allowed = is_date_in_tuple(ctx.month_day, FISH_ALLOWED)
139 |
140 | # Saturday - strict fast
141 | if is_saturday:
142 | level = FastingLevels.STRICT_FAST.value
143 | # Friday - absolute or strict fast
144 | elif is_friday:
145 | level = FastingLevels.STRICT_FAST.value if fish_allowed else FastingLevels.ABSOLUTE_FAST.value
146 | # Other days with fish allowed
147 | elif fish_allowed:
148 | level = FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
149 | # Other days
150 | else:
151 | level = FastingLevels.STRICT_FAST.value
152 |
153 | return ctx.create_result(FastingSeasons.HOLY_WEEK.value, level, level)
154 |
155 |
156 | def check_bright_week(ctx: FastingContext) -> FastingResult | None:
157 | """Check if date is in Bright Week."""
158 | if PaschaDistanceBefore.BRIGHT_WEEK.value <= ctx.pascha_distance <= PaschaDistanceAfter.BRIGHT_WEEK.value:
159 | return ctx.create_result(
160 | FastingSeasons.BRIGHT_WEEK.value, FastingLevels.NO_FASTING.value, FastingLevels.DAIRY_PRODUCTS_ALLOWED.value
161 | )
162 | return None
163 |
164 |
165 | def check_week_holy_spirit(ctx: FastingContext) -> FastingResult | None:
166 | """Check if date is in Week of the Holy Spirit."""
167 | if (
168 | PaschaDistanceBefore.WEEK_OF_THE_HOLY_SPIRIT.value
169 | <= ctx.pascha_distance
170 | <= PaschaDistanceAfter.WEEK_OF_THE_HOLY_SPIRIT.value
171 | ):
172 | return ctx.create_result(
173 | FastingSeasons.WEEK_OF_THE_HOLY_SPIRIT.value,
174 | FastingLevels.NO_FASTING.value,
175 | FastingLevels.DAIRY_PRODUCTS_ALLOWED.value,
176 | )
177 | return None
178 |
179 |
180 | def check_apostles_fast(ctx: FastingContext) -> FastingResult | None:
181 | """Check if date is in Apostles' Fast."""
182 | if not (
183 | ctx.pascha_distance >= PaschaDistanceBefore.APOSTLES_FAST.value
184 | and is_date_before(ctx.current_date, ctx.comparison_date(DateFixed.APOSTLES_FAST.value))
185 | ):
186 | return None
187 |
188 | # Fish allowed days
189 | if is_date_in_tuple(ctx.month_day, FISH_ALLOWED):
190 | level = FastingLevels.FISH_ALLOWED.value
191 | # Weekend
192 | elif ctx.is_weekend():
193 | before_baptist = is_date_before(ctx.current_date, ctx.comparison_date(DateFixed.NATIVITY_OF_THE_BAPTIST.value))
194 | level = FastingLevels.FISH_ALLOWED.value if before_baptist else FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
195 | # Monday, Wednesday, Friday
196 | elif ctx.is_mon_wed_fri():
197 | level = (
198 | FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
199 | if is_date_in_tuple(ctx.month_day, WINE_AND_OLIVE_OIL_ALLOWED)
200 | else FastingLevels.STRICT_FAST.value
201 | )
202 | # Other days
203 | else:
204 | level = FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
205 |
206 | return ctx.create_result(FastingSeasons.APOSTLES_FAST.value, level, level)
207 |
208 |
209 | def check_dormition_fast(ctx: FastingContext) -> FastingResult | None:
210 | """Check if date is in Dormition Fast."""
211 | if not is_date_in_range(
212 | ctx.current_date,
213 | ctx.comparison_date(DateFixed.DORMITION_FAST_BEFORE.value),
214 | ctx.comparison_date(DateFixed.DORMITION_FAST_AFTER.value),
215 | ):
216 | return None
217 |
218 | # Fish allowed days
219 | if is_date_in_tuple(ctx.month_day, FISH_ALLOWED):
220 | level = FastingLevels.FISH_ALLOWED.value
221 | # Weekend
222 | elif ctx.is_weekend():
223 | level = FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
224 | # Other days
225 | else:
226 | level = FastingLevels.STRICT_FAST.value
227 |
228 | return ctx.create_result(FastingSeasons.DORMITION_FAST.value, level, level)
229 |
230 |
231 | def check_nativity_fast(ctx: FastingContext) -> FastingResult | None:
232 | """Check if date is in Nativity Fast."""
233 | if not is_date_in_range(
234 | ctx.current_date,
235 | ctx.comparison_date(DateFixed.NATIVITY_FAST_BEFORE.value),
236 | ctx.comparison_date(DateFixed.NATIVITY_FAST_AFTER.value),
237 | ):
238 | return None
239 |
240 | is_strict_day = is_date_in_tuple(ctx.month_day, STRICT_FAST)
241 |
242 | # Strict fast days
243 | if is_strict_day:
244 | level = FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value if ctx.is_weekend() else FastingLevels.STRICT_FAST.value
245 | # Fish allowed days
246 | elif is_date_in_tuple(ctx.month_day, FISH_ALLOWED):
247 | level = FastingLevels.FISH_ALLOWED.value
248 | # Weekend
249 | elif ctx.is_weekend():
250 | in_theotokos_range = is_date_in_range(
251 | ctx.current_date,
252 | ctx.comparison_date(DateFixed.THE_ENTRANCE_OF_THE_THEOTOKOS.value),
253 | ctx.comparison_date(DateFixed.SAINT_SPYRIDON.value),
254 | )
255 | level = (
256 | FastingLevels.FISH_ALLOWED.value if in_theotokos_range else FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
257 | )
258 | # Monday, Wednesday, Friday
259 | elif ctx.is_mon_wed_fri():
260 | level = (
261 | FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
262 | if is_date_in_tuple(ctx.month_day, WINE_AND_OLIVE_OIL_ALLOWED)
263 | else FastingLevels.STRICT_FAST.value
264 | )
265 | # Other days
266 | else:
267 | level = FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
268 |
269 | return ctx.create_result(FastingSeasons.NATIVITY_FAST.value, level, level)
270 |
271 |
272 | def check_twelve_day_fast_free(ctx: FastingContext) -> FastingResult | None:
273 | """Check if date is in Twelve-Day Fast-Free period."""
274 | if not is_date_in_range(
275 | ctx.current_date,
276 | ctx.comparison_date(DateFixed.TWELVE_DAY_FAST_FREE_BEFORE.value),
277 | ctx.comparison_date(DateFixed.TWELVE_DAY_FAST_FREE_AFTER.value),
278 | ):
279 | return None
280 |
281 | if is_date_in_tuple(ctx.month_day, STRICT_FAST):
282 | level = FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value if ctx.is_weekend() else FastingLevels.STRICT_FAST.value
283 | return ctx.create_result(FastingSeasons.TWELVE_DAY_FAST_FREE.value, level, level)
284 |
285 | return ctx.create_result(
286 | FastingSeasons.TWELVE_DAY_FAST_FREE.value,
287 | FastingLevels.NO_FASTING.value,
288 | FastingLevels.DAIRY_PRODUCTS_ALLOWED.value,
289 | )
290 |
291 |
292 | def check_regular_season(ctx: FastingContext) -> FastingResult:
293 | """Check regular season fasting rules."""
294 | # Strict fast days
295 | if is_date_in_tuple(ctx.month_day, STRICT_FAST):
296 | level = FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value if ctx.is_weekend() else FastingLevels.STRICT_FAST.value
297 | return ctx.create_result(FastingSeasons.REGULAR_SEASON.value, level, level)
298 |
299 | # Monday, Wednesday, Friday
300 | if ctx.is_mon_wed_fri():
301 | # Special movable feasts (not on Monday)
302 | if ctx.weekday != Weekdays.MON.value and (
303 | is_date_in_tuple(ctx.month_day, FISH_ALLOWED)
304 | or ctx.pascha_distance == DateMovable.MIDFEAST_OF_PENTECOST.value
305 | or ctx.pascha_distance == DateMovable.LEAVETAKING_OF_PASCHA.value
306 | ):
307 | level_lay = level_monk = FastingLevels.FISH_ALLOWED.value
308 | # Wine and oil allowed (not on Monday)
309 | elif ctx.weekday != Weekdays.MON.value and is_date_in_tuple(ctx.month_day, WINE_AND_OLIVE_OIL_ALLOWED):
310 | level_lay = level_monk = FastingLevels.WINE_AND_OLIVE_OIL_ALLOWED.value
311 | # Monday
312 | elif ctx.weekday == Weekdays.MON.value:
313 | level_lay = FastingLevels.NO_FASTING.value
314 | level_monk = FastingLevels.STRICT_FAST.value
315 | # Wednesday and Friday
316 | else:
317 | level_lay = level_monk = FastingLevels.STRICT_FAST.value
318 |
319 | return ctx.create_result(FastingSeasons.REGULAR_SEASON.value, level_lay, level_monk)
320 |
321 | # Other days - no fasting for laypeople
322 | return ctx.create_result(
323 | FastingSeasons.REGULAR_SEASON.value, FastingLevels.NO_FASTING.value, FastingLevels.DAIRY_PRODUCTS_ALLOWED.value
324 | )
325 |
326 |
327 | # Main function with strategy pattern
328 |
329 | FASTING_STRATEGIES = [
330 | check_first_week_triodion,
331 | check_cheesefare_week,
332 | check_three_day_fast,
333 | check_great_lent,
334 | check_holy_week,
335 | check_bright_week,
336 | check_week_holy_spirit,
337 | check_apostles_fast,
338 | check_dormition_fast,
339 | check_nativity_fast,
340 | check_twelve_day_fast_free,
341 | ]
342 |
343 |
344 | def get_fasting(current_date: date, calendar_style: str) -> dict[str, str]:
345 | """Get the fasting_season_index, fasting_laymen_index and fasting_monks_index.
346 |
347 | This function uses a strategy pattern to check each fasting period in order.
348 | The first matching period determines the fasting rules.
349 | """
350 | ctx = FastingContext(current_date, calendar_style)
351 |
352 | # Check special fasting periods in order
353 | for strategy in FASTING_STRATEGIES:
354 | result = strategy(ctx)
355 | if result is not None:
356 | return result
357 |
358 | # Default to regular season
359 | return check_regular_season(ctx)
360 |
--------------------------------------------------------------------------------