Skip to content

Commit 8dd12f9

Browse files
committed
refactor: dodanie testow do akcji oraz refactor kodu samej akcji
fix: dodanie brakujacego clicka
1 parent b8aef2b commit 8dd12f9

24 files changed

+2120
-238
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
.Python
7+
build/
8+
develop-eggs/
9+
dist/
10+
downloads/
11+
eggs/
12+
.eggs/
13+
lib/
14+
lib64/
15+
parts/
16+
sdist/
17+
var/
18+
wheels/
19+
share/python-wheels/
20+
*.egg-info/
21+
.installed.cfg
22+
*.egg
23+
MANIFEST
24+
25+
# Virtual environments
26+
.venv/
27+
venv/
28+
env/
29+
ENV/
30+
env.bak/
31+
venv.bak/
32+
33+
# Testing
34+
.pytest_cache/
35+
.coverage
36+
htmlcov/
37+
.tox/
38+
.nox/
39+
coverage.xml
40+
*.cover
41+
*.py,cover
42+
.hypothesis/
43+
44+
# IDEs and editors
45+
.vscode/
46+
.idea/
47+
*.swp
48+
*.swo
49+
*~
50+
.project
51+
.pydevproject
52+
53+
# OS specific
54+
.DS_Store
55+
.DS_Store?
56+
._*
57+
.Spotlight-V100
58+
.Trashes
59+
ehthumbs.db
60+
Thumbs.db
61+
desktop.ini
62+
63+
# Logs and temporary files
64+
*.log
65+
*.tmp
66+
*.temp
67+
.cache/
68+
69+
# Local configuration
70+
.env
71+
.env.local
72+
.env.*.local
73+
74+
# Lock files (keep uv.lock but ignore others)
75+
# uv.lock - keep this for reproducible builds
76+
poetry.lock
77+
Pipfile.lock

.github/actions/validate-organizations/action.yml

Lines changed: 20 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,20 @@ inputs:
1616
runs:
1717
using: 'composite'
1818
steps:
19-
- name: Set up Python
20-
uses: actions/setup-python@v4
19+
- name: Install uv
20+
uses: astral-sh/setup-uv@v3
2121
with:
22-
python-version: '3.11'
22+
version: "0.4.18"
2323

24-
- name: Cache pip dependencies
25-
uses: actions/cache@v3
26-
with:
27-
path: ~/.cache/pip
28-
key: ${{ runner.os }}-pip-${{ hashFiles('${{ github.action_path }}/requirements.txt') }}
29-
restore-keys: |
30-
${{ runner.os }}-pip-
24+
- name: Set up Python
25+
run: uv python install 3.11
26+
shell: bash
3127

3228
- name: Install dependencies
3329
shell: bash
3430
run: |
35-
pip install -r ${{ github.action_path }}/requirements.txt
31+
cd ${{ github.action_path }}
32+
uv sync --frozen
3633
3734
- name: Validate organizations
3835
shell: bash
@@ -41,8 +38,16 @@ runs:
4138
echo "Files to check: ${{ inputs.files }}"
4239
echo "Organizations dir: ${{ inputs.organizations-dir }}"
4340
ls -la organizations/ || echo "organizations directory not found"
44-
export PYTHONPATH="${{ github.action_path }}:$PYTHONPATH"
45-
python ${{ github.action_path }}/validate.py \
46-
--files "${{ inputs.files }}" \
47-
--organizations-dir "${{ inputs.organizations-dir }}" \
41+
42+
# Convert relative paths to absolute paths from current working directory
43+
REPO_ROOT="$(pwd)"
44+
FILES_ABSOLUTE=""
45+
for file in ${{ inputs.files }}; do
46+
FILES_ABSOLUTE="$FILES_ABSOLUTE $REPO_ROOT/$file"
47+
done
48+
49+
cd ${{ github.action_path }}
50+
uv run python validate.py \
51+
--files "$FILES_ABSOLUTE" \
52+
--organizations-dir "$REPO_ROOT/${{ inputs.organizations-dir }}" \
4853
--slug-field "${{ inputs.slug-field }}"

