Compare commits

...

2 Commits

Author SHA1 Message Date
comasqw
a20f2ba6a3 refactor Prototype class init 2024-11-19 17:12:42 +04:00
comasqw
c00de7fd09 refactor 2024-11-19 17:09:14 +04:00
28 changed files with 522 additions and 325 deletions

View File

@@ -0,0 +1,5 @@
from .logger import get_logger, LogText
from .exceptions import *
from .parsers import YamlParser, FtlParser
from .prototype import Prototype, check_prototype_attrs
from .localization_helper import LocalizationHelper

View File

@@ -0,0 +1,6 @@
class ErrorWhileWritingToFile(Exception):
pass
class ErrorWhileReadingFromFile(Exception):
pass

View File

@@ -0,0 +1,180 @@
import json
import os
from . import Prototype, check_prototype_attrs, get_logger, LogText, ErrorWhileWritingToFile, ErrorWhileReadingFromFile
from .parsers import FtlParser, YamlParser, create_ftl
logger = get_logger(__name__)
SAVE_RESULT_TO = "entities.ftl"
YAML_PARSER_LAST_LAUNCH_RESULT_PATH = "last_launch_result/result.json"
class LocalizationHelper:
def __init__(self):
logger.debug("%s LocalizationHelper ", LogText.CLASS_INITIALIZATION)
@staticmethod
def _save_to_json(path: str, data: dict):
os.makedirs(os.path.dirname(path), exist_ok=True)
try:
logger.debug("%s: %s", LogText.SAVING_DATA_TO_FILE, path)
with open(path, "w", encoding="utf-8") as file:
json.dump(data, file, ensure_ascii=False, indent=4)
except Exception as e:
raise ErrorWhileWritingToFile(e)
@staticmethod
def _read_from_json(path: str) -> dict:
if os.path.exists(path):
try:
logger.debug("%s: %s", LogText.READING_DATA_FROM_FILE, path)
with open(path, encoding="utf-8") as file:
return json.load(file)
except Exception as e:
raise ErrorWhileReadingFromFile(e)
return {}
def _save_yaml_parser_last_launch_result(self, last_launch_result: dict[str, Prototype]):
logger.debug("%s %s", LogText.SAVING_LAST_LAUNCH_RESULT, YAML_PARSER_LAST_LAUNCH_RESULT_PATH)
prototypes_dict = {}
for prototype_id, prototype_obj in last_launch_result.items():
prototypes_dict[prototype_id] = prototype_obj.attrs_dict
self._save_to_json(YAML_PARSER_LAST_LAUNCH_RESULT_PATH, prototypes_dict)
def _read_prototypes_from_last_launch_result(self) -> dict[str, Prototype] | None:
if os.path.isfile(YAML_PARSER_LAST_LAUNCH_RESULT_PATH):
last_launch_result = self._read_from_json(YAML_PARSER_LAST_LAUNCH_RESULT_PATH)
last_launch_result_dict = {}
for prototype_id, prototype_attrs in last_launch_result.items():
last_launch_result_dict[prototype_id] = Prototype(prototype_attrs)
return last_launch_result_dict
return None
@staticmethod
def _update_prototype_if_attrs_has_been_changed(yaml_prototype_obj: Prototype, last_launch_prototype_obj: Prototype,
final_prototype_obj: Prototype):
if yaml_prototype_obj.attrs_dict != last_launch_prototype_obj.attrs_dict:
log_text = f"Has been updated from: {final_prototype_obj.attrs_dict}, to: "
for key, value in yaml_prototype_obj.attrs_dict.items():
if final_prototype_obj.attrs_dict[key] != value:
final_prototype_obj.set_attrs_dict_value(key, value)
log_text += f"{final_prototype_obj.attrs_dict}"
logger.debug(log_text)
return final_prototype_obj
def _merge_yaml_parser_prototypes_and_ftl_parser_prototypes(self, yaml_parser_prototypes: dict[str, Prototype],
ftl_parser_prototypes: dict[str, Prototype]) -> dict[str, Prototype]:
general_prototypes_dict = {}
last_launch_result = self._read_prototypes_from_last_launch_result()
for prototype_id, yaml_prototype_obj in yaml_parser_prototypes.items():
final_prototype_obj = yaml_prototype_obj
if prototype_id in ftl_parser_prototypes:
final_prototype_obj = ftl_parser_prototypes[prototype_id]
if last_launch_result and prototype_id in last_launch_result:
last_launch_prototype_obj = last_launch_result[prototype_id]
final_prototype_obj = self._update_prototype_if_attrs_has_been_changed(yaml_prototype_obj,
last_launch_prototype_obj,
final_prototype_obj)
general_prototypes_dict[prototype_id] = final_prototype_obj
return general_prototypes_dict
@staticmethod
def _set_parent_attrs(prototype_parent_id: str, prototype_obj: Prototype, parent_prototype_obj: Prototype):
for attr_name, attr_value in prototype_obj.attrs_dict.items():
if attr_value or attr_name in ("parent", "id"):
continue
parent_prototype_attr_value = parent_prototype_obj.attrs_dict.get(attr_name)
if parent_prototype_attr_value:
if attr_name == "name":
prototype_obj.name = f"{{ ent-{prototype_parent_id} }}"
elif attr_name == "description":
prototype_obj.description = f"{{ ent-{prototype_parent_id}.desc }}"
elif attr_name == "suffix":
prototype_obj.suffix = parent_prototype_attr_value
return prototype_obj
def _parent_checks(self, general_prototypes_dict: dict[str, Prototype]):
to_delete = []
for prototype_id, prototype_obj in general_prototypes_dict.items():
prototype_parent_id = prototype_obj.parent
if isinstance(prototype_parent_id, list):
continue
parent_prototype_obj = general_prototypes_dict.get(prototype_parent_id)
if parent_prototype_obj and check_prototype_attrs(parent_prototype_obj, False):
self._set_parent_attrs(prototype_parent_id, prototype_obj, parent_prototype_obj)
else:
if not check_prototype_attrs(prototype_obj, False):
to_delete.append(prototype_id)
for prototype_id in to_delete:
logger.debug("%s %s: %s", prototype_id, LogText.HAS_BEEN_DELETED, general_prototypes_dict[prototype_id])
del general_prototypes_dict[prototype_id]
return general_prototypes_dict
def _create_general_prototypes_dict(self, yaml_parser_prototypes: dict[str, Prototype],
ftl_parser_prototypes: dict[str, Prototype]) -> dict[str, Prototype]:
general_prototypes_dict = self._merge_yaml_parser_prototypes_and_ftl_parser_prototypes(yaml_parser_prototypes,
ftl_parser_prototypes)
self._save_yaml_parser_last_launch_result(yaml_parser_prototypes)
general_prototypes_dict = self._parent_checks(general_prototypes_dict)
return general_prototypes_dict
@staticmethod
def _create_result_ftl(general_prototypes_dict: dict[str, Prototype]) -> str:
result = ""
for prototype_obj in general_prototypes_dict.values():
result += create_ftl(prototype_obj)
return result
def _save_result(self, general_prototypes_dict: dict[str, Prototype]):
logger.debug("%s: %s", LogText.SAVING_FINAL_RESULT, SAVE_RESULT_TO)
result = self._create_result_ftl(general_prototypes_dict)
try:
with open(SAVE_RESULT_TO, "w", encoding="utf-8") as file:
file.write(result)
except Exception as e:
raise ErrorWhileWritingToFile(e)
@staticmethod
def _print_info(general_prototypes_dict):
logger.info("%s: %s prototypes", LogText.HAS_BEEN_PROCESSED, len(general_prototypes_dict))
logger.info("Logs in: 'logs/helper.log'")
def main(self, yaml_prototypes_path: str, ftl_prototypes_path: str):
try:
logger.debug("%s: %s", LogText.GETTING_PROTOTYPES_FROM_YAML, yaml_prototypes_path)
prototypes_list_parsed_from_yaml = YamlParser().get_prototypes(yaml_prototypes_path)
logger.debug("%s: %s", LogText.GETTING_PROTOTYPES_FROM_FTL, ftl_prototypes_path)
prototypes_list_parsed_from_ftl = FtlParser().get_prototypes(ftl_prototypes_path)
logger.debug(LogText.FORMING_FINAL_DICTIONARY)
general_prototypes_dict = self._create_general_prototypes_dict(prototypes_list_parsed_from_yaml,
prototypes_list_parsed_from_ftl)
self._save_result(general_prototypes_dict)
self._print_info(general_prototypes_dict)
except ErrorWhileWritingToFile as e:
logger.error("%s: %s", LogText.ERROR_WHILE_WRITING_DATA_TO_FILE, e, exc_info=True)
except ErrorWhileReadingFromFile as e:
logger.error("%s: %s", LogText.ERROR_WHILE_READING_DATA_FROM_FILE, e, exc_info=True)
except Exception as e:
logger.error("%s: %s", LogText.UNKNOWN_ERROR, e, exc_info=True)

