Files
Kvan7 06c396f0cc v0.5.0 (#308)
* change discord id #278

* Update config & data

* Commit mockup image

* update mockup

* feat: filter quick update (#247)

* feat: filter generator in renderer

* feat: filter generator in main

* chore: some polishing

* feat: about section

* feat: i18n (-ish), only en text

* chore: cleanup

* chore: clarification regarding original filter

* chore: safety checks to ignore empty or not full identifier, or empty filters all together

* fix: linting fixes

* chore: locked version of winreg

* fix: dont hide divine orbs :O

* fix: set filter to hide would always hide it, even after turning it back
fix: updated config version

* fix: moved ignore low lvl area items down in filter order

* fix: drop winreg in favor of existing solution

* Change config file handling

* add in empty string check

* feat: changing filter generator into filter updater

* chore: simplify generator pages

* chore: removed code that is no longer needed

* chore: linter fixes

* fix: remove debug text

* feat: customizable folder path

* Add russian localization

* Merge commit 'ec35412d2694eb6a677415893776939b496b8aae'

* Revert "Merge commit 'ec35412d2694eb6a677415893776939b496b8aae'"

This reverts commit 8a3d84af91.

* Merge branch 'dev' into pr/tmakuch/247

* update config version

* Add note about case sensitivity

* run linter/formatter

* Features/germanLang (#300)

* add app_i18n.json

Co-authored-by: @professorspoon

* Update files

* official site

* Build files

* update description parser?

* [Not Recognized Modifier] - %phys #298

* add new unique stats, don't have roll values yet

* Update uniques again

* [PoE2] - Checking log book hard crashes #305

* run data script again?

* data files update for 0.1.1

* version bump
2025-01-16 15:57:50 -06:00

240 lines
8.2 KiB
Python

import logging
import os
from collections import defaultdict
from pprint import pprint
from services.statNameBuilder import convert_stat_name
logger = logging.getLogger(__name__)
class Description:
english_ref = None
def __init__(
self, lines: list[str], lang="English", log_level=logging.FATAL, manual=None
):
self.lang = lang
if manual is not None:
self.id = manual["id"]
self.data = manual["data"]
return
logger.debug(f"Initializing Description with lang: {lang}")
if not lines[0].startswith("description"):
logger.error("Invalid description block.")
raise ValueError("Invalid description block")
self.id = self.parse_id(lines)
logger.debug(f"Parsed ID: {self.id}")
self.data = self.parse_lines(lines, self.id)
def __str__(self):
return f"Description(id={self.id}, english_ref={self.english_ref})"
def parse_id(self, lines: list[str]) -> str:
assert lines[0].startswith("description")
line = lines[1].strip()
logger.debug(f"Parsing ID from line: {line}")
return line.strip()[2:].replace('"', "")
def parse_lines(self, lines: list[str], id: str) -> dict:
assert lines[0].startswith("description")
sanitized_lines = self.sanitize_lines(lines)
logger.debug(f"Sanitized lines: {sanitized_lines}")
blocks = self.parse_blocks(sanitized_lines)
lang_blocks = [self.simplify_block(block) for block in blocks]
# Create the lang_dict
lang_dict = {
lang: self.get_matchers(mod_lines, lang == "English")
for lang, mod_lines in lang_blocks
}
logger.debug(f"Language dictionary created: {lang_dict}")
# Filter lang_dict for only English and self.lang
# filtered_lang_dict = {
# lang: matchers
# for lang, matchers in lang_dict.items()
# if lang in ["English", self.lang]
# }
# logger.debug(f"Filtered language dictionary: {filtered_lang_dict}")
return lang_dict
def sanitize_lines(self, lines: list[str]) -> list[str]:
logger.debug(f"Sanitizing lines: {lines}")
return [line.strip() for line in lines[1:]]
def parse_blocks(self, lines: list[str]) -> list[list[str]]:
logger.debug("Parsing lines into blocks.")
blocks = [[]]
for i in range(len(lines)):
if lines[i].startswith("lang"):
blocks.append([])
blocks[-1].append(lines[i])
logger.debug(f"Created {len(blocks)} blocks.")
return blocks
def simplify_block(
self, block: list[str]
) -> tuple[str, list[tuple[str, str, str | None]]]:
lang = self.extract_lang(block[0])
lines = block[2:]
# TODO: Parse for more complex line sets
# pos_line = lines[0].strip()
# out_lines = [pos_line[pos_line.find('"') + 1 : pos_line.rfind('"')]]
out_lines = []
# if len(lines) > 1:
# neg_line = lines[1].strip()
# if "lang" not in neg_line and "negate" in neg_line:
# # mod has a negated version
# end = neg_line.find("negate")
# neg_line = neg_line[neg_line.find('"') + 1 : end + len("negate")]
# out_lines.append(neg_line)
for line in lines:
line = line.strip()
left_quote = line.find('"')
right_quote = line.rfind('"')
if left_quote != -1 and right_quote != -1:
left_side = line[:left_quote].strip()
out_line = line[left_quote + 1 : right_quote].strip()
right_side = None
if right_quote + 1 < len(line):
right_side = line[right_quote + 1 :].strip()
out_lines.append((left_side, out_line, right_side))
logger.debug(f"Simplified block for lang: {lang}, lines: {out_lines}")
return lang, out_lines
def extract_lang(self, line: str) -> str:
line = line.strip()
if not line.startswith("lang"):
return "English"
return line[line.find('"') + 1 : line.rfind('"')]
def get_matchers(
self, lines: list[tuple[str, str, str | None]], is_en: bool
) -> list[dict[str, str | bool]]:
"""
Generate matchers from the parsed lines.
Each line is a tuple (left_side, out_line, right_side).
Negation is determined by the presence of 'negate' in right_side.
Groups matchers by 'string' and aggregates other fields into sets.
"""
logger.debug("Getting matchers.")
temp_matchers = []
for left_side, out_line, right_side in lines:
# Use `out_line` as the main part of the stat to convert
stat_name = convert_stat_name(out_line)
if stat_name is None:
logger.warning(
f"Stat name could not be converted. Skipping line: {out_line}"
)
continue
matcher = stat_name
# Remove prefixes
if matcher[0] == "+":
matcher = matcher[1:]
# Detect negations based on presence of "negate" in `right_side`
has_negate = "negate" in (right_side or "")
# Replace "+#" with "#"
if "+#" in matcher:
matcher = matcher.replace("+#", "#")
# Append the matcher
temp_matchers.append(
{
"string": matcher,
"negate": has_negate,
"left_side": left_side.strip(), # Include left metadata for context
"right_side": right_side.strip()
if right_side
else None, # Optional right metadata
"stat_name": stat_name,
}
)
# # Set the English reference if applicable
# if is_en and self.english_ref is None:
# self.english_ref = stat_name
# Group matchers by 'string' and aggregate other fields
grouped_matchers = defaultdict(
lambda: {
"negate": set(),
"left_side": set(),
"right_side": set(),
"stat_name": set(),
}
)
for matcher in temp_matchers:
string_value = matcher["string"]
grouped_matchers[string_value]["negate"].add(matcher["negate"])
grouped_matchers[string_value]["left_side"].add(matcher["left_side"])
if matcher["right_side"]:
grouped_matchers[string_value]["right_side"].add(matcher["right_side"])
grouped_matchers[string_value]["stat_name"].add(matcher["stat_name"])
# Format grouped matchers into the final output
matchers = []
for string_value, data in grouped_matchers.items():
negate = any(data["negate"])
left_side = [str(x).split(" ")[0] for x in data["left_side"]]
right_side = list(data["right_side"])
# Determine the value based on specific rules
value = None
if "#|-100" in left_side:
value = -1
elif "100|#" in left_side:
value = 100
else:
for item in left_side:
if item.isdigit(): # Check if `item` is a number
value = int(item)
break
if value is None and not negate and is_en and self.english_ref is None:
self.english_ref = data["stat_name"].pop()
matcher_data = {
"string": string_value,
"negate": negate,
}
if value is not None:
matcher_data["value"] = value
matchers.append(matcher_data)
if is_en:
logger.info(f"Final matchers: {matchers}")
return matchers
if __name__ == "__main__":
# Test functionality
with open(
f"{os.path.dirname(os.path.realpath(__file__))}/singleDesc.csd",
"r",
encoding="utf-8",
) as f:
lines = f.readlines()
logger.debug(f"Loaded lines for testing: {lines}")
desc = Description(lines, lang="English")
print(desc.english_ref)