Feat(Fortune): More json file checker #57

Merged
tobiichi3227 merged 3 commits from feat/more-checkers into main 2025-02-25 16:23:14 +00:00
2 changed files with 177 additions and 0 deletions
Showing only changes of commit 6ba6ac3c41 - Show all commits

View File

@@ -34,3 +34,11 @@ repos:
files: fortune_generator/json/static_special.json
types: [json]
pass_filenames: false
- id: check-fortune
name: check-fortune
entry: python3 scripts/check-fortune.py fortune_generator/json/fortune.json
language: python
files: fortune_generator/json/fortune.json
types: [json]
pass_filenames: false

169
scripts/check-fortune.py Normal file
View File

@@ -0,0 +1,169 @@
#!/bin/python3
import json
import logging
import argparse
import collections
args_parser = argparse.ArgumentParser(description="fortune checker")
args_parser.add_argument("path", type=str, help="event json file path")
args = args_parser.parse_args()
errors: dict[tuple[str, int], list[str]] = collections.defaultdict(list)
good_fortunes: list[dict] = None
bad_fortunes: list[dict] = None
all_fortunes = None
try:
with open(args.path) as f:
all_fortunes = json.loads(f.read())
except json.JSONDecodeError:
print(f"`{args.path}` json syntax error.")
exit(-1)
except FileNotFoundError:
print(f"`{args.path}` not found.")
print("Please contact developer to solve this problem.")
exit(-1)
if not isinstance(all_fortunes, dict):
print(f"`{args.path}` should contain a dict")
exit(-1)
try:
good_fortunes = all_fortunes["goodFortunes"]
except KeyError:
print(f"`{args.path}` should contain `goodFortunes`")
if not isinstance(good_fortunes, list):
print("`goodFortunes` should be a list.")
try:
bad_fortunes = all_fortunes["badFortunes"]
except KeyError:
print(f"`{args.path}` should contain `badFortunes`")
if not isinstance(bad_fortunes, list):
print("`badFortunes` should be a list.")
def require_field_check(
obj: dict,
fortune_idx: tuple[str, int],
fields: list[tuple[str, type]],
required_field: str = "",
) -> bool:
"""
Validates the presence and types of required fields in a given object.
Args:
obj (dict): The object (dictionary) to validate.
fortune_idx (tuple[str, int]): The index of the fortune for associating validation errors.
fields (list[tuple[str, type]]): A list of tuples where each tuple contains a field name and its expected type.
required_field (str, optional): An optional prefix for error messages to indicate a higher-level required field. Defaults to "".
Returns:
bool: True if all required fields are present and have the correct types, otherwise False.
Validation Rules:
- If a required field is missing, an error message is recorded.
- If a field is present but its type does not match the expected type, an error message is recorded.
- The `required_field` parameter, if provided, is prepended to error messages for context.
"""
error_found = False
for field_name, field_type in fields:
if field_name not in obj:
error_found = True
msg = ""
if required_field != "":
msg = f"`{required_field}` "
msg += f"missing `{field_name}`."
errors[fortune_idx].append(msg)
elif not isinstance(obj[field_name], field_type):
error_found = True
errors[fortune_idx].append(
f"`{field_name}` should be a `{field_type}` type."
)
if error_found:
return False
return True
fortune_names = set()
def check_fortune(fortune, idx: tuple[str, int]):
if not isinstance(fortune, dict):
errors[idx].append("fortune should be a dict.")
return False
if not require_field_check(fortune, idx, [
("event", str),
("description", list)
]):
return False
fortune_name = fortune["event"]
if fortune_name in fortune_names:
errors[idx].append(f"fortune `{fortune_name}` already exists.")
if not fortune_name:
errors[idx].append("fortune name should not be empty.")
if not fortune["description"]:
errors[idx].append("fortune description should not be empty.")
return False
descriptions = set()
for desc in fortune["description"]:
if not isinstance(desc, str):
errors[idx].append(f"fortune description {desc} should be a string.")
continue
if not desc:
errors[idx].append(f"fortune description {desc} should not be empty.")
continue
if desc in descriptions:
errors[idx].append(f"fortune description {desc} already exists.")
continue
else:
descriptions.add(desc)
fortune_names.add(fortune_name)
return True
if good_fortunes:
for idx, fortune in enumerate(good_fortunes):
check_fortune(fortune, ("goodFortunes", idx))
fortune_names.clear()
if bad_fortunes:
for idx, fortune in enumerate(bad_fortunes):
check_fortune(fortune, ("badFortunes", idx))
if errors:
logging.error(args.path)
for idx, error_msgs in errors.items():
fortunes = None
if idx[0] == "goodFortunes":
fortunes = good_fortunes
elif idx[0] == "badFortunes":
fortunes = bad_fortunes
if not fortunes:
continue
logging.error(
json.dumps(
fortunes[idx[1]], indent=4, ensure_ascii=False
)
)
for msg in error_msgs:
logging.error(msg)
exit(-1)