View File

@@ -0,0 +1,43 @@
import logging
import os
LOG_FILE_PATH = os.path.join("logs", "helper.log")
os.makedirs(os.path.dirname(LOG_FILE_PATH), exist_ok=True)
file_handler = logging.FileHandler(LOG_FILE_PATH, mode='w', encoding="utf-8")
file_handler.setLevel(logging.DEBUG)
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)
logging.basicConfig(
level=logging.DEBUG,
format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
handlers=[
file_handler,
console_handler
]
)
def get_logger(name: str) -> logging.Logger:
return logging.getLogger(name)
class LogText:
CLASS_INITIALIZATION = "initializing"
GETTING_PROTOTYPES_FROM_YAML = "Getting prototypes from YAML files"
GETTING_PROTOTYPES_FROM_FTL = "Getting prototypes from FTL files"
FORMING_FINAL_DICTIONARY = "Forming the final prototypes dictionary"
SAVING_LAST_LAUNCH_RESULT = "Saving yaml_parser result to file"
READING_LAST_LAUNCH_RESULT = "Reading data from last launch"
SAVING_FINAL_RESULT = "Saving the final prototypes dictionary to FTL file"
SAVING_DATA_TO_FILE = "Saving data to file"
READING_DATA_FROM_FILE = "Reading data from file"
ERROR_WHILE_WRITING_DATA_TO_FILE = "Error while writing data to a file"
ERROR_WHILE_READING_DATA_FROM_FILE = "Error while writing data to a file"
UNKNOWN_ERROR = "An error occurred during execution"
HAS_BEEN_DELETED = "Has been deleted due to lack of relevant data"
HAS_BEEN_PROCESSED = "Has been processed"
FORMING_FTL_FOR_PROTOTYPE = "Forming FTL for Prototype"

