diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000000..6deafc2617 --- /dev/null +++ b/.flake8 @@ -0,0 +1,2 @@ +[flake8] +max-line-length = 120 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4828463213..608233765e 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -44,15 +44,17 @@ repos: - id: check_xcstrings_stale_entries name: Check .xcstrings files for stale entries description: This hook checks .xcstrings files for stale entries - entry: Tools/Hooks/check_xcstrings_stale_entries.sh - language: script + entry: python3 Tools/Hooks/check_xcstrings_stale_entries.py + language: python + additional_dependencies: ["pygments"] files: '.*\.xcstrings' - id: check_xcstrings_unusual_terminators name: Check .xcstrings files for unusual terminators description: This hook checks .xcstrings files for unusual terminators - entry: Tools/Hooks/check_xcstrings_unusual_terminators.sh - language: script + entry: python3 Tools/Hooks/check_xcstrings_unusual_terminators.py + language: python + additional_dependencies: ["pygments"] files: '.*\.xcstrings' - id: check_yaml_definitions_professions diff --git a/Specs/jtd/tests/activity.yml b/Specs/jtd/tests/activity.yml deleted file mode 100644 index a1db126b5f..0000000000 --- a/Specs/jtd/tests/activity.yml +++ /dev/null @@ -1,145 +0,0 @@ -# Leka - iOS Monorepo -# Copyright APF France handicap -# SPDX-License-Identifier: Apache-2.0 - -uuid: 5A670B59EB214A6AA11AB39D64D63990 -name: contentkit-specs-jtd-tests-activity_good - -status: published - -authors: - - leka_at_apf_france_handicap - - aurore_kiesler - -skills: - - skill_one - - skill_two - - skill_three - -hmi: - - robot - - magic_cards - - tablet_robot - -tags: - - tag_one - - tag_two - - tag_three - -locales: - - en_US - - fr_FR - -l10n: - - locale: fr_FR - details: - icon: name_of_the_activity-icon-fr_FR.svg - - title: Le titre/nom de l'activité - subtitle: Le sous-titre de l'activité - - description: | - Courte description de l'activité sur plusieurs lignes - Si besoin! - - Et en markdown **simple** seulement - - instructions: | - ## Longues instructions en markdown plus complexe si on veut - - Lorem markdownum recepta avidum, missa de quam patientia, antris: cum defuit, - Titan repetemus nomine, ignare. Quod ad aura, et non quod vidisse utque ulla: - - - Pro inposuit tibi orsa tum artes ferox - - Acmon plausu qua agrestum situs virgo in - - Vacuus a pendens rostro non si pharetrae - - Haeremusque quos auxiliaris coniunx - - Repulsa impediunt munera teneri fallebat - - Bracchia frustra telo Iovis faucibus casus - - - locale: en_US - details: - icon: name_of_the_activity-icon-en_US.svg - - title: The title/name of the activity - subtitle: The subtitle of the activity - - description: | - Short description of the activity on several lines - If needed! - - And in **simple** markdown only - - instructions: | - ## Long instructions in more complex markdown if we want - - Lorem markdownum recepta avidum, missa de quam patientia, antris: cum defuit, - Titan repetemus nomine, ignare. Quod ad aura, et non quod vidisse utque ulla: - - - Pro inposuit tibi orsa tum artes ferox - - Acmon plausu qua agrestum situs virgo in - - Vacuus a pendens rostro non si pharetrae - - Haeremusque quos auxiliaris coniunx - - Repulsa impediunt munera teneri fallebat - - Bracchia frustra telo Iovis faucibus casus - -gameengine: - shuffle_exercises: true - shuffle_sequences: true - -exercises: - - group: - - instructions: - - locale: fr_FR - value: Place les instruments dans les bonnes familles d'instruments - - locale: en_US - value: Place the instruments in the right instrument families - interface: dragAndDropIntoZones - gameplay: findTheRightAnswers - payload: "placeholder payload" - - - instructions: - - locale: fr_FR - value: Place les instruments dans les bonnes familles d'instruments - - locale: en_US - value: Place the instruments in the right instrument families - interface: dragAndDropIntoZones - gameplay: findTheRightAnswers - payload: "placeholder payload" - - - instructions: - - locale: fr_FR - value: Place les instruments dans les bonnes familles d'instruments - - locale: en_US - value: Place the instruments in the right instrument families - interface: dragAndDropIntoZones - gameplay: findTheRightAnswers - payload: "placeholder payload" - - - group: - - instructions: - - locale: fr_FR - value: Place les instruments dans les bonnes familles d'instruments - - locale: en_US - value: Place the instruments in the right instrument families - interface: dragAndDropIntoZones - gameplay: findTheRightAnswers - payload: "placeholder payload" - - - instructions: - - locale: fr_FR - value: Place les instruments dans les bonnes familles d'instruments - - locale: en_US - value: Place the instruments in the right instrument families - interface: dragAndDropIntoZones - gameplay: findTheRightAnswers - payload: "placeholder payload" - - - instructions: - - locale: fr_FR - value: Place les instruments dans les bonnes familles d'instruments - - locale: en_US - value: Place the instruments in the right instrument families - interface: dragAndDropIntoZones - gameplay: findTheRightAnswers - payload: "placeholder payload" diff --git a/Specs/jtd/tests/curriculum.yml b/Specs/jtd/tests/curriculum.yml deleted file mode 100644 index 3398f2c2cd..0000000000 --- a/Specs/jtd/tests/curriculum.yml +++ /dev/null @@ -1,67 +0,0 @@ -# Leka - iOS Monorepo -# Copyright APF France handicap -# SPDX-License-Identifier: Apache-2.0 - -uuid: 05E867F57451418789DA11B8FC5C8D34 -name: contentkit-specs-jtd-tests-curriculum_good - -status: published - -authors: - - leka_at_apf_france_handicap - - aurore_kiesler - -locales: - - en_US - - fr_FR - -skills: - - skill_one - - skill_two - - skill_three - -hmi: - - robot - - magic_cards - - tablet_robot - -tags: - - tag_one - - tag_two - - tag_three - -l10n: - - locale: fr_FR - details: - icon: name_of_the_curriculum-icon-fr_FR.svg - - title: Le titre/nom du curriculum - subtitle: Le sous-titre du curriculum - - abstract: | - Courte description du curriculum sur plusieurs lignes - Si besoin! - Et en markdown **simple** seulement - - description: | - ## Longues description en markdown plus complexe si on veut - - Lorem markdownum recepta avidum, missa de quam patientia, antris: cum defuit, - Titan repetemus nomine, ignare. Quod ad aura, et non quod vidisse utque ulla: - - - Pro inposuit tibi orsa tum artes ferox - - Acmon plausu qua agrestum situs virgo in - - Vacuus a pendens rostro non si pharetrae - - Haeremusque quos auxiliaris coniunx - - Repulsa impediunt munera teneri fallebat - - Bracchia frustra telo Iovis faucibus casus - -activities: - - 537FFD9FAD2E41759A9EF5D2DFD28CC0 - - 144B209647D246EB8411C3E9BDA47677 - - 43EEAC939FEC4BA1933E19D8282682DB - - 7D0D0A70A0094C5FAC3077B6BD8A9E09 - - 5DC42002DF83463BB23EC1C628205655 - - 321D94072E3F4271836DB8597C384F3A - - D2B544D41CE94E449EDD2A7DC31EFB09 - - B14C105D38314FC48B2CFC4A475103D3 diff --git a/Specs/jtd/tests/skills.yml b/Specs/jtd/tests/skills.yml deleted file mode 100644 index c80d5b0da3..0000000000 --- a/Specs/jtd/tests/skills.yml +++ /dev/null @@ -1,80 +0,0 @@ -# Leka - iOS Monorepo -# Copyright APF France handicap -# SPDX-License-Identifier: Apache-2.0 - -skills: - - id: spatial_understanding - l10n: - - locale: en_US - name: Spatial understanding - description: > - Understanding and knowing how to position oneself in a place, see - where things are around, and how to move or navigate in that space. - - locale: fr_FR - name: Apprehension de l'espace - description: > - Comprendre et savoir comment se situer dans un lieu, voir où les - choses se trouvent autour de soi, et comment bouger ou se déplacer - dans cet espace. - subskills: [] - - id: time_and_temporality_understanding - l10n: - - locale: en_US - name: Time and temporality understanding - description: > - Understanding time and knowing when things happen (For example, - breakfast is in the morning and dinner is in the evening). This also - means knowing how long things last (For example, a minute is shorter - than an hour) and understanding the order of events (For example, - today is after yesterday and before tomorrow, spring is after winter - and before summer). - - locale: fr_FR - name: Apprehension du temps et de la temporalité - description: > - Comprendre le temps et savoir quand les choses arrivent (par exemple, - le petit-déjeuner a lieu le matin et le diner a lieu le soir). Cela - signifie aussi savoir combien de temps durent les choses (par exemple, - une minute est plus courte qu'une heure) et connaître l'ordre des - événements (par exemple, aujourd'hui est après hier et avant demain, - le printemps est après l’hiver et avant l’été). - subskills: [] - - id: association - l10n: - - locale: en_US - name: Association - description: > - Creating logical connections between elements that may seem different - and finding relationships between them. - - locale: fr_FR - name: Association - description: > - Créer des liens logiques entre des éléments qui semblent différents et - trouver des relations entre eux. - subskills: - - id: association/matching - l10n: - - locale: en_US - name: Matching - description: > - Finding two or more things that are exactly the same or very - similar (for example, pairing two identical socks) - - locale: fr_FR - name: Apparier - description: > - Trouver deux ou plusieurs choses qui sont exactement les mêmes ou - très similaires, (par exemple, mettre ensemble deux chaussettes - identiques). - subskills: [] - - id: association/sorting - l10n: - - locale: en_US - name: Sorting - description: > - Putting things into groups based on what they have in common (for - example, sorting fruits by color) - - locale: fr_FR - name: Trier - description: > - Mettre des choses en groupes selon ce qu'elles ont en commun, (par - exemple, trier des fruits par couleur) - subskills: [] diff --git a/Tools/Hooks/check_xcstrings_stale_entries.py b/Tools/Hooks/check_xcstrings_stale_entries.py new file mode 100755 index 0000000000..a582fc4d52 --- /dev/null +++ b/Tools/Hooks/check_xcstrings_stale_entries.py @@ -0,0 +1,57 @@ +#!/usr/bin/python3 +"""Module providing a hook to check for stale entries in .xcstrings files.""" + +# Leka - iOS Monorepo +# Copyright APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +import json +import sys +from pygments import highlight +from pygments.lexers.data import JsonLexer +from pygments.formatters.terminal import TerminalFormatter + + +def check_for_stale_entries(json_file): + """Check for stale entries in a .xcstrings file.""" + with open(json_file, "r", encoding="utf8") as file: + data = json.load(file) + strings = data.get("strings", {}) + + stale_entries = [] + + for key, value in strings.items(): + if value.get("extractionState") == "stale": + stale_entries.append((key, strings[key])) + + return stale_entries + + +def main(): + """Main function.""" + # ? Check if a file was specified + if len(sys.argv) > 1: + xcstrings_files = sys.argv[1:] + else: + print("❌ No file specified") + sys.exit(1) + + must_fail = False + + for file in xcstrings_files: + stale_entries = check_for_stale_entries(file) + if stale_entries: + print(f"❌ Stale entries found in {file}") + for key, data in stale_entries: + data = json.dumps(data, indent=4) + print(highlight(f'"{key}": {data}', JsonLexer(), TerminalFormatter())) + must_fail = True + + if must_fail: + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Tools/Hooks/check_xcstrings_stale_entries.sh b/Tools/Hooks/check_xcstrings_stale_entries.sh deleted file mode 100755 index 17e5cfba07..0000000000 --- a/Tools/Hooks/check_xcstrings_stale_entries.sh +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh - -# Leka - iOS Monorepo -# Copyright APF France handicap -# SPDX-License-Identifier: Apache-2.0 - -RED='\033[0;31m' # Red color for error -BLUE='\033[4;34m' # Blue color with underline for file path -NC='\033[0m' # No Color - -JSON_FILE="$1" - -jq -e '.strings | to_entries | all(.value.extractionState != "stale")' $JSON_FILE > /dev/null - -RET_VAL=$? - -if [ $RET_VAL -ne 0 ]; then - echo "${RED}❌ Error:${NC} Some strings are stale, please open ${BLUE}$JSON_FILE${NC} in Xcode to remove them." - exit 1 -else - exit 0 -fi diff --git a/Tools/Hooks/check_xcstrings_unusual_terminators.py b/Tools/Hooks/check_xcstrings_unusual_terminators.py new file mode 100755 index 0000000000..8cfbf58c4f --- /dev/null +++ b/Tools/Hooks/check_xcstrings_unusual_terminators.py @@ -0,0 +1,74 @@ +#!/usr/bin/python3 +"""Check xstrings for unusal terminators""" + +# Leka - LekaOS +# Copyright 2020 APF France handicap +# SPDX-License-Identifier: Apache-2.0 + +import sys +import json +from pygments import highlight +from pygments.lexers.data import JsonLexer +from pygments.formatters.terminal import TerminalFormatter + + +CHARACTER_TO_SEARCH = "\u2028" + +# +# Mark: - Functions +# + + +def check_for_unusual_terminators(file_path): + """Check the given file for unusual terminators.""" + with open(file_path, "r", encoding="utf-8") as file: + data = json.load(file) + + wrong_entries = [] + + # Navigate through the JSON structure + for key, value in data["strings"].items(): + for _, localizations in value["localizations"].items(): + localized_string = localizations["stringUnit"]["value"] + if CHARACTER_TO_SEARCH in localized_string: + wrong_entries.append((key, value)) + + return wrong_entries + + +# +# Mark: - Main +# + + +def main(): + """Main function""" + # Check if a file was specified + if len(sys.argv) > 1: + profession_definitions_files = sys.argv[1:] + else: + print("❌ No file specified") + sys.exit(1) + + must_fail = False + + for file in profession_definitions_files: + print(f"🔍 Checking file {file} for unusual terminators...") + wrong_entries = check_for_unusual_terminators(file) + if wrong_entries: + must_fail = True + print( + f"❌ Unusual terminators 'U+{ord(CHARACTER_TO_SEARCH):04X}' found in {file}" + ) + for key, data in wrong_entries: + data = json.dumps(data, indent=4) + print(highlight(f'"{key}": {data}', JsonLexer(), TerminalFormatter())) + + if must_fail: + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Tools/Hooks/check_xcstrings_unusual_terminators.sh b/Tools/Hooks/check_xcstrings_unusual_terminators.sh deleted file mode 100755 index 7318087371..0000000000 --- a/Tools/Hooks/check_xcstrings_unusual_terminators.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -# Leka - iOS Monorepo -# Copyright APF France handicap -# SPDX-License-Identifier: Apache-2.0 - -RED='\033[0;31m' # Red color for error -BLUE='\033[4;34m' # Blue color with underline for file path -NC='\033[0m' # No Color - -# Path to the JSON file you want to check -JSON_FILE="$1" - -# Check for unusual terminators like -if grep --quiet $'\xe2\x80\xa8' "$JSON_FILE"; then - echo "${RED}❌ Error:${NC} The file ${BLUE}$JSON_FILE${NC} contains unusual terminators (e.g., ).\nPlease replace them with '\\\n' them using vscode" - exit 1 -fi - -exit 0 diff --git a/Tools/Hooks/check_yaml_content_activities.py b/Tools/Hooks/check_yaml_content_activities.py index 5379411be8..cd5a6ce8a8 100755 --- a/Tools/Hooks/check_yaml_content_activities.py +++ b/Tools/Hooks/check_yaml_content_activities.py @@ -1,4 +1,5 @@ #!/usr/bin/python3 +"""Check the content of a YAML file for an activity""" # Leka - LekaOS # Copyright 2020 APF France handicap @@ -7,67 +8,96 @@ import os import subprocess import uuid -import ruamel.yaml import sys +import ruamel.yaml JTD_SCHEMA = "Specs/jtd/activity.jtd.json" +# +# Mark: - Functions +# + + +def check_content_activity(filename): + """Check the content of a YAML file for an activity""" + # ? Create a YAML object + yaml = ruamel.yaml.YAML(typ="rt") + yaml.indent(mapping=2, sequence=4, offset=2) + + file_is_valid = True + + # ? Load the YAML file + with open(filename, "r", encoding="utf8") as file: + data = yaml.load(file) + + # ? Check uuid is the same as the filename + filename_uuid = os.path.basename(filename).split("-")[0] + if data["uuid"] != filename_uuid: + print(f"\n❌ The id in {filename} is not the same as the filename") + print(f"uuid: {data['uuid']}") + print(f"filename: {filename_uuid}") + file_is_valid = False + + # ? Check uuid is valid + try: + uuid.UUID(data["uuid"]) + except ValueError: + print(f"\n❌ The id in {filename} is not valid") + print(f"uuid: {data['uuid']}") + file_is_valid = False + + # ? Check schema validation with ajv + os.environ["FORCE_COLOR"] = "true" + cmd = ( + f"ajv validate --verbose --all-errors --spec=jtd -s {JTD_SCHEMA} -d {filename}" + ) + result = subprocess.run(cmd, shell=True, capture_output=True, check=False) + + if result.returncode != 0: + error = result.stderr.decode("utf-8") + print(f"\n❌ File does not match the schema {JTD_SCHEMA}") + print(error) + file_is_valid = False + + # ? Check name is the same as filename + filename_name = os.path.basename(filename).split("-")[-1].split(".activity.yml")[0] + if data["name"] != filename_name: + print(f"\n❌ The name in {filename} is not the same as the filename") + print(f"name: {data['name']}") + print(f"filename: {filename_name}") + file_is_valid = False + + return file_is_valid + + # # Mark: - Main # -# ? Check if a file was specified -if len(sys.argv) > 1: - FILENAME = sys.argv[1] -else: - print("❌ No file specified") - exit(1) - -# ? Create a YAML object -yaml = ruamel.yaml.YAML(typ='rt') -yaml.indent(mapping=2, sequence=4, offset=2) - -# ? Load the YAML file -with open(FILENAME, 'r') as file: - data = yaml.load(file) - -# ? Check uuid is the same as the filename -filename_uuid = os.path.basename(FILENAME).split("-")[0] -if data['uuid'] != filename_uuid: - print(f"❌ The id in {FILENAME} is not the same as the filename") - print(f"uuid: {data['uuid']}") - print(f"filename: {filename_uuid}") - exit(1) - -# ? Check uuid is valid -try: - uuid.UUID(data['uuid']) -except ValueError: - print(f"❌ The id in {FILENAME} is not valid") - print(f"uuid: {data['uuid']}") - exit(1) - -# ? Check schema validation with ajv -os.environ['FORCE_COLOR'] = 'true' -cmd = f"ajv validate --verbose --all-errors --spec=jtd -s {JTD_SCHEMA} -d {FILENAME}" -result = subprocess.run(cmd, shell=True, capture_output=True) - -if result.returncode != 0: - error = result.stderr.decode('utf-8') - print(f"❌ File does not match the schema {JTD_SCHEMA}") - print(error) - exit(1) - -# ? Check name is the same as filename -filename_name = os.path.basename(FILENAME).split('-')[-1].split('.activity.yml')[0] -if data['name'] != filename_name: - print(f"❌ The name in {FILENAME} is not the same as the filename") - print(f"name: {data['name']}") - print(f"filename: {filename_name}") - exit(1) - - -exit(0) +def main(): + """Main function to check the content of a YAML file for an activity""" + # ? Check if a file was specified + if len(sys.argv) > 1: + activity_files = sys.argv[1:] + else: + print("\n❌ No file specified") + sys.exit(1) + + must_fail = False + + for file in activity_files: + file_is_valid = check_content_activity(file) + if file_is_valid is False: + must_fail = True + + if must_fail: + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Tools/Hooks/check_yaml_definitions_authors.py b/Tools/Hooks/check_yaml_definitions_authors.py index 6bf354d934..b024644187 100755 --- a/Tools/Hooks/check_yaml_definitions_authors.py +++ b/Tools/Hooks/check_yaml_definitions_authors.py @@ -1,61 +1,101 @@ #!/usr/bin/python3 +"""Check authors definitions""" # Leka - LekaOS # Copyright 2020 APF France handicap # SPDX-License-Identifier: Apache-2.0 import os -import ruamel.yaml import subprocess import sys +import ruamel.yaml JTD_SCHEMA = "Specs/jtd/authors.jtd.json" -# ? Check if a file was specified -if len(sys.argv) > 1: - FILENAME = sys.argv[1] -else: - print("❌ No file specified") - exit(1) - -# ? Create a YAML object -yaml = ruamel.yaml.YAML(typ='rt') -yaml.indent(mapping=2, sequence=4, offset=2) - -# ? Load the YAML file -with open(FILENAME, 'r') as file: - data = yaml.load(file) - -# ? Sort the list by id -data['list'] = sorted(data['list'], key=lambda item: item['id']) - -# ? Write the sorted data back to the file -with open(FILENAME, 'w') as file: - yaml.dump(data, file) - -# ? Extract the ids -ids = [item['id'] for item in data['list']] - -# ? Check if all ids are unique -if len(set(ids)) != len(ids): - print(f"❌ There are duplicate ids in {FILENAME}") - seen = set() - duplicate = set() - for id in ids: - if id in seen: - duplicate.add(id) - else: - seen.add(id) - print(f"Duplicate ids: {duplicate}") - exit(1) - -# Check schema validation with ajv -os.environ['SYSTEMD_COLORS'] = '1' -cmd = f"ajv --spec=jtd -s {JTD_SCHEMA} -d {FILENAME}" -result = subprocess.run(cmd, shell=True) - -if result.returncode != 0: - print(f"❌ {FILENAME} is invalid") - exit(1) - -exit(0) +# +# Mark: - Functions +# + + +def check_author_definitions(filename): + """Check author definitions""" + # ? Create a YAML object + yaml = ruamel.yaml.YAML(typ="rt") + yaml.indent(mapping=2, sequence=4, offset=2) + + # ? Load the YAML file + with open(filename, "r", encoding="utf8") as file: + data = yaml.load(file) + + # ? Sort the list by id + data["list"] = sorted(data["list"], key=lambda item: item["id"]) + + # ? Write the sorted data back to the file + with open(filename, "w", encoding="utf8") as file: + yaml.dump(data, file) + + # ? Extract the ids + ids = [item["id"] for item in data["list"]] + + file_is_valid = True + + # ? Check if all ids are unique + if len(set(ids)) != len(ids): + print(f"❌ There are duplicate ids in {filename}") + seen = set() + duplicate = set() + for author_id in ids: + if author_id in seen: + duplicate.add(author_id) + else: + seen.add(author_id) + print(f"Duplicate ids: {duplicate}") + file_is_valid = False + + # ? Check schema validation with ajv + os.environ["FORCE_COLOR"] = "true" + cmd = ( + f"ajv validate --verbose --all-errors --spec=jtd -s {JTD_SCHEMA} -d {filename}" + ) + + result = subprocess.run(cmd, shell=True, capture_output=True, check=False) + + if result.returncode != 0: + error = result.stderr.decode("utf-8") + print(f"\n❌ File does not match the schema {JTD_SCHEMA}") + print(error) + file_is_valid = False + + return file_is_valid + + +# +# Mark: - Main +# + + +def main(): + """Main function""" + # ? Check if a file was specified + if len(sys.argv) > 1: + author_definitions_files = sys.argv[1:] + else: + print("❌ No file specified") + sys.exit(1) + + must_fail = False + + for file in author_definitions_files: + file_is_valid = check_author_definitions(file) + + if file_is_valid is False: + must_fail = True + + if must_fail: + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Tools/Hooks/check_yaml_definitions_professions.py b/Tools/Hooks/check_yaml_definitions_professions.py index fbb6ad0e9b..c7bc9ff1dc 100755 --- a/Tools/Hooks/check_yaml_definitions_professions.py +++ b/Tools/Hooks/check_yaml_definitions_professions.py @@ -1,61 +1,101 @@ #!/usr/bin/python3 +"""Check profession definitions""" # Leka - LekaOS # Copyright 2020 APF France handicap # SPDX-License-Identifier: Apache-2.0 import os -import ruamel.yaml import subprocess import sys +import ruamel.yaml JTD_SCHEMA = "Specs/jtd/professions.jtd.json" -# ? Check if a file was specified -if len(sys.argv) > 1: - FILENAME = sys.argv[1] -else: - print("❌ No file specified") - exit(1) - -# ? Create a YAML object -yaml = ruamel.yaml.YAML(typ='rt') -yaml.indent(mapping=2, sequence=4, offset=2) - -# ? Load the YAML file -with open(FILENAME, 'r') as file: - data = yaml.load(file) - -# ? Sort the list by id -data['list'] = sorted(data['list'], key=lambda item: item['id']) - -# ? Write the sorted data back to the file -with open(FILENAME, 'w') as file: - yaml.dump(data, file) - -# ? Extract the ids -ids = [item['id'] for item in data['list']] - -# ? Check if all ids are unique -if len(set(ids)) != len(ids): - print(f"❌ There are duplicate ids in {FILENAME}") - seen = set() - duplicate = set() - for id in ids: - if id in seen: - duplicate.add(id) - else: - seen.add(id) - print(f"Duplicate ids: {duplicate}") - exit(1) - -# Check schema validation with ajv -os.environ['SYSTEMD_COLORS'] = '1' -cmd = f"ajv --spec=jtd -s {JTD_SCHEMA} -d {FILENAME}" -result = subprocess.run(cmd, shell=True) - -if result.returncode != 0: - print(f"❌ {FILENAME} is invalid") - exit(1) - -exit(0) +# +# Mark: - Functions +# + + +def check_profession_definitions(filename): + """Check profession definitions""" + # ? Create a YAML object + yaml = ruamel.yaml.YAML(typ="rt") + yaml.indent(mapping=2, sequence=4, offset=2) + + # ? Load the YAML file + with open(filename, "r", encoding="utf8") as file: + data = yaml.load(file) + + # ? Sort the list by id + data["list"] = sorted(data["list"], key=lambda item: item["id"]) + + # ? Write the sorted data back to the file + with open(filename, "w", encoding="utf8") as file: + yaml.dump(data, file) + + # ? Extract the ids + ids = [item["id"] for item in data["list"]] + + file_is_valid = True + + # ? Check if all ids are unique + if len(set(ids)) != len(ids): + print(f"❌ There are duplicate ids in {filename}") + seen = set() + duplicate = set() + for profession_id in ids: + if profession_id in seen: + duplicate.add(profession_id) + else: + seen.add(profession_id) + print(f"Duplicate ids: {duplicate}") + file_is_valid = False + + # ? Check schema validation with ajv + os.environ["FORCE_COLOR"] = "true" + cmd = ( + f"ajv validate --verbose --all-errors --spec=jtd -s {JTD_SCHEMA} -d {filename}" + ) + + result = subprocess.run(cmd, shell=True, capture_output=True, check=False) + + if result.returncode != 0: + error = result.stderr.decode("utf-8") + print(f"\n❌ File does not match the schema {JTD_SCHEMA}") + print(error) + file_is_valid = False + + return file_is_valid + + +# +# Mark: - Main +# + + +def main(): + """Main function""" + # ? Check if a file was specified + if len(sys.argv) > 1: + profession_definitions_files = sys.argv[1:] + else: + print("❌ No file specified") + sys.exit(1) + + must_fail = False + + for file in profession_definitions_files: + file_is_valid = check_profession_definitions(file) + + if file_is_valid is False: + must_fail = True + + if must_fail: + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/Tools/Hooks/check_yaml_definitions_skills.py b/Tools/Hooks/check_yaml_definitions_skills.py index 870885f238..8538b21cfd 100755 --- a/Tools/Hooks/check_yaml_definitions_skills.py +++ b/Tools/Hooks/check_yaml_definitions_skills.py @@ -1,61 +1,101 @@ #!/usr/bin/python3 +"""Check skill definitions""" # Leka - LekaOS # Copyright 2020 APF France handicap # SPDX-License-Identifier: Apache-2.0 import os -import ruamel.yaml import subprocess import sys +import ruamel.yaml JTD_SCHEMA = "Specs/jtd/skills.jtd.json" -# ? Check if a file was specified -if len(sys.argv) > 1: - FILENAME = sys.argv[1] -else: - print("❌ No file specified") - exit(1) - -# ? Create a YAML object -yaml = ruamel.yaml.YAML(typ='rt') -yaml.indent(mapping=2, sequence=4, offset=2) - -# ? Load the YAML file -with open(FILENAME, 'r') as file: - data = yaml.load(file) - -# ? Sort the list by id -data['list'] = sorted(data['list'], key=lambda item: item['id']) - -# ? Write the sorted data back to the file -with open(FILENAME, 'w') as file: - yaml.dump(data, file) - -# ? Extract the ids -ids = [item['id'] for item in data['list']] - -# ? Check if all ids are unique -if len(set(ids)) != len(ids): - print(f"❌ There are duplicate ids in {FILENAME}") - seen = set() - duplicate = set() - for id in ids: - if id in seen: - duplicate.add(id) - else: - seen.add(id) - print(f"Duplicate ids: {duplicate}") - exit(1) - -# Check schema validation with ajv -os.environ['SYSTEMD_COLORS'] = '1' -cmd = f"ajv --spec=jtd -s {JTD_SCHEMA} -d {FILENAME}" -result = subprocess.run(cmd, shell=True) - -if result.returncode != 0: - print(f"❌ {FILENAME} is invalid") - exit(1) - -exit(0) +# +# Mark: - Functions +# + + +def check_skill_definitions(filename): + """Check profession definitions""" + # ? Create a YAML object + yaml = ruamel.yaml.YAML(typ="rt") + yaml.indent(mapping=2, sequence=4, offset=2) + + # ? Load the YAML file + with open(filename, "r", encoding="utf8") as file: + data = yaml.load(file) + + # ? Sort the list by id + data["list"] = sorted(data["list"], key=lambda item: item["id"]) + + # ? Write the sorted data back to the file + with open(filename, "w", encoding="utf8") as file: + yaml.dump(data, file) + + # ? Extract the ids + ids = [item["id"] for item in data["list"]] + + file_is_valid = True + + # ? Check if all ids are unique + if len(set(ids)) != len(ids): + print(f"❌ There are duplicate ids in {filename}") + seen = set() + duplicate = set() + for profession_id in ids: + if profession_id in seen: + duplicate.add(profession_id) + else: + seen.add(profession_id) + print(f"Duplicate ids: {duplicate}") + file_is_valid = False + + # ? Check schema validation with ajv + os.environ["FORCE_COLOR"] = "true" + cmd = ( + f"ajv validate --verbose --all-errors --spec=jtd -s {JTD_SCHEMA} -d {filename}" + ) + + result = subprocess.run(cmd, shell=True, capture_output=True, check=False) + + if result.returncode != 0: + error = result.stderr.decode("utf-8") + print(f"\n❌ File does not match the schema {JTD_SCHEMA}") + print(error) + file_is_valid = False + + return file_is_valid + + +# +# Mark: - Main +# + + +def main(): + """Main function""" + # ? Check if a file was specified + if len(sys.argv) > 1: + profession_definitions_files = sys.argv[1:] + else: + print("❌ No file specified") + sys.exit(1) + + must_fail = False + + for file in profession_definitions_files: + file_is_valid = check_skill_definitions(file) + + if file_is_valid is False: + must_fail = True + + if must_fail: + return 1 + + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/requirements.txt b/requirements.txt index 38c6d8f141..66f1a85cfd 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ # SPDX-License-Identifier: Apache-2.0 ruamel.yaml==0.18.6 +Pygments==2.17.2