cup-check 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cup_check/__init__.py +14 -0
- cup_check/models.py +33 -0
- cup_check/validator.py +90 -0
- cup_check-0.2.0.dist-info/METADATA +42 -0
- cup_check-0.2.0.dist-info/RECORD +6 -0
- cup_check-0.2.0.dist-info/WHEEL +4 -0
cup_check/__init__.py
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"""Validazione formale locale dei Codici Unici di Progetto."""
|
|
2
|
+
|
|
3
|
+
from cup_check.models import Outcome, Rule, ValidationResult, Warning
|
|
4
|
+
from cup_check.validator import normalize_cup, validate_format, validate_many
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"Outcome",
|
|
8
|
+
"Rule",
|
|
9
|
+
"ValidationResult",
|
|
10
|
+
"Warning",
|
|
11
|
+
"normalize_cup",
|
|
12
|
+
"validate_format",
|
|
13
|
+
"validate_many",
|
|
14
|
+
]
|
cup_check/models.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from enum import StrEnum
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class Outcome(StrEnum):
|
|
8
|
+
INVALIDO_FORMATO = "INVALIDO_FORMATO"
|
|
9
|
+
FORMATO_VALIDO_DA_VERIFICARE = "FORMATO_VALIDO_DA_VERIFICARE"
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class Rule(StrEnum):
|
|
13
|
+
R0 = "R0"
|
|
14
|
+
R1 = "R1"
|
|
15
|
+
R2 = "R2"
|
|
16
|
+
R3 = "R3"
|
|
17
|
+
R4 = "R4"
|
|
18
|
+
R5 = "R5"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class Warning(StrEnum):
|
|
22
|
+
N1 = "N1"
|
|
23
|
+
N2 = "N2"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True)
|
|
27
|
+
class ValidationResult:
|
|
28
|
+
input_row: int | None
|
|
29
|
+
raw_value: str
|
|
30
|
+
normalized_value: str
|
|
31
|
+
outcome: Outcome
|
|
32
|
+
failed_rules: tuple[Rule, ...]
|
|
33
|
+
warnings: tuple[Warning, ...]
|
cup_check/validator.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import re
|
|
4
|
+
from collections.abc import Iterable
|
|
5
|
+
from datetime import UTC, datetime
|
|
6
|
+
|
|
7
|
+
from cup_check.models import Outcome, Rule, ValidationResult, Warning
|
|
8
|
+
|
|
9
|
+
_ALLOWED_CHARS = re.compile(r"^[A-Z0-9]*$")
|
|
10
|
+
_FIRST_POSITION = re.compile(r"^[A-Z]")
|
|
11
|
+
_TWO_DIGIT_YEAR = re.compile(r"^\d{2}$")
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def normalize_cup(value: object) -> str:
|
|
15
|
+
return str("" if value is None else value).strip().upper()
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def validate_format(
|
|
19
|
+
value: object,
|
|
20
|
+
input_row: int | None = None,
|
|
21
|
+
*,
|
|
22
|
+
current_year: int | None = None,
|
|
23
|
+
) -> ValidationResult:
|
|
24
|
+
raw_value = str("" if value is None else value)
|
|
25
|
+
trimmed_value = raw_value.strip()
|
|
26
|
+
normalized_value = normalize_cup(raw_value)
|
|
27
|
+
failed_rules: list[Rule] = []
|
|
28
|
+
warnings = _normalization_warnings(raw_value, trimmed_value, normalized_value)
|
|
29
|
+
|
|
30
|
+
if len(trimmed_value) == 0:
|
|
31
|
+
return ValidationResult(
|
|
32
|
+
input_row=input_row,
|
|
33
|
+
raw_value=raw_value,
|
|
34
|
+
normalized_value=normalized_value,
|
|
35
|
+
outcome=Outcome.INVALIDO_FORMATO,
|
|
36
|
+
failed_rules=(Rule.R0,),
|
|
37
|
+
warnings=warnings,
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
if len(normalized_value) != 15:
|
|
41
|
+
failed_rules.append(Rule.R1)
|
|
42
|
+
|
|
43
|
+
if _ALLOWED_CHARS.fullmatch(normalized_value) is None:
|
|
44
|
+
failed_rules.append(Rule.R2)
|
|
45
|
+
|
|
46
|
+
if _FIRST_POSITION.match(normalized_value) is None:
|
|
47
|
+
failed_rules.append(Rule.R3)
|
|
48
|
+
|
|
49
|
+
year_token = normalized_value[4:6]
|
|
50
|
+
two_digit_year = (current_year if current_year is not None else datetime.now(UTC).year) % 100
|
|
51
|
+
if _TWO_DIGIT_YEAR.fullmatch(year_token) is None or int(year_token) > two_digit_year:
|
|
52
|
+
failed_rules.append(Rule.R4)
|
|
53
|
+
|
|
54
|
+
if not (len(normalized_value) > 3 and "A" <= normalized_value[3] <= "Z"):
|
|
55
|
+
failed_rules.append(Rule.R5)
|
|
56
|
+
|
|
57
|
+
return ValidationResult(
|
|
58
|
+
input_row=input_row,
|
|
59
|
+
raw_value=raw_value,
|
|
60
|
+
normalized_value=normalized_value,
|
|
61
|
+
outcome=(
|
|
62
|
+
Outcome.FORMATO_VALIDO_DA_VERIFICARE
|
|
63
|
+
if len(failed_rules) == 0
|
|
64
|
+
else Outcome.INVALIDO_FORMATO
|
|
65
|
+
),
|
|
66
|
+
failed_rules=tuple(failed_rules),
|
|
67
|
+
warnings=warnings,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def validate_many(
|
|
72
|
+
values: Iterable[object],
|
|
73
|
+
*,
|
|
74
|
+
current_year: int | None = None,
|
|
75
|
+
) -> tuple[ValidationResult, ...]:
|
|
76
|
+
return tuple(
|
|
77
|
+
validate_format(value, input_row=index, current_year=current_year)
|
|
78
|
+
for index, value in enumerate(values, start=1)
|
|
79
|
+
)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _normalization_warnings(
|
|
83
|
+
raw_value: str, trimmed_value: str, normalized_value: str
|
|
84
|
+
) -> tuple[Warning, ...]:
|
|
85
|
+
warnings = []
|
|
86
|
+
if raw_value != trimmed_value:
|
|
87
|
+
warnings.append(Warning.N1)
|
|
88
|
+
if trimmed_value != normalized_value:
|
|
89
|
+
warnings.append(Warning.N2)
|
|
90
|
+
return tuple(warnings)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: cup-check
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Validazione formale locale dei Codici Unici di Progetto (CUP).
|
|
5
|
+
Project-URL: Homepage, https://github.com/ale-saglia/cup-check
|
|
6
|
+
Project-URL: Repository, https://github.com/ale-saglia/cup-check
|
|
7
|
+
Project-URL: Issues, https://github.com/ale-saglia/cup-check/issues
|
|
8
|
+
Author: Alessandro Saglia
|
|
9
|
+
License-Expression: EUPL-1.2
|
|
10
|
+
Keywords: codice unico progetto,cup,opencup,validazione
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: License :: OSI Approved :: European Union Public Licence 1.2 (EUPL 1.2)
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
17
|
+
Requires-Python: >=3.12
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# cup-check
|
|
21
|
+
|
|
22
|
+
Libreria Python per validare localmente il formato dei Codici Unici di Progetto (CUP).
|
|
23
|
+
|
|
24
|
+
La verifica e solo formale: un CUP con formato valido viene restituito come
|
|
25
|
+
`FORMATO_VALIDO_DA_VERIFICARE`, non come CUP esistente.
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from cup_check import validate_format
|
|
29
|
+
|
|
30
|
+
result = validate_format("G17H03000130001")
|
|
31
|
+
|
|
32
|
+
print(result.outcome)
|
|
33
|
+
print(result.failed_rules)
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Per validare piu valori:
|
|
37
|
+
|
|
38
|
+
```python
|
|
39
|
+
from cup_check import validate_many
|
|
40
|
+
|
|
41
|
+
results = validate_many(["G17H03000130001", "117H03000130001"])
|
|
42
|
+
```
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
cup_check/__init__.py,sha256=ZkUNNXOVt7d6edAt1zKUrpxQNAbnmirRsPpaIM7Rtio,358
|
|
2
|
+
cup_check/models.py,sha256=T-uZUTMh95gyqYmTWNjr7Ah0dhq1fPElIuVazziBkOE,601
|
|
3
|
+
cup_check/validator.py,sha256=IPmmEaynv2oBGki6jTHiI3BVMUdd-xrNkkPujZmT5b4,2729
|
|
4
|
+
cup_check-0.2.0.dist-info/METADATA,sha256=l5szInRdZTlvHcfQ7BkwgciR-Sb3Zjj3c0UFx9sxCl4,1364
|
|
5
|
+
cup_check-0.2.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
|
|
6
|
+
cup_check-0.2.0.dist-info/RECORD,,
|