View File

@@ -0,0 +1,3 @@
from .base_parser import BaseParser
from .yaml import YamlParser
from .fluent import FtlParser, create_ftl

View File

@@ -0,0 +1,28 @@
import os
from abc import ABC, abstractmethod
from LocalizationHelper.prototype import Prototype
class BaseParser(ABC):
@staticmethod
def _get_files_paths_in_dir(path: str) -> list[str]:
files_paths_lst = []
for dirpath, _, filenames in os.walk(path):
for filename in filenames:
file_path = f"{dirpath}\\{filename}"
files_paths_lst.append(file_path)
return files_paths_lst
@staticmethod
def _check_file_extension(path: str, extension: str) -> bool:
if path.endswith(extension):
return True
return False
@abstractmethod
def get_prototypes(self, prototypes_files_path: str) -> list[Prototype]:
pass

View File

@@ -0,0 +1,3 @@
from .ftl_reader import read_ftl
from .ftl_parser import FtlParser
from .ftl_writer import create_ftl

View File

@@ -0,0 +1,28 @@
from . import read_ftl
from LocalizationHelper import get_logger, LogText
from LocalizationHelper.prototype import Prototype
from LocalizationHelper.parsers import BaseParser
logger = get_logger(__name__)
class FtlParser(BaseParser):
def __init__(self):
logger.debug("%s FtlParser", LogText.CLASS_INITIALIZATION)
def get_prototypes(self, ftl_prototypes_path: str) -> dict[str, Prototype]:
prototypes = {}
ftl_prototypes_files_path = self._get_files_paths_in_dir(ftl_prototypes_path)
for prototype_file_path in ftl_prototypes_files_path:
if not self._check_file_extension(prototype_file_path, "ftl"):
continue
file_prototypes_dict = read_ftl(prototype_file_path)
for prototype_dict in file_prototypes_dict.values():
prototype_obj = Prototype(prototype_dict)
logger.debug("%s: %s", LogText.HAS_BEEN_PROCESSED, prototype_obj)
prototypes[prototype_obj.id] = prototype_obj
return prototypes

