fix localization helper & protos descriptions in ftl (#282)

* fix localization helper & protos descriptions in ftl

* Update main.py

* Update objects.ftl

* добавил ярлык напрямую к файлу локализации

* megasuperduperpuperTWEAK

* Update yml_parser.py

* Update yml_parser.py

* Update yml_parser.py

* Update yml_parser.py

* Update main.py

* tweak

---------

Co-authored-by: Ed <96445749+TheShuEd@users.noreply.github.com>
Co-authored-by: Ed <edwardxperia2000@gmail.com>
This commit is contained in:
comasqw
2024-06-27 23:07:25 +04:00
committed by GitHub
parent 4d5fa91a39
commit b592842a66
10 changed files with 201 additions and 97 deletions

View File

@@ -63,20 +63,6 @@ ent-CP14Pestle = пестик
ent-CP14Mortar = ступка
.desc = Алхимическая прочная тарелка для измельчения реагентов
ent-CP14VialTiny = крохотный флакон
.desc = Прочный стеклянный флакон для хранения небольшого количества жидкости.
ent-CP14BaseVialFragile = маленький флакон
.desc = Хрупкий стеклянный флакон для хранения небольшого количества жидкости.
ent-CP14VialSmallBloodgrassSap = { ent-CP14VialTiny }
.desc = { ent-CP14VialTiny.desc }
ent-CP14VialSmallAgaricMushroom = { ent-CP14VialTiny }
.desc = { ent-CP14VialTiny.desc }
ent-CP14VialSmallWildSage = { ent-CP14VialTiny }
.desc = { ent-CP14VialTiny.desc }
ent-CP14VialSmallChromiumSlime = { ent-CP14VialTiny }
.desc = { ent-CP14VialTiny.desc }
# Tools
ent-CP14OldLantern = Старая Лампа
.desc = Пережиток прошлого техномагии. Большой, тяжелый, непрактичный. Таким приятно разве что бить по голове.

Binary file not shown.

View File

@@ -0,0 +1,38 @@
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,6 +1,8 @@
{
"paths": {
"prototypes": "../../../Resources/Prototypes/_CP14/Entities",
"localization": "../../../Resources/Locale/ru-RU/_CP14/_PROTO/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,4 +1,4 @@
def read_ftl(path: str) -> dict:
def read_ftl(paths: tuple) -> dict:
"""
The function looks at each line of the ftl
file and determines by the indentation in the line whether
@@ -9,7 +9,7 @@ def read_ftl(path: str) -> dict:
}
last_prototype = ""
path, error_log_path = paths
try:
with open(path, encoding="utf-8") as file:
for line in file.readlines():
@@ -20,7 +20,8 @@ def read_ftl(path: str) -> dict:
proto_id, proto_name = line.split(" = ")
proto_id = proto_id.replace("ent-", "")
last_prototype = proto_id
prototypes[proto_id] = {"name": proto_name.strip(),
prototypes[proto_id] = {
"name": proto_name.strip(),
"desc": None,
"suffix": None
}
@@ -32,7 +33,7 @@ def read_ftl(path: str) -> dict:
prototypes[last_prototype][attr] = line.split(" = ")[-1].strip()
except Exception as e:
with open("logs/errors_log.txt", "a") as file:
with open(error_log_path, "a") as file:
file.write(f"FTL-ERROR:\nAn error occurred while reading a file {path}, error - {e}\n")
return prototypes

View File

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

View File

@@ -1,23 +1,26 @@
from fluent import ftl_reader
import os
from base_parser import BaseParser
def ftl_parser(path: str) -> dict:
class FTLParser(BaseParser):
"""
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.
The class inherits from the "BaseParser" class, parses ftl files of localization.
"""
prototypes = {}
for dirpath, _, filenames in os.walk(path):
for filename in filenames:
path = f"{dirpath}\\{filename}"
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 = {}
if not filename.endswith(".ftl"):
for path in self.get_files_paths():
if not self.check_file_extension(path, ".ftl"):
continue
file = ftl_reader.read_ftl(path)
file = ftl_reader.read_ftl((path, self.errors_path))
prototypes.update(file)
return prototypes
return prototypes

View File

@@ -1,6 +1,7 @@
import json
from yml_parser import yml_parser
from ftl_parser import ftl_parser
import os
from yml_parser import YMLParser
from ftl_parser import FTLParser
from fluent import ftl_writer
@@ -12,30 +13,91 @@ def read_config():
def get_paths(config: dict):
prototypes_path = config["paths"]["prototypes"]
localization_path = config["paths"]["localization"]
return prototypes_path, localization_path
errors_log_path = config["paths"]["error_log_path"]
yml_parser_last_launch = config["paths"]["yml_parser_last_launch"]
return prototypes_path, localization_path, errors_log_path, yml_parser_last_launch
def print_errors_log_info(errors_log_path: str, all_prototypes: dict) -> None:
with open(errors_log_path, "r") as file:
errors = file.read()
successful_count = len(all_prototypes) - errors.count("ERROR")
print(f"""Of the {len(all_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 check_changed_attrs(yml_parser_last_launch: str, prototypes_dict: dict, localization_dict: dict):
"""
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(yml_parser_last_launch):
with open(yml_parser_last_launch, 'r', encoding='utf-8') as file:
last_launch_prototypes = json.load(file)
for prototype, proto_attrs_in_prototypes in prototypes_dict.items():
if prototype in last_launch_prototypes and prototype in localization_dict:
attrs = localization_dict[prototype]
last_launch_prototype_attrs = last_launch_prototypes[prototype]
for key, value in proto_attrs_in_prototypes.items():
if value != last_launch_prototype_attrs[key]:
attrs[key] = value
localization_dict[prototype] = attrs
def save_result(entities: str, file_name: str) -> None:
with open(file_name, "w", encoding="utf-8") as file:
file.write(entities)
print(f"{file_name} has been created\n")
def main():
"""
The function reads the config, gets paths to prototypes and localization files,
and through parsers gets two dictionaries with information about prototypes,
and creates one common vocabulary.
The function gets paths, creates dictionaries with the help of parsers,
performs various checks, and finally creates ftl file.
"""
with open("logs/errors_log.txt", "w") as file:
config = read_config()
prototypes_path, localization_path, errors_log_path, yml_parser_last_launch = get_paths(config)
if not os.path.isdir("last_launch"):
os.mkdir("last_launch")
with open(errors_log_path, "w") as file:
file.write("")
config = read_config()
prototypes_path, localization_path = get_paths(config)
prototypes_dict, localization_dict = yml_parser.yml_parser(prototypes_path), ftl_parser.ftl_parser(localization_path)
yml_parser = YMLParser((prototypes_path, errors_log_path))
prototypes_dict = yml_parser.yml_parser()
ftl_parser = FTLParser((localization_path, errors_log_path))
localization_dict = ftl_parser.ftl_parser()
check_changed_attrs(yml_parser_last_launch, prototypes_dict, localization_dict)
with open(yml_parser_last_launch, 'w') as json_file:
json.dump(prototypes_dict, json_file, indent=4)
# This is where the two dictionaries are merged, and prototypes from
# the localization_dict dictionary are preferably selected.
all_prototypes = {**prototypes_dict, **localization_dict}
entities_ftl = ""
"""
The function traverses each prototype from the dictionary, checks if it has a parent,
and performs certain checks on the attributes of the parent if the prototype does not have its own attributes.
The function traverses each prototype from the dictionary, checks if it has a parent,
and performs certain checks on the attributes of the parent if the prototype does not have its own attributes.
"""
for prototype in all_prototypes:
@@ -44,38 +106,31 @@ def main():
try:
if prototype in prototypes_dict:
prototype_attrs["parent"] = prototypes_dict[prototype]["parent"]
parent = prototype_attrs["parent"]
if not prototype_attrs.get("name"):
prototype_attrs["name"] = f"{{ ent-{prototype_attrs["parent"]} }}"
if not prototype_attrs["desc"]:
if not prototype_attrs.get("name"):
prototype_attrs["name"] = f"{{ ent-{parent} }}"
if not prototype_attrs.get("desc"):
if parent and not isinstance(parent, list) and prototypes_dict.get(parent):
prototype_attrs["desc"] = f"{{ ent-{parent}.desc }}"
if not prototype_attrs.get("suffix"):
if prototypes_dict[prototype].get("suffix"):
prototype_attrs["suffix"] = prototypes_dict[prototype]["suffix"]
proto_ftl = ftl_writer.create_ftl(prototype, all_prototypes[prototype])
entities_ftl += proto_ftl
except Exception as e:
with open("logs/errors_log.txt", "a") as file:
with open(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")
file_name = "entities.ftl"
with open(file_name, "w", encoding="utf-8") as file:
file.write(entities_ftl)
save_result(entities_ftl, file_name)
print(f"{file_name} has been created\n")
with open("logs/errors_log.txt", "r") as file:
errors = file.read()
successful_count = len(all_prototypes) - errors.count("ERROR")
print(f"""Of the {len(all_prototypes)} prototypes, {successful_count} were successfully processed.
Errors can be found in 'logs/errors_log.txt'.
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")}""")
print_errors_log_info(errors_log_path, all_prototypes)
if __name__ == '__main__':

View File

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

View File

@@ -1,52 +1,69 @@
import yaml
import os
from base_parser import BaseParser
def check_proto_attrs(prototype: dict) -> bool:
return any(prototype.get(attr) is not None for attr in ["name", "description", "suffix"])
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 yml_parser(path: str) -> dict:
class YMLParser(BaseParser):
"""
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 processes the file using the "PyYaml" library
The class inherits from the "BaseParser" class, parses yml prototypes.
"""
prototypes = {}
for dirpath, _, filenames in os.walk(path):
for filename in filenames:
path = f"{dirpath}\\{filename}"
@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")
if not filename.endswith(".yml"):
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")
}
@staticmethod
def create_proto(file) -> str:
proto = ""
for line in file.readlines():
# The PyYaml library cannot handle the following SpaceStation 14 prototype syntax - !type: ...
# We need to fix this :(
if "!type" in line:
continue
proto += line
return proto
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
try:
with open(path, encoding="utf-8") as file:
proto = ""
for line in file.readlines():
# The PyYaml library cannot handle the following SpaceStation 14 prototype syntax - !type: ...
if "!type" in line:
continue
proto += line
proto = self.create_proto(file)
data = yaml.safe_load(proto)
except Exception as e:
with open("logs/errors_log.txt", "a") as file:
with open(self.errors_path, "a") as file:
file.write(f"YML-ERROR:\nAn error occurred during prototype processing {path}, error - {e}\n")
else:
if data is not None:
for prototype in data:
if check_proto_attrs(prototype):
get_proto_attrs(prototypes, prototype)
if self.check_proto_attrs(prototype):
self.get_proto_attrs(prototypes, prototype)
return prototypes
return prototypes