Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Move logic to core module #45

Merged
merged 3 commits into from
Dec 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/release-please-config.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
"packages": {
".": {
"release-type": "python",
"skip-github-release": true,
"include-v-in-tag": false,
"include-component-in-tag": false,
"always-update": true
Expand Down
34 changes: 7 additions & 27 deletions .github/workflows/pypi-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,14 @@ permissions:

jobs:

build:
name: Build distribution
publish-to-testpypi:
name: Publish to TestPyPI
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/dac7
permissions:
id-token: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4

Expand All @@ -27,31 +32,6 @@ jobs:
- name: Package project
run: make build

- name: Store the distribution
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4
with:
name: test-python-package-distributions
path: dist/

publish-to-testpypi:
name: Publish to TestPyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/p/dac7

permissions:
id-token: write

steps:
- name: Download the distribution
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
with:
name: test-python-package-distributions
path: dist/

- name: Publish distribution to TestPyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
Expand Down
32 changes: 7 additions & 25 deletions .github/workflows/pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,14 @@ permissions:

jobs:

build:
name: Build distribution
publish-to-pypi:
name: Publish to PyPI
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/dac7
permissions:
id-token: write
steps:
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4

Expand All @@ -28,28 +33,5 @@ jobs:
- name: Package project
run: make build

- name: Store the distribution
uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4
with:
name: python-package-distributions
path: dist/

publish-to-pypi:
name: Publish to PyPI
needs:
- build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/dac7
permissions:
id-token: write
steps:
- name: Download the distribution
uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16 # v4
with:
name: python-package-distributions
path: dist/

- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
27 changes: 27 additions & 0 deletions .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,37 @@ jobs:
release-please:
name: Release PR
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/p/dac7
permissions:
id-token: write
steps:
- uses: googleapis/release-please-action@7987652d64b4581673a76e33ad5e98e3dd56832f # v4
id: release
with:
# this is a built-in strategy in release-please, see "Action Inputs"
# for more options
config-file: .github/release-please-config.json
manifest-file: .github/release-please-manifest.json

- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4
if: ${{ steps.release.outputs.release_created }}

- name: Install poetry
run: pipx install --force poetry==1.8.5
if: ${{ steps.release.outputs.release_created }}

- uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5
with:
python-version: '3.11'
cache: 'poetry'
if: ${{ steps.release.outputs.release_created }}

- name: Package project
run: make build
if: ${{ steps.release.outputs.release_created }}

- name: Publish distribution to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
if: ${{ steps.release.outputs.release_created }}
131 changes: 131 additions & 0 deletions cli/src/dac7/core.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
import gzip
import json

from datetime import UTC
from datetime import datetime
from pathlib import Path
from typing import Any
from typing import Optional
from typing import cast

from dac7.constants import Env
from dac7.constants import FileFormat
from dac7.encryption import EncryptionService
from dac7.encryption import KeyInfo
from dac7.models.flat import Declaration
from dac7.models.flat import PlatformOperator
from dac7.naming import build_filename

import xmlschema

# from dateutil.parser import parser

DAC7_SCHEMA = Path(__file__).parent / "schemas" / "DPIXML_v1.1-fr1.xsd"


def build_filename_from_xml_data(
xml_data: str,
file_format: FileFormat = FileFormat.XML,
schema_path: Path = DAC7_SCHEMA,
) -> str:
"""
Return the expected name of the declaration file.
"""

schema = xmlschema.XMLSchema10(schema_path)

schema.validate(xml_data)

xml_declaration = cast(dict[str, dict[str, str]], schema.decode(xml_data))
message_ref_id = xml_declaration["dpi:MessageSpec"]["dpi:MessageRefId"]
timestamp = xml_declaration["dpi:MessageSpec"]["dpi:Timestamp"]

expected_filename = build_filename(message_ref_id=message_ref_id, timestamp=timestamp)

return f"{expected_filename}.{file_format.value.lower()}"


def build_json(
env: Env,
declaration_id: int,
fiscal_year: int,
platform_path: Path,
other_platforms_path: Optional[Path] = None,
entity_sellers_path: Optional[Path] = None,
individual_sellers_path: Optional[Path] = None,
timestamp: Optional[datetime] = None,
) -> str:
"""
Build a XML or JSON declaration from simple, flat JSON files.

The expected schemas for the JSON input files are available, see: dac7 schemas build --help.
"""

platform_operator = PlatformOperator.model_validate_json(platform_path.read_text())

other_platform_data = load_json(other_platforms_path, default={})
entity_sellers_data = load_json(entity_sellers_path, default=[])
individual_sellers_data = load_json(individual_sellers_path, default=[])

declaration = Declaration(
fiscal_year=fiscal_year,
declaration_id=declaration_id,
timestamp=timestamp or get_timestamp(),
platform_operator=platform_operator,
other_platform_operators=other_platform_data,
reportable_entity_sellers=entity_sellers_data,
reportable_individual_sellers=individual_sellers_data,
env=env,
)

dpi_declaration = declaration.get_dpi()

dpi_data = dpi_declaration.model_dump(by_alias=True, exclude_defaults=True, mode="json")
dpi_json = json.dumps(dpi_data, indent=4, ensure_ascii=True)
return dpi_json.strip()


def encrypt_data(
env: Env,
input_data: bytes,
compression_requested: bool = False,
) -> bytes:
"""
Encrypt a DAC7 file, optionally after GZIP compression.

Requires GnuPG to be installed.
"""

# Compress

if compression_requested:
input_data = gzip.compress(input_data)

# Encrypt

key_info = KeyInfo.for_env(env)
service = EncryptionService(key_info=key_info)

return service.encrypt_data(input_data)


def get_timestamp() -> datetime:
return datetime.now(UTC).replace(tzinfo=None)


def load_json(path: Optional[Path], default: Any) -> Any:
if path is None:
return default
return json.loads(path.read_text())


def json_to_xml(json_data: str, schema_path: Path) -> str:
xml_schema = xmlschema.XMLSchema10(schema_path)

xml_data = xmlschema.from_json(json_data, schema=xml_schema, converter=xmlschema.UnorderedConverter)
xml_content: str = xmlschema.etree_tostring( # type: ignore[assignment]
xml_data, # type: ignore[arg-type]
namespaces={"dpi": "urn:oecd:ties:dpi", "stf": "urn:oecd:ties:dpistf"},
encoding="unicode",
)
return f'<?xml version="1.0" encoding="UTF-8"?>\n{xml_content}\n'
Loading
Loading