View File

@@ -1,14 +1,15 @@
def read_ftl(paths: tuple) -> dict: from LocalizationHelper import get_logger, LogText
"""
The function looks at each line of the ftl logger = get_logger(__name__)
file and determines by the indentation in the line whether
it is a new prototype or an attribute of an old one.
""" def read_ftl(path: str) -> dict:
prototypes = {} prototypes = {}
last_prototype = "" last_prototype = ""
path, error_log_path = paths
try: try:
logger.debug("%s: %s", LogText.READING_DATA_FROM_FILE, path)
with open(path, encoding="utf-8") as file: with open(path, encoding="utf-8") as file:
for line in file.readlines(): for line in file.readlines():
if line.startswith("#") or line.startswith("\n"): if line.startswith("#") or line.startswith("\n"):
@@ -19,19 +20,19 @@ def read_ftl(paths: tuple) -> dict:
proto_id = proto_id.replace("ent-", "") proto_id = proto_id.replace("ent-", "")
last_prototype = proto_id last_prototype = proto_id
prototypes[proto_id] = { prototypes[proto_id] = {
"name": proto_name.strip(), "id": proto_id,
"desc": None, "name": proto_name.strip(),
"suffix": None "description": None,
} "suffix": None
}
else: else:
if "desc" in line: if "desc" in line:
attr = "desc" attr = "description"
elif "suffix" in line: elif "suffix" in line:
attr = "suffix" attr = "suffix"
prototypes[last_prototype][attr] = line.split(" = ")[-1].strip() prototypes[last_prototype][attr] = line.split(" = ", 1)[1].strip()
except Exception as e: except Exception as e:
with open(error_log_path, "a") as file: logger.error("%s: %s - %s", LogText.ERROR_WHILE_READING_DATA_FROM_FILE, path, e, exc_info=True)
file.write(f"FTL-ERROR:\nAn error occurred while reading a file {path}, error - {e}\n") else:
return prototypes
return prototypes

View File

@@ -0,0 +1,23 @@
from LocalizationHelper import get_logger, LogText
from LocalizationHelper.prototype import Prototype
logger = get_logger(__name__)
INDENT = " "
def create_ftl(prototype: Prototype) -> str:
logger.debug("%s: %s", LogText.FORMING_FTL_FOR_PROTOTYPE, prototype.attrs_dict)
ftl = ""
ftl += f"ent-{prototype.id} = {prototype.name}\n"
if prototype.description:
ftl += f"{INDENT}.desc = {prototype.description}\n"
if prototype.suffix:
ftl += f"{INDENT}.suffix = {prototype.suffix}\n"
ftl += "\n"
return ftl

View File

@@ -0,0 +1,2 @@
from .yaml_reader import read_yaml
from .yaml_parser import YamlParser

View File