.github/actions/validate-organizations/krs_puller.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,12 @@
44
"""
55

66
import requests
7-
from typing import Optional, Tuple
7+
from typing import Optional
88

99

1010
class KRSDataPuller:
1111
"""Pulls and validates organization data from Polish KRS registry."""
12-
12+
1313
def __init__(self, krs: str):
1414
self.krs = krs
1515
self.data = self._pull_data()
@@ -19,9 +19,9 @@ def _pull_data(self) -> Optional[dict]:
1919
try:
2020
response = requests.get(
2121
f"https://api-krs.ms.gov.pl/api/krs/OdpisAktualny/{self.krs}?rejestr=S&format=json",
22-
timeout=10
22+
timeout=10,
2323
)
24-
24+
2525
if response.status_code == 200:
2626
try:
2727
return response.json()
@@ -35,7 +35,7 @@ def _pull_data(self) -> Optional[dict]:
3535
raise
3636
else:
3737
raise requests.HTTPError(f"Failed to fetch data for KRS {self.krs}")
38-
38+
3939
except requests.exceptions.RequestException as e:
4040
raise requests.HTTPError(f"Network error fetching KRS {self.krs}: {e}")
4141

@@ -44,7 +44,7 @@ def name(self) -> Optional[str]:
4444
"""Get organization name from KRS data."""
4545
if not self.data:
4646
return None
47-
47+
4848
return (
4949
self.data.get("odpis", {})
5050
.get("dane", {})
@@ -54,10 +54,10 @@ def name(self) -> Optional[str]:
5454
)
5555

5656
@classmethod
57-
def get_organization_data(cls, krs: str) -> Optional['KRSDataPuller']:
57+
def get_organization_data(cls, krs: str) -> Optional["KRSDataPuller"]:
5858
"""
5959
Get organization data from KRS registry.
60-
60+
6161
Returns:
6262
KRSDataPuller instance if successful, None if failed
6363
"""
@@ -69,4 +69,5 @@ def get_organization_data(cls, krs: str) -> Optional['KRSDataPuller']:
6969

7070
class KRSMaintenanceError(Exception):
7171
"""Raised when KRS API is in maintenance mode."""
72+
7273
pass
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[build-system]
2+
requires = ["hatchling"]
3+
build-backend = "hatchling.build"
4+
5+
[project]
6+
name = "organization-validator"
7+
version = "0.1.0"
8+
description = "Validates organization YAML files for conflicts and schema compliance"
9+
requires-python = ">=3.11"
10+
dependencies = [
11+
"PyYAML==6.0.2",
12+
"click==8.1.7",
13+
"requests==2.32.3",
14+
"pytest==8.3.3",
15+
"pytest-mock==3.14.0",
16+
"responses==0.25.3",
17+
"ruff>=0.12.9",
18+
]
19+
20+
[tool.hatch.build.targets.wheel]
21+
packages = ["."]
22+
23+
[tool.pytest.ini_options]
24+
testpaths = ["tests"]
25+
python_files = ["test_*.py"]
26+
python_classes = ["Test*"]
27+
python_functions = ["test_*"]
28+
addopts = ["-v", "--tb=short"]
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
"""
2+
Repository pattern for organization data access.
3+
Separates file I/O from validation logic for better testability.
4+
"""
5+
6+
from abc import ABC, abstractmethod
7+
from pathlib import Path
8+
from typing import Dict, List, Tuple, Optional
9+
import yaml
10+
11+
12+
class OrganizationRepository(ABC):
13+
"""Abstract repository for organization data access."""
14+
15+
@abstractmethod
16+
def load_all_organizations(
17+
self, slug_field: str
18+
) -> Tuple[Dict[str, str], List[str]]:
19+
"""
20+
Load all organizations and return slug to filename mapping with errors.
21+
22+
Args:
23+
slug_field: YAML field name for organization slug
24+
25+
Returns:
26+
Tuple of (slug_to_filename_mapping, errors)
27+
"""
28+
pass
29+
30+
@abstractmethod
31+
def load_organization_data(self, file_path: str) -> Optional[dict]:
32+
"""
33+
Load organization data from a specific file.
34+
35+
Args:
36+
file_path: Path to the organization file
37+
38+
Returns:
39+
Organization data dictionary or None if failed
40+
"""
41+
pass
42+
43+
44+
class FileSystemRepository(OrganizationRepository):
45+
"""File system implementation of organization repository."""
46+
47+
def __init__(self, organizations_dir: str):
48+
self.organizations_dir = Path(organizations_dir)
49+
self.reserved_slugs = {"info", "organizacje", "404"}
50+
51+
def load_all_organizations(
52+
self, slug_field: str
53+
) -> Tuple[Dict[str, str], List[str]]:
54+
"""Load all organization files and return slug to filename mapping with errors."""
55+
slug_to_file = {}
56+
errors = []
57+
58+
if not self.organizations_dir.exists():
59+
return slug_to_file, errors
60+
61+
yaml_files = list(self.organizations_dir.glob("*.yaml")) + list(
62+
self.organizations_dir.glob("*.yml")
63+
)
64+
for yaml_file in yaml_files:
65+
try:
66+
with open(yaml_file, "r", encoding="utf-8") as f:
67+
data = yaml.safe_load(f)
68+
if data and slug_field in data:
69+
slug = data[slug_field]
70+
if slug in slug_to_file:
71+
errors.append(
72+
f"Duplikat {slug_field} '{slug}' znaleziony w {yaml_file.name} i {slug_to_file[slug]}"
73+
)
74+
else:
75+
slug_to_file[slug] = str(
76+
self.organizations_dir / yaml_file.name
77+
)
78+
except Exception as e:
79+
errors.append(f"Błąd wczytywania pliku {yaml_file.name}: {e}")
80+
81+
return slug_to_file, errors
82+
83+
def load_organization_data(self, file_path: str) -> Optional[dict]:
84+
"""Load organization data from a specific file."""
85+
try:
86+
full_path = Path(file_path)
87+
if not full_path.exists():
88+
return None
89+
90+
with open(full_path, "r", encoding="utf-8") as f:
91+
return yaml.safe_load(f)
92+
except Exception:
93+
return None

.github/actions/validate-organizations/requirements.txt

Lines changed: 0 additions & 3 deletions
This file was deleted.

0 commit comments

Comments
 (0)