From 46594aa4ddda3aa19d266758ed46a6fa5c05bcb3 Mon Sep 17 00:00:00 2001 From: Omid Saadat Date: Thu, 14 Dec 2023 23:31:09 +0300 Subject: [PATCH] Add support for generic rules --- .DS_Store | Bin 8196 -> 8196 bytes create_rule.py | 5 ++- docs/03_rules.md | 1 + docs/06_naming_convention.md | 46 ++++++++++++++++++++ rule_base.py | 7 ++- rule_parser.py | 24 +++++++--- rules/2d/.DS_Store | Bin 6148 -> 6148 bytes rules/generic/check/.gitignore | 0 rules/generic/check/01_naming_convention.py | 41 +++++++++++++++++ rules/generic/postcheck/.gitignore | 0 rules/generic/precheck/.gitignore | 0 11 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 docs/06_naming_convention.md create mode 100644 rules/generic/check/.gitignore create mode 100644 rules/generic/check/01_naming_convention.py create mode 100644 rules/generic/postcheck/.gitignore create mode 100644 rules/generic/precheck/.gitignore diff --git a/.DS_Store b/.DS_Store index c0fdace8a2381d5e5cce48a464abd3d4357cf451..bd6bdd42dc553e4241c2e83c1214f9824cd17612 100644 GIT binary patch delta 82 zcmZp1XmQx^Mp&&c%73y?-%&>f1_l;}6o!0;WQOA0{1lg@octu97zadtvyjL_CT50v YCX=^`#WOJ;*eotShaHJOSzP=r07my3UH||9 delta 82 zcmZp1XmQx^Mp!NKQW)|Xk{OC~^HW@sa`KaaVjK|p%|apznV1=s YmrdR#7SF`AV6(XR9CjrBWO4Dg0Ck-jr~m)} 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 283d4a6b7d007bbdffd0b6a250e9f7b067960a6b..cb73cee81003a172dd8b5151d3c42815c6515c87 100644 GIT binary patch delta 51 zcmZoMXffEJ!NkOSf3g;n6kCneL;t(Ilf9T^V9XpQIYx!a8=1r>OEK|n-oO+h3IM_M B5ZM3# delta 51 zcmZoMXffEJ!Neq`H(85GimfZ}jN_rU$zDt{FlG*u9HYbJjZET`rI`3OZ(s@$1pujl B5KjOA 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