@@ -0,0 +1,29 @@
from . import read_yaml
from LocalizationHelper import get_logger, LogText
from LocalizationHelper.prototype import Prototype, check_prototype_attrs
from LocalizationHelper.parsers import BaseParser
logger = get_logger(__name__)
class YamlParser(BaseParser):
def __init__(self):
logger.debug("%s YamlParser", LogText.CLASS_INITIALIZATION)
def get_prototypes(self, yaml_prototypes_path: str) -> dict[str, Prototype]:
prototypes = {}
yaml_prototypes_files_path = self._get_files_paths_in_dir(yaml_prototypes_path)
for prototype_file_path in yaml_prototypes_files_path:
if not self._check_file_extension(prototype_file_path, "yml"):
continue
file_prototypes_list: list[dict] = read_yaml(prototype_file_path)
if file_prototypes_list:
for prototype_dict in file_prototypes_list:
prototype_obj = Prototype(prototype_dict)
if check_prototype_attrs(prototype_obj):
logger.debug("%s: %s", LogText.HAS_BEEN_PROCESSED, prototype_obj)
prototypes[prototype_obj.id] = prototype_obj
return prototypes

View File

@@ -0,0 +1,31 @@
import yaml
from LocalizationHelper import get_logger, LogText
logger = get_logger(__name__)
# taken from - https://github.com/poeMota/map-converter-ss14/blob/master/src/Yaml/yaml.py
def unknown_tag_constructor(loader, tag_suffix, node):
if isinstance(node, yaml.ScalarNode):
value = loader.construct_scalar(node) # Node
elif isinstance(node, yaml.SequenceNode):
value = loader.construct_sequence(node) # List
elif isinstance(node, yaml.MappingNode):
value = loader.construct_mapping(node) # Dict
else:
raise TypeError(f"Unknown node type: {type(node)}")
return {tag_suffix: value}
yaml.add_multi_constructor('!', unknown_tag_constructor)
def read_yaml(path: str) -> list[dict]:
try:
logger.debug("%s: %s", LogText.READING_DATA_FROM_FILE, path)
with open(path, encoding="utf-8") as file:
data = yaml.full_load(file)
return data
except Exception as e:
logger.error("%s: %s - %s", LogText.ERROR_WHILE_READING_DATA_FROM_FILE, path, e, exc_info=True)

View File

@@ -0,0 +1,99 @@
class Prototype:
def __init__(self, prototype: dict):
self._name = prototype.get("name")
self._description = prototype.get("description")
self._parent = prototype.get("parent")
self._id = prototype.get("id")
self._suffix = prototype.get("suffix")
self._attrs_dict = {
"id": self._id,
"name": self._name,
"description": self._description,
"parent": self._parent,
"suffix": self._suffix
}
@property
def name(self):
return self._name
@name.setter
def name(self, new_name: str):
self._name = new_name
self._attrs_dict["name"] = new_name
@property
def description(self):
return self._description
@description.setter
def description(self, new_description: str):
self._description = new_description
self._attrs_dict["description"] = new_description
@property
def parent(self):
return self._parent
@parent.setter
def parent(self, new_parent: str):
self._parent = new_parent
self._attrs_dict["parent"] = new_parent
@property
def id(self):
return self._id
@id.setter
def id(self, new_id: str):
self._id = new_id
self._attrs_dict["id"] = new_id
@property
def suffix(self):
return self._suffix
@suffix.setter
def suffix(self, new_suffix: str):
self._suffix = new_suffix
self._attrs_dict["suffix"] = new_suffix
@property
def attrs_dict(self):
return self._attrs_dict
def set_attrs_dict_value(self, key, value):
self._attrs_dict[key] = value
if key == "name":
self._name = value
elif key == "description":
self._description = value
elif key == "id":
self._id = value
elif key == "parent":
self._parent = value
elif key == "suffix":
self._suffix = value
def __repr__(self):
return str(self._attrs_dict)
def check_prototype_attrs(prototype: Prototype, with_parent_check: bool = True) -> bool:
if prototype.name:
# if prototype.id == "CP14BaseWooden":
# print(prototype.name)
return True
elif prototype.description:
# if prototype.id == "CP14BaseWooden":
# print(prototype.description)
return True
elif prototype.suffix:
return True
# In some cases a parent can be a list (because of multiple parents),
# the game will not be able to handle such cases in ftl files.
elif with_parent_check and prototype.parent and not isinstance(prototype.parent, list):
return True
return False

