feat: regex implementation
This commit is contained in:
@@ -1,5 +0,0 @@
|
|||||||
FROM python:3.11-alpine
|
|
||||||
|
|
||||||
COPY example-files .
|
|
||||||
|
|
||||||
CMD ["ls", "/shows"]
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# builds a container with the current Dockerfile for testing the bb-asset-validation program
|
|
||||||
docker build -t bb-asset-validation .
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env bash
|
|
||||||
|
|
||||||
# run the docker container for testing the bb-asset-validation program
|
|
||||||
docker run bb-asset-validation
|
|
||||||
9
example-paths-malformed.txt
Normal file
9
example-paths-malformed.txt
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
# should throw invalid version error
|
||||||
|
/shows/projectX/assets/environment/forest_v003/model/render/forest_beauty_v002.1001.exr
|
||||||
|
|
||||||
|
# should throw wrong size error
|
||||||
|
/shows/projectX/assets/environment/forest_v003/model/forest_beauty_v002.1001.exr
|
||||||
|
|
||||||
|
# should throw invalid prefix path error
|
||||||
|
/other/projectX/assets/environment/forest_v002/model/cache/forest_v002.fbx
|
||||||
|
|
||||||
22
example-paths-mixed.txt
Normal file
22
example-paths-mixed.txt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
# valid paths
|
||||||
|
/shows/projectX/assets/environment/forest_v001/model/render/forest_beauty_v001.1001.exr
|
||||||
|
/shows/projectX/assets/environment/forest_v001/model/render/forest_beauty_v001.1002.exr
|
||||||
|
/shows/projectX/assets/environment/forest_v001/model/render/forest_beauty_v001.1003.exr
|
||||||
|
/shows/projectX/assets/environment/forest_v001/model/cache/forest_v001.fbx
|
||||||
|
/shows/projectX/assets/environment/forest_v002/model/render/forest_beauty_v002.1001.exr
|
||||||
|
/shows/projectX/assets/environment/forest_v002/model/render/forest_beauty_v002.1002.exr
|
||||||
|
/shows/projectX/assets/environment/forest_v002/model/render/forest_beauty_v002.1003.exr
|
||||||
|
/shows/projectX/assets/environment/forest_v002/model/cache/forest_v002.fbx
|
||||||
|
|
||||||
|
# should throw invalid version error
|
||||||
|
/shows/projectX/assets/environment/forest_v003/model/render/forest_beauty_v002.1001.exr
|
||||||
|
|
||||||
|
# should throw wrong size error
|
||||||
|
/shows/projectX/assets/environment/forest_v003/model/forest_beauty_v002.1001.exr
|
||||||
|
|
||||||
|
# valid path
|
||||||
|
/shows/projectX/assets/environment/forest_v002/model/cache/forest_v002.fbx
|
||||||
|
|
||||||
|
# should throw invalid prefix path error
|
||||||
|
/other/projectX/assets/environment/forest_v002/model/cache/forest_v002.fbx
|
||||||
|
|
||||||
@@ -1,6 +1,14 @@
|
|||||||
import argparse
|
import argparse
|
||||||
import os.path
|
import os.path
|
||||||
import sys
|
import sys
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Global Variables. In production code I would not use global variables
|
||||||
|
|
||||||
|
# ANSI color escape codes, will not work on windows.
|
||||||
|
ANSI_FORE_RED = "\033[31m"
|
||||||
|
ANSI_FORE_GREEN = "\033[32m"
|
||||||
|
ANSI_DEFAULT = "\033[0m"
|
||||||
|
|
||||||
def parse_args() -> argparse.Namespace:
|
def parse_args() -> argparse.Namespace:
|
||||||
"""
|
"""
|
||||||
@@ -21,21 +29,6 @@ def parse_args() -> argparse.Namespace:
|
|||||||
|
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
|
||||||
def validate_shows_dir(shows_directory: str):
|
|
||||||
"""
|
|
||||||
Checks the given show directory is valid.
|
|
||||||
|
|
||||||
Arguments:
|
|
||||||
shows_directory (str): The directory shows are stored within
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: Whether the directory is valid.
|
|
||||||
"""
|
|
||||||
|
|
||||||
# Check path exists
|
|
||||||
if(not os.path.exists(shows_directory)):
|
|
||||||
raise ValueError("Designated show path does not exist: " + shows_directory)
|
|
||||||
|
|
||||||
def split_path(path):
|
def split_path(path):
|
||||||
PATH_DELIMETER = os.path.sep
|
PATH_DELIMETER = os.path.sep
|
||||||
|
|
||||||
@@ -46,66 +39,162 @@ def split_path(path):
|
|||||||
|
|
||||||
return split_path
|
return split_path
|
||||||
|
|
||||||
def validate_asset_path_structure(parsed_args, asset_path: str):
|
def get_path_components(asset_path: str):
|
||||||
|
pattern = re.compile(r"(?P<full_path>/shows/(?P<project_name>\S*?)/\S*?/(?P<asset_type>\S*?)/(?P<asset_name>\S*?)_v(?P<asset_version>\S*?)/(?P<task>\S*?)/\S*?/(?P<file_name>\S*?)_v(?P<file_version>\d*)\D*?(?P<frame_number>\d*)\D*?\.(?P<file_extension>\w*)\Z)")
|
||||||
|
match = pattern.match(asset_path)
|
||||||
|
path_components = match.groupdict()
|
||||||
|
|
||||||
|
return path_components
|
||||||
|
|
||||||
|
def assemble_new_path(path_components: dict) -> str:
|
||||||
|
frame_number = f".{path_components['frame_number']}" if path_components['frame_number'] else ""
|
||||||
|
file_extension = f".{path_components['file_extension']}" if path_components['file_extension'] else ""
|
||||||
|
return f"/shows/{path_components['project_name']}/staging/delivery/assets/{path_components['asset_type']}_{path_components['asset_name']}/{path_components['task']}_{path_components['asset_version']}/{path_components['file_name']}{frame_number}{file_extension}"
|
||||||
|
|
||||||
|
def should_skip_line(line):
|
||||||
|
line = line.strip()
|
||||||
|
|
||||||
|
return (
|
||||||
|
len(line)<=1 or # too short
|
||||||
|
line[0]=="#" # comment
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def validate_paths_file(parsed_args):
|
||||||
|
with open(parsed_args.paths_file, "r") as f:
|
||||||
|
line = f.readline()
|
||||||
|
while(line):
|
||||||
|
line = f.readline()
|
||||||
|
|
||||||
|
if(should_skip_line(line)):
|
||||||
|
continue
|
||||||
|
# clean line
|
||||||
|
path = line.strip()
|
||||||
|
|
||||||
|
# validate path
|
||||||
|
if(not validate_path(parsed_args, path)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# all components of the project
|
||||||
|
all_components = get_path_components(path)
|
||||||
|
|
||||||
|
# map the specific components listed in the brief
|
||||||
|
stored_components = map_stored_components(all_components)
|
||||||
|
|
||||||
|
# assemble the new path
|
||||||
|
new_path = assemble_new_path(all_components)
|
||||||
|
|
||||||
|
# print out the resulting values
|
||||||
|
print_result(path, new_path, stored_components)
|
||||||
|
|
||||||
|
def validate_static_list(parsed_args):
|
||||||
|
static_list = [
|
||||||
|
"/shows/projectX/assets/environment/forest_v001/model/render/forest_beauty_v001.1002.exr",
|
||||||
|
"/shows/projectX/assets/environment/forest_v001/model/cache/forest_v001.fbx"
|
||||||
|
]
|
||||||
|
|
||||||
|
for line in static_list:
|
||||||
|
if(should_skip_line(line)):
|
||||||
|
continue
|
||||||
|
# clean line
|
||||||
|
path = line.strip()
|
||||||
|
|
||||||
|
# validate path
|
||||||
|
if(not validate_path(parsed_args, path)):
|
||||||
|
continue
|
||||||
|
|
||||||
|
# all components of the project
|
||||||
|
all_components = get_path_components(path)
|
||||||
|
|
||||||
|
# map the specific components listed in the brief
|
||||||
|
stored_components = map_stored_components(all_components)
|
||||||
|
|
||||||
|
# assemble the new path
|
||||||
|
new_path = assemble_new_path(all_components)
|
||||||
|
|
||||||
|
# print out the resulting values
|
||||||
|
print_result(path, new_path, stored_components)
|
||||||
|
|
||||||
|
def map_stored_components(all_components):
|
||||||
|
stored_components = {
|
||||||
|
"full_path" : all_components["full_path"],
|
||||||
|
"project" : all_components["project_name"],
|
||||||
|
"asset_type" : all_components["asset_type"],
|
||||||
|
"asset_name" : all_components["asset_name"],
|
||||||
|
"version" : all_components["asset_version"],
|
||||||
|
"task" : all_components["task"],
|
||||||
|
"file_extension" :all_components["file_extension"],
|
||||||
|
}
|
||||||
|
return stored_components
|
||||||
|
|
||||||
|
def print_result(original_path, new_path, stored_components):
|
||||||
|
print(f"{ANSI_FORE_GREEN}\n--------------")
|
||||||
|
print(f"original path:\n\t{original_path}\n")
|
||||||
|
print(f"new path:\n\t{new_path}\n")
|
||||||
|
print(f"stored components:\n\t{stored_components}")
|
||||||
|
print(f"--------------\n{ANSI_DEFAULT}")
|
||||||
|
|
||||||
|
# def validate_static_list():
|
||||||
|
|
||||||
|
|
||||||
|
def throw_schema_error(path: str, error_message: str) -> None:
|
||||||
|
print(f"\n{ANSI_FORE_RED}-----\nPath is invalid: {path}\n{error_message}\n-----{ANSI_DEFAULT}", file=sys.stderr)
|
||||||
|
|
||||||
|
def validate_path(parsed_args: argparse.Namespace, asset_path: str) -> bool:
|
||||||
|
"""
|
||||||
|
Validates the given path based on the arguments provided.
|
||||||
|
|
||||||
|
Parameters:
|
||||||
|
parsed_args (argparse.Namespace): CLI arguments
|
||||||
|
asset_path (str): The path to validate
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Whether the provided path is valid
|
||||||
|
"""
|
||||||
|
# check path is absolute
|
||||||
if(asset_path[0] != "/"):
|
if(asset_path[0] != "/"):
|
||||||
print(f"\nError for path: {asset_path}\n-------------", file=sys.stderr)
|
throw_schema_error(asset_path, "Expected absolute asset path, recieved path does not start with '/'")
|
||||||
print("Expected absolute asset path, recieved path does not start with '/'\n", file=sys.stderr)
|
return False
|
||||||
|
|
||||||
split_asset_path = split_path(os.path.normpath(asset_path))
|
|
||||||
|
|
||||||
|
# check length
|
||||||
|
EXPECTED_LENGTH=8
|
||||||
|
split_asset_path = split_path(asset_path)
|
||||||
|
if(EXPECTED_LENGTH is not None and len(split_asset_path) != EXPECTED_LENGTH):
|
||||||
|
throw_schema_error(asset_path, f"Path isn't expected length {EXPECTED_LENGTH}.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# check path is prefixed with shows path
|
# check path is prefixed with shows path
|
||||||
# valid_show_path = True
|
shows_path = os.path.realpath(parsed_args.shows_directory)
|
||||||
split_shows_path = split_path(os.path.realpath(parsed_args.shows_directory))
|
valid_show_path = shows_path == asset_path[:len(shows_path)]
|
||||||
valid_show_path = split_shows_path == split_asset_path[:len(split_shows_path)]
|
|
||||||
|
|
||||||
if(not valid_show_path):
|
if(not valid_show_path):
|
||||||
print(f"\nError for path: {asset_path}\n-------------", file=sys.stderr)
|
throw_schema_error(asset_path, f"Path does not start with expected show directory '{parsed_args.shows_directory}'")
|
||||||
print(f"Path starting with '{split_asset_path[0]}' does not start with expected show directory '{parsed_args.shows_directory}'\n", file=sys.stderr)
|
return False
|
||||||
else:
|
|
||||||
print("\n--------------\nGOOD PATH:", asset_path,"\n----------------\n")
|
|
||||||
|
|
||||||
split_asset_path_suffix = split_asset_path[len(split_shows_path):]
|
# get components
|
||||||
|
path_components = get_path_components(asset_path)
|
||||||
|
|
||||||
path_components = {
|
|
||||||
"project_name" : split_asset_path_suffix[0],
|
|
||||||
"asset_type" : split_asset_path_suffix[2],
|
|
||||||
"asset_name" : split_asset_path_suffix[3].split("_")[0],
|
|
||||||
"task" : split_asset_path_suffix[4],
|
|
||||||
"version" : split_asset_path_suffix[3].split("_")[1],
|
|
||||||
"filename" : os.path.splitext(split_asset_path_suffix[6])[0].split("_")[0],
|
|
||||||
"file_extension" : os.path.splitext(split_asset_path_suffix[6])[1],
|
|
||||||
}
|
|
||||||
print(path_components)
|
|
||||||
|
|
||||||
new_path = f"/shows/{path_components['project_name']}/staging/delivery/assets/{path_components['asset_type']}_{path_components['asset_name']}/{path_components['task']}_{path_components['version']}/{path_components['filename']}{path_components['file_extension']}"
|
|
||||||
print("new path:\n", new_path)
|
|
||||||
|
|
||||||
|
# validate versions
|
||||||
|
if(path_components["asset_version"] != path_components["file_version"]):
|
||||||
|
throw_schema_error(asset_path, "Asset version and file version does not match.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
def main() -> None:
|
def main() -> None:
|
||||||
# Parse args
|
# Parse args
|
||||||
parsed_args = parse_args()
|
parsed_args = parse_args()
|
||||||
shows_directory = parsed_args.shows_directory
|
|
||||||
|
|
||||||
# validate_shows_dir(shows_directory)
|
|
||||||
|
|
||||||
if(parsed_args.paths_file):
|
if(parsed_args.paths_file):
|
||||||
with open(parsed_args.paths_file, "r") as f:
|
validate_paths_file(parsed_args)
|
||||||
line = f.readline()
|
else:
|
||||||
while(line):
|
print("No file provided.\n")
|
||||||
line = f.readline()
|
print("Try passing one of the example files with the --paths-file argument.")
|
||||||
|
print(f"eg. '{ANSI_FORE_GREEN}bb-asset-validation --paths-file example-paths-mixed.txt{ANSI_DEFAULT}'")
|
||||||
|
print("\nAlternatively, here is the validation of a hardcoded list of paths:")
|
||||||
|
|
||||||
# clean line
|
validate_static_list(parsed_args)
|
||||||
line = line.strip()
|
|
||||||
# skip empty lines
|
|
||||||
if(line==""):
|
|
||||||
continue
|
|
||||||
|
|
||||||
print('line:', line)
|
|
||||||
validate_asset_path_structure(parsed_args, line)
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
|
|||||||
Reference in New Issue
Block a user