-
Notifications
You must be signed in to change notification settings - Fork 29
/
Copy pathcheck_yml_integrity.py
139 lines (116 loc) · 3.51 KB
/
check_yml_integrity.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""
This script checks the ingegrity of the structure and (partially) the data of the eventy.yml YAML file.
@author: Jules Kreuer
@contact: contact@juleskreuer.eu
"""
from datetime import datetime
from schema import Schema, And, Use, Optional, SchemaError
import yaml
FILE_NAME = "events.yml"
UNIQUE_VALUES = {}
DATE_FORMAT = "%d.%m.%Y %H:%M"
def is_date(s: str) -> bool:
"""
Must follow %d.%m.%Y %H:%M format
Eg: 07.03.2024 01:02
"""
try:
datetime.strptime(s, DATE_FORMAT)
except ValueError:
print(f"The date {s} does not follow the format {DATE_FORMAT}")
return False
return True
def is_unique(key, data) -> bool:
"""
Check if `data` for a given `key` is unique across all events.
"""
if key not in UNIQUE_VALUES:
UNIQUE_VALUES[key] = [data]
return True
if data not in UNIQUE_VALUES[key]:
UNIQUE_VALUES[key].append(data)
return True
return False
def is_icon(s: str) -> bool:
valid_icon_names = [
"beer",
"cap",
"clock",
"cocktail",
"dice",
"film",
"food",
"grill",
"hiking",
"home",
"marker",
"route",
"signs",
"snowflake"
]
return s in valid_icon_names
# Schema of a single event
# To check the value for:
# a string use: str
# a non-empty string use: And(str, len),
# a date use: And(str, is_date),
# a unique field use: lambda s: is_unique("KEY NAME", s)
# a list of str use: [str]
#
# To check for optional keys use: Optionale("KEY NAME"): VALUE
REQUIRED_SCHEMA = Schema(
{
Optional("name"): And(str, len),
Optional("cancelled"): bool,
Optional("text"): str,
Optional("info"): str,
"location": str,
"max_participants": int,
Optional("dinos"): bool,
"event_date": {
"start": And(str, is_date),
Optional("end"): And(str, is_date),
"onTime": bool,
},
"registration_date": {
Optional("start"): And(str, is_date),
"end": And(str, is_date),
},
"csv_path": And(str, len, lambda s: is_unique("csv_path", s)),
Optional("form"): {
Optional("breakfast"): bool,
Optional("food"): bool,
Optional("course_required"): bool
},
"icon": And(str, is_icon),
Optional("metas"): [str],
}
)
class UniqueKeyLoader(yaml.SafeLoader):
"""
Load the YAML file, raises an error if a douplicate key is found.
Adapted from https://gist.github.com/pypt/94d747fe5180851196eb
"""
def construct_mapping(self, node, deep=False):
mapping = set()
for key_node, value_node in node.value:
if ":merge" in key_node.tag:
continue
key = self.construct_object(key_node, deep=deep)
if key in mapping:
raise ValueError(f"Duplicate {key!r} key found in YAML.")
mapping.add(key)
return super().construct_mapping(node, deep)
with open(FILE_NAME, "r") as f:
yaml_data = yaml.load(f, Loader=UniqueKeyLoader)
max_key_len = max(len(k) for k in yaml_data["events"].keys()) + 1
num_errors = 0
for key, event in yaml_data["events"].items():
try:
REQUIRED_SCHEMA.validate(event)
print(f" {key.ljust(max_key_len)} valid.")
except SchemaError as e:
num_errors += 1
e = str(e).replace("\n", " ")
print(f" {key.ljust(max_key_len)} Invalid schema. {e}")
exit(num_errors)