View File

@@ -6,7 +6,7 @@ A script to help support game translation
## The right version of python: 3.10+ ## The right version of python: 3.10+
## Deployment ## Dependencies Install
in console in console
@@ -31,10 +31,18 @@ or just run
run.bat run.bat
``` ```
## Demo ## Tests
in console
```bash
python run_tests.py
```
or just run
```bash
tests.bat
```
https://youtu.be/5HfDjLzhjA4
## License ## License
Author: asqw: Discord - .asqw, GitHub - comasqw Author: asqw: Discord - .asqw, GitHub - comasqw

View File

@@ -1,38 +0,0 @@
import os
import json
class BaseParser:
"""
BaseParser, contains the basic functions for the yml_parser module in the yml_parser package
and for the ftl_parser module in the ftl_parser package
"""
def __init__(self, paths: tuple):
self.path, self.errors_path = paths
def _get_files_paths(self) -> list:
"""
The method gets the path to the yml folder of localization prototypes/files, e.g. "ftl",
then with the help of os library goes through each file in
the folder and creates a path for it, e.g. "ftl/objects.ftl".
"""
files_paths_lst = []
for dirpath, _, filenames in os.walk(self.path):
for filename in filenames:
path = f"{dirpath}\\{filename}"
files_paths_lst.append(path)
return files_paths_lst
@staticmethod
def save_to_json(prototypes: dict, path: str) -> None:
with open(path, 'w') as json_file:
json.dump(prototypes, json_file, indent=4)
@staticmethod
def _check_file_extension(path: str, extension: str) -> bool:
if path.endswith(extension):
return True
return False

View File

@@ -1,8 +0,0 @@
{
"paths": {
"prototypes": "../../../Resources/Prototypes/_CP14/Entities",
"localization": "../../../Resources/Locale/ru-RU/_CP14/_PROTO/entities",
"error_log_path": "logs/errors.log",
"yml_parser_last_launch": "last_launch/yml_parser.json"
}
}

View File

@@ -1,17 +0,0 @@
import json
def create_ftl(key: str, prototype: dict) -> str:
ftl = ""
ftl += f"ent-{key} = {prototype["name"]}\n"
if prototype["desc"] is not None:
ftl += f" .desc = {prototype["desc"]}\n"
if prototype["suffix"] is not None:
ftl += f" .suffix = {prototype["suffix"]}\n"
ftl += "\n"
return ftl

View File

@@ -1 +0,0 @@
from .ftl_parser import FTLParser

View File

@@ -1,26 +0,0 @@
from fluent import ftl_reader
from base_parser import BaseParser
class FTLParser(BaseParser):
"""
The class inherits from the "BaseParser" class, parses ftl files of localization.
"""
def ftl_parser(self) -> dict:
"""
The function gets the path, then with the help of the os library
goes through each file,checks that the file extension is "ftl",
then reads it through the "ftl_reader" module of the "fluent" package.
"""
prototypes = {}
for path in self._get_files_paths():
if not self._check_file_extension(path, ".ftl"):
continue
file = ftl_reader.read_ftl((path, self.errors_path))
prototypes.update(file)
return prototypes

View File

