Skip to content

Commit

Permalink
Merge pull request #27 from QuVery/generic-rules
Browse files Browse the repository at this point in the history
Add support for generic rules
  • Loading branch information
omid3098 authored Dec 14, 2023
2 parents 533ba4c + 46594aa commit 1ed94db
Show file tree
Hide file tree
Showing 11 changed files with 112 additions and 12 deletions.
Binary file modified .DS_Store
Binary file not shown.
5 changes: 3 additions & 2 deletions create_rule.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
1 change: 1 addition & 0 deletions docs/03_rules.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
46 changes: 46 additions & 0 deletions docs/06_naming_convention.md
Original file line number Diff line number Diff line change
@@ -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

7 changes: 3 additions & 4 deletions rule_base.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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}")
Expand All @@ -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
24 changes: 18 additions & 6 deletions rule_parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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

Expand All @@ -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


Expand Down
Binary file modified rules/2d/.DS_Store
Binary file not shown.
Empty file added rules/generic/check/.gitignore
Empty file.
41 changes: 41 additions & 0 deletions rules/generic/check/01_naming_convention.py
Original file line number Diff line number Diff line change
@@ -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 {}
Empty file.
Empty file.

0 comments on commit 1ed94db

Please sign in to comment.