diff --git a/.DS_Store b/.DS_Store index c0fdace..bd6bdd4 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/create_rule.py b/create_rule.py index a8bc371..a1ea5f7 100644 --- a/create_rule.py +++ b/create_rule.py @@ -46,9 +46,10 @@ def parse_args(): "2d", "audio", "dir", - "custom"]: + "custom", + "generic"]: raise ValueError( - "Rule type must be one of the following: 3d, 2d, audio, dir, custom") + "Rule type must be one of the following: 3d, 2d, audio, dir, custom or generic") parsed_args['rule_name'] = args[1] return parsed_args diff --git a/docs/03_rules.md b/docs/03_rules.md index 6076e30..940a65a 100644 --- a/docs/03_rules.md +++ b/docs/03_rules.md @@ -18,6 +18,7 @@ Same as PreCheck but will be run after the Check rules. Before creating a new rule, you need to know the structure of the rules. There are 5 predefined rule categories: `2d`, `3d`, `audio`, `custom` and `dir`. +There is also a directory called ‌`generic` which contains the generic rules that are used for all categories. To create a new rule, you need to create a new python file in the rules folder under the one if these subfolders: `2d`, `3d`, `audio`, `custom`, `dir` depending on the type of the rule you are creating. Then you can fill the file with the following template: diff --git a/docs/06_naming_convention.md b/docs/06_naming_convention.md new file mode 100644 index 0000000..839d77d --- /dev/null +++ b/docs/06_naming_convention.md @@ -0,0 +1,46 @@ +# Naming Convention + +Cheching namin convention is a generic rule. These rules are needed to run on all kinds of assets. Either it is a 3D model, a texture, a sound, etc. The rules are the same for all assets. +Directories are not considered as assets but it is easy to define rules for them too. + +To design a generic rule for checking the naming convention of assets in QuVery, we have to consider the following points: + +- The name of the asset should be unique. +- The name of the asset should be descriptive. +- The name of the asset should be consistent. + +### Unique + +This means that the name of the asset should be unique in the project. This is important because it will be used to identify the asset in the project. + +### Descriptive + +The name of the asset should be able to describe the asset. By reading the name of the asset, we should be able to know what the asset is about and what it is used for. + +### Consistent + +The name of the asset should be consistent with the other assets in the project. This means that the name of the asset should follow the same naming convention as the other assets in the project. It will make it easier to find the asset in the project. + +## Some Naming Convention Examples +### UE5 Naming Convention + +[Recommended Asset Naming Convention in UE5](https://docs.unrealengine.com/5.3/en-US/recommended-asset-naming-conventions-in-unreal-engine-projects/) +[UE5 Style](https://github.com/Allar/ue5-style-guide) + +[AssetTypePrefix]_[AssetName]_[Descriptor]_[OptionalVariantLetterOrNumber] + +Eample: +- A static mesh for a table in the game would be named SM_Table_Wood_01 +- A diffuse/albedo/color texture for a table in the game would be named T_Table_Wood_01_D +- If we have multiple versions of the metal door with different diffuse textures, we would name those T_Door_Metal_01_D, T_Door_Metal_02_D, etc. +- A no + +### Unity Naming Convention by Justin Wasilenko + +[Unity Asset Naming Conventions](https://github.com/justinwasilenko/Unity-Style-Guide?tab=readme-ov-file#4-asset-naming-conventions) + +[AssetTypePrefix]_[AssetName]_[Variant]_[Suffix] + +Example: +- A static mesh for a table in the game would be named SM_Table_Wood_01 + diff --git a/rule_base.py b/rule_base.py index cda8a06..1a31e7c 100644 --- a/rule_base.py +++ b/rule_base.py @@ -11,6 +11,7 @@ class InputCategory(Enum): FILE_AUDIO = "AUDIO" DIRECTORY = "DIR" CUSTOM = "CUSTOM" + GENERIC = "GENERIC" UNSUPPORTED = "UNSUPPORTED" # an abstract class for rules to inherit from @@ -49,8 +50,6 @@ def get_check_rules(self): return rules def execute_rules(self, input): - result_json = {} - result_json["input"] = input rules_json = {} for module in self._precheck_rules: logger.info(f"Processing rule {module.RULE_NAME}") @@ -70,5 +69,5 @@ def execute_rules(self, input): process_result = module.process(input) if process_result != {}: return f"Postcheck failed at rule \"{module.RULE_NAME}\"." - result_json["rules"] = rules_json - return result_json + # result_json["rules"] = rules_json + return rules_json diff --git a/rule_parser.py b/rule_parser.py index 1dcf288..f9153ce 100644 --- a/rule_parser.py +++ b/rule_parser.py @@ -53,12 +53,17 @@ def create_rules() -> None: listCustom: RuleList = __find_rules(os.path.join( rules_path, InputCategory.CUSTOM.value.lower()), InputCategory.CUSTOM) logger.info(f"Loading Custom rules completed...") + listGeneric: RuleList = __find_rules(os.path.join( + rules_path, InputCategory.GENERIC.value.lower()), InputCategory.GENERIC) + _all_rules.append(list2D) _all_rules.append(list3D) _all_rules.append(listAudio) _all_rules.append(listDir) _all_rules.append(listCustom) + _all_rules.append(listGeneric) + logger.info("Finished Creating rules...") logger.info("Loading custom extensions...") custom_extensions = __load_custom_extensions() @@ -73,7 +78,7 @@ def get_rules(type: str) -> list[str]: rules[ruleList.type.value.lower()] = ruleList.get_check_rules() else: for ruleList in _all_rules: - if ruleList.type.value.lower() == type.lower(): + if ruleList.type.value.lower() == type.lower() or ruleList.type.value.lower() == InputCategory.GENERIC.value.lower(): rules[ruleList.type.value.lower()] = ruleList.get_check_rules() return rules @@ -85,17 +90,24 @@ def is_ignored(input: str) -> bool: def execute_rules_for_file(input: str) -> list[str]: + result_json = {} + result_json["input"] = input + rules_json = {} if (is_ignored(input)): - return {"input": input, "error": Error_Codes.FILE_IGNORED.value} + result_json["error"] = Error_Codes.FILE_IGNORED.value + return result_json input_type = get_input_category(input) if input_type == InputCategory.UNSUPPORTED: - return {"input": input, "error": Error_Codes.FILE_NOT_VALID.value} - result_json = {} + result_json["error"] = Error_Codes.FILE_NOT_VALID.value + return result_json for ruleList in _all_rules: - if ruleList.type == input_type: + if ruleList.type == input_type or ruleList.type == InputCategory.GENERIC: result = ruleList.execute_rules(input) if result != {}: - result_json = result + # append the result dictionary to the rules_json dictionary + rules_json.update(result) + + result_json["rules"] = rules_json return result_json diff --git a/rules/2d/.DS_Store b/rules/2d/.DS_Store index 283d4a6..cb73cee 100644 Binary files a/rules/2d/.DS_Store and b/rules/2d/.DS_Store differ diff --git a/rules/generic/check/.gitignore b/rules/generic/check/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/rules/generic/check/01_naming_convention.py b/rules/generic/check/01_naming_convention.py new file mode 100644 index 0000000..b53be84 --- /dev/null +++ b/rules/generic/check/01_naming_convention.py @@ -0,0 +1,41 @@ +import os +import re + +# imports here + +# necessary for the rule to be loaded + + +RULE_NAME = "NamingConvention" + +def process(input): + """ + This function is called for each file that is checked. The input is the file path. + the function should return an empty json if the file is valid and a json object with the status and required information. + status can be one of the following: "error", "warning", "info" + example: + for a single error: {"status": "error" , "details": {"object_name": "error message"}} + for multiple errors: {"status": "error" , "details": {"object_name": "error message", "object_name2": "error message2"}} + for a single warning: {"status": "warning" , "details": {"object_name": "warning message"}} + for info: {"status": "info" , "details": {"object_name": "info message"}} + """ + + result_json = {"status": "error"} # default status is info + details_json = {} + + # the naming convention for files is: [AssetTypePrefix]_[AssetName]_[Descriptor]_[OptionalVariantLetterOrNumber] + # AssetTypePrefix is mandatory and can be like T for texture, SM for static mesh, A for audio clip, etc. + # AssetName is mandatory and can be like: Tree, Rock, etc. + # Descriptor is optional. like _Blue or _Zombie + # OptionalVariantLetterOrNumber is optional. like _a or _1 or _a1 + + match_regex = r"^[A-Z]{1,2}_[a-zA-Z0-9]+(_[a-zA-Z0-9]+)*(_[a-zA-Z0-9]+)*(_[a-zA-Z0-9]+)*$" + file_name_without_extension = os.path.splitext(os.path.basename(input))[0] + if not re.match(match_regex, file_name_without_extension): + details_json[file_name_without_extension] = f"File name '{file_name_without_extension}' does not match the naming convention: [AssetTypePrefix]_[AssetName]_[Descriptor]_[OptionalVariantLetterOrNumber]" + + if details_json != {}: + result_json["details"] = details_json + return result_json + else: + return {} diff --git a/rules/generic/postcheck/.gitignore b/rules/generic/postcheck/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/rules/generic/precheck/.gitignore b/rules/generic/precheck/.gitignore new file mode 100644 index 0000000..e69de29