@@ -1,136 +0,0 @@
import json
import os
from yml_parser import YMLParser
from ftl_parser import FTLParser
from fluent import ftl_writer
# Config constants
CONFIG_PATH = "config.json"
CONFIG_PATHS_KEY_NAME = "paths"
PROTOTYPES_PATH_IN_CONFIG = "prototypes"
FTL_PATH_IN_CONFIG = "localization"
ERRORS_LOG_PATH_IN_CONFIG = "error_log_path"
PARSED_PROTOTYPES_PATH_IN_LAST_LAUNCH = "yml_parser_last_launch"
LAST_LAUNCH_PROTOTYPES_DIR_NAME = "last_launch"
NAME_OF_FILE_TO_SAVE = "entities.ftl"
class LocalizationHelper:
if not os.path.isdir(LAST_LAUNCH_PROTOTYPES_DIR_NAME):
os.mkdir(LAST_LAUNCH_PROTOTYPES_DIR_NAME)
def __init__(self, config_path: str):
self._config = self._read_config(config_path)
self._prototypes_path, self._localization_path, self._errors_log_path, self._yml_parser_last_launch = self._get_paths()
self._clear_logs()
self.prototypes_dict_yml = YMLParser((self._prototypes_path, self._errors_log_path)).yml_parser()
self.prototypes_dict_ftl = FTLParser((self._localization_path, self._errors_log_path)).ftl_parser()
self._check_changed_attrs()
self.prototypes = {**self.prototypes_dict_yml, **self.prototypes_dict_ftl}
def _clear_logs(self):
with open(self._errors_log_path, "w") as file:
file.write("")
@staticmethod
def _read_config(config_path: str) -> dict:
with open(config_path, "r", encoding="utf-8") as file:
return json.load(file)
def _get_paths(self) -> tuple:
paths_dict = self._config[CONFIG_PATHS_KEY_NAME]
prototypes_path = paths_dict[PROTOTYPES_PATH_IN_CONFIG]
localization_path = paths_dict[FTL_PATH_IN_CONFIG]
errors_log_path = paths_dict[ERRORS_LOG_PATH_IN_CONFIG]
yml_parser_last_launch = paths_dict[PARSED_PROTOTYPES_PATH_IN_LAST_LAUNCH]
return prototypes_path, localization_path, errors_log_path, yml_parser_last_launch
def _check_changed_attrs(self):
"""
What error it fixes - without this function, changed attributes of prototypes that have not been changed in
localization files will simply not be added to the original ftl file, because the script first of all takes data
from localization files, if they exist, of course
The function gets the data received during the last run of the script, and checks if some attribute from
the last run has been changed,then simply replaces with this attribute the attribute
of the prototype received during parsing of localization files.
"""
if os.path.isfile(self._yml_parser_last_launch):
with open(self._yml_parser_last_launch, 'r', encoding='utf-8') as file:
last_launch_prototypes = json.load(file)
if last_launch_prototypes:
for prototype, last_launch_attrs in last_launch_prototypes.items():
if prototype in self.prototypes_dict_yml:
if prototype in self.prototypes_dict_ftl:
attrs = self.prototypes_dict_ftl[prototype]
proto_attrs_in_yml = self.prototypes_dict_yml[prototype]
for key, value in proto_attrs_in_yml.items():
if value != last_launch_attrs.get(key):
attrs[key] = value
self.prototypes_dict_ftl[prototype] = attrs
else:
if prototype in self.prototypes_dict_ftl:
del self.prototypes_dict_ftl[prototype]
@staticmethod
def _save_result(entities: str) -> None:
with open(NAME_OF_FILE_TO_SAVE, "w", encoding="utf-8") as file:
file.write(entities)
print(f"{NAME_OF_FILE_TO_SAVE} has been created\n")
@staticmethod
def _print_errors_log_info(errors_log_path: str, prototypes: dict) -> None:
with open(errors_log_path, "r") as file:
errors = file.read()
successful_count = len(prototypes) - errors.count("ERROR")
print(f"""Of the {len(prototypes)} prototypes, {successful_count} were successfully processed.
Errors can be found in {errors_log_path}
Number of errors during YML processing - {errors.count("YML-ERROR")}
Number of errors during FTL processing - {errors.count("FTL-ERROR")}
Number of errors during data extraction and creation of new FTL - {errors.count("RETRIEVING-ERROR")}""")
def main(self):
entities_ftl = ""
for prototype, prototype_attrs in self.prototypes.items():
try:
# This fragment is needed to restore some attributes after connecting the dictionary of
# prototypes parsed from ftl with the dictionary of prototypes parsed from yml.
if prototype in self.prototypes_dict_yml:
parent = self.prototypes_dict_yml[prototype]["parent"]
if parent and not isinstance(parent, list) and parent in self.prototypes_dict_yml:
if not prototype_attrs.get("name"):
prototype_attrs["name"] = f"{{ ent-{parent} }}"
if not prototype_attrs.get("desc"):
prototype_attrs["desc"] = f"{{ ent-{parent}.desc }}"
if not prototype_attrs.get("suffix"):
if self.prototypes_dict_yml[prototype].get("suffix"):
prototype_attrs["suffix"] = self.prototypes_dict_yml[prototype]["suffix"]
if any(prototype_attrs[attr] is not None for attr in ("name", "desc", "suffix")):
proto_ftl = ftl_writer.create_ftl(prototype, self.prototypes[prototype])
entities_ftl += proto_ftl
except Exception as e:
with open(self._errors_log_path, "a") as file:
print(prototype, prototype_attrs)
file.write(
f"RETRIEVING-ERROR:\nAn error occurred while retrieving data to be written to the file - {e}\n")
self._save_result(entities_ftl)
self._print_errors_log_info(self._errors_log_path, self.prototypes)
if __name__ == '__main__':
helper = LocalizationHelper(CONFIG_PATH)
helper.main()

View File

@@ -0,0 +1,11 @@
from LocalizationHelper import get_logger, LocalizationHelper
YAML_FILES_PATH = "../../../Resources/Prototypes/_CP14/Entities"
FTL_FILES_PATH = "../../../Resources/Locale/ru-RU/_CP14/_PROTO/entities"
if __name__ == '__main__':
logger = get_logger(__name__)
logger.info("Starting...")
helper = LocalizationHelper()
helper.main(YAML_FILES_PATH, FTL_FILES_PATH)

View File

@@ -1 +1 @@
PyYAML==6.0.1 PyYAML~=6.0.2

View File

@@ -1,3 +1,3 @@
@echo off @echo off
python localization_helper.py python main.py
pause pause

View File

@@ -1 +0,0 @@
from .yml_parser import YMLParser

View File

@@ -1,76 +0,0 @@
import yaml
from base_parser import BaseParser
import re
class YMLParser(BaseParser):
"""
The class inherits from the "BaseParser" class, parses yml prototypes.
"""
@staticmethod
def _check_proto_attrs(prototype: dict) -> bool:
"""
The function checks that the prototype at least has some attribute from the "attrs_lst".
"""
attrs_lst = ["name", "description", "suffix"]
# In some cases a parent can be a list (because of multiple parents),
# the game will not be able to handle such cases in ftl files.
if not isinstance(prototype.get("parent"), list):
attrs_lst.append("parent")
return any(prototype.get(attr) is not None for attr in attrs_lst)
@staticmethod
def _get_proto_attrs(prototypes: dict, prototype: dict) -> None:
prototypes[prototype.get("id")] = {
"parent": prototype.get("parent"),
"name": prototype.get("name"),
"desc": prototype.get("description"),
"suffix": prototype.get("suffix")
}
def _load_proto(self, file, path) -> list[dict]:
content_str = file.read()
prototypes_lst = re.split(r"\n(?=- type:)", content_str)
prototypes = []
for proto in prototypes_lst:
try:
prototype_str = ""
for line in proto.splitlines():
if "components:" in line:
break
prototype_str += f"{line}\n"
prototype = yaml.safe_load(prototype_str)
if prototype is None:
continue
prototypes.append(prototype[0])
except Exception as e:
with open(self.errors_path, "a") as error_file:
error_file.write(
f"YML-ERROR:\nAn error occurred during prototype processing {path}, error - {e}\n")
return prototypes
def yml_parser(self) -> dict:
"""
The function gets the path, then with the help of the os library
goes through each file,checks that the file extension is "yml",
then processes the file using the "PyYaml" library
"""
prototypes = {}
for path in self._get_files_paths():
if not self._check_file_extension(path, ".yml"):
continue
with open(path, encoding="utf-8") as file:
content = self._load_proto(file, path)
if content is not None:
for prototype in content:
if self._check_proto_attrs(prototype):
self._get_proto_attrs(prototypes, prototype)
return prototypes