Add: Keyword extract

This commit is contained in:
LmeSzinc 2023-05-22 20:20:31 +08:00
parent 36d898b038
commit 50e0ddb954
9 changed files with 252 additions and 40 deletions

View File

@ -206,11 +206,10 @@ def generate_code():
path = os.path.join(AzurLaneConfig.ASSETS_MODULE, module.split('/', maxsplit=1)[0]) path = os.path.join(AzurLaneConfig.ASSETS_MODULE, module.split('/', maxsplit=1)[0])
output = os.path.join(path, 'assets') output = os.path.join(path, 'assets')
gen = CodeGenerator() gen = CodeGenerator()
gen.add('from module.base.button import Button, ButtonWrapper') gen.Import("""
gen.Empty() from module.base.button import Button, ButtonWrapper
gen.Comment('This file was auto-generated, do not modify it manually. To generate:') """)
gen.Comment('``` python -m dev_tools.button_extract ```') gen.CommentAutoGenerage('dev_tools.button_extract')
gen.Empty()
for assets, assets_data in module_data.items(): for assets, assets_data in module_data.items():
has_share = SHARE_SERVER in assets_data has_share = SHARE_SERVER in assets_data
with gen.Object(key=assets, object_class='ButtonWrapper'): with gen.Object(key=assets, object_class='ButtonWrapper'):

View File

@ -0,0 +1,108 @@
import os
import typing as t
from functools import cached_property
from module.base.code_generator import CodeGenerator
from module.config.utils import read_file
from module.logger import logger
from module.ocr.keyword import text_to_variable
UI_LANGUAGES = ['cn', 'cht', 'en', 'jp']
class TextMap:
DATA_FOLDER = ''
def __init__(self, lang: str):
self.lang = lang
@cached_property
def data(self) -> dict[int, str]:
if not TextMap.DATA_FOLDER:
logger.critical('`TextMap.DATA_FOLDER` is empty, please set it to your path to StarRailData')
exit(1)
file = os.path.join(TextMap.DATA_FOLDER, 'TextMap', f'TextMap{self.lang.upper()}.json')
data = {}
for id_, text in read_file(file).items():
data[int(id_)] = text
return data
def find(self, name: t.Union[int, str]) -> tuple[int, str]:
"""
Args:
name:
Returns:
text id (hash in TextMap)
text
"""
if isinstance(name, int) or (isinstance(name, str) and name.isdigit()):
name = int(name)
try:
return name, self.data[name]
except KeyError:
pass
name = str(name)
for row_id, row_name in self.data.items():
if row_id >= 0 and row_name == name:
return row_id, row_name
for row_id, row_name in self.data.items():
if row_name == name:
return row_id, row_name
logger.error(f'Cannot find name: "{name}" in language {self.lang}')
return 0, ''
class KeywordExtract:
def __init__(self):
self.text_map: dict[str, TextMap] = {lang: TextMap(lang) for lang in UI_LANGUAGES}
self.keywords_id: list[int] = []
def find_keyword(self, keyword, lang):
text_map = self.text_map[lang]
return text_map.find(keyword)
def load_keywords(self, keywords: list[str], lang='cn'):
text_map = self.text_map[lang]
self.keywords_id = [text_map.find(keyword)[0] for keyword in keywords]
self.keywords_id = [keyword for keyword in self.keywords_id if keyword != 0]
def write_keywords(
self,
keyword_class,
output_file: str
):
"""
Args:
keyword_class:
keyword_import:
output_file:
"""
gen = CodeGenerator()
gen.Import(f"""
from .classes import {keyword_class}
""")
gen.CommentAutoGenerage('dev_tools.keyword_extract')
for index, keyword in enumerate(self.keywords_id):
_, en = self.find_keyword(keyword, lang='en')
en = text_to_variable(en)
with gen.Object(key=en, object_class=keyword_class):
gen.ObjectAttr(key='id', value=index + 1)
for lang in UI_LANGUAGES:
gen.ObjectAttr(key=lang, value=self.find_keyword(keyword, lang=lang)[1])
gen.write(output_file)
def generate():
ex = KeywordExtract()
ex.load_keywords(['模拟宇宙', '拟造花萼(金)', '拟造花萼(赤)', '凝滞虚影', '侵蚀隧洞', '忘却之庭'])
ex.write_keywords(keyword_class='DungeonNav', output_file='./tasks/dungeon/keywords/nav.py')
ex.load_keywords(['行动摘要', '生存索引', '每日实训'])
ex.write_keywords(keyword_class='DungeonTab', output_file='./tasks/dungeon/keywords/tab.py')
if __name__ == '__main__':
TextMap.DATA_FOLDER = r''
generate()

View File

@ -100,6 +100,19 @@ class CodeGenerator:
line = line.strip() line = line.strip()
self.add(line, comment=True) self.add(line, comment=True)
def CommentAutoGenerage(self, file):
"""
Args:
file: dev_tools.button_extract
"""
# Only leave one blank line at above
if len(self.lines) >= 2:
if self.lines[-2:] == ['\n', '\n']:
self.lines.pop(-1)
self.Comment('This file was auto-generated, do not modify it manually. To generate:')
self.Comment(f'``` python -m {file} ```')
self.Empty()
def List(self, key=None): def List(self, key=None):
if key is not None: if key is not None:
return TabWrapper(self, prefix=str(key) + ' = [', suffix=']') return TabWrapper(self, prefix=str(key) + ' = [', suffix=']')

View File

@ -6,7 +6,7 @@ from typing import ClassVar
from module.exception import ScriptError from module.exception import ScriptError
import module.config.server as server import module.config.server as server
REGEX_PUNCTUATION = re.compile(r'[,.\'",。\-—/\\\n\t]') REGEX_PUNCTUATION = re.compile(r'[ ,.\'",。\-—/\\\n\t()]')
def parse_name(n): def parse_name(n):
@ -14,13 +14,19 @@ def parse_name(n):
return n return n
def text_to_variable(text):
text = re.sub('[ \-]', '_', text)
text = re.sub('[()]', '', text)
return text
@dataclass @dataclass
class Keyword: class Keyword:
id: int id: int
cn: str cn: str
en: str en: str
jp: str jp: str
tw: str cht: str
""" """
Instance attributes and methods Instance attributes and methods
@ -39,8 +45,26 @@ class Keyword:
return parse_name(self.jp) return parse_name(self.jp)
@cached_property @cached_property
def tw_parsed(self) -> str: def cht_parsed(self) -> str:
return parse_name(self.tw) return parse_name(self.cht)
@cached_property
def name(self) -> str:
return text_to_variable(self.en)
def __str__(self):
return self.name
__repr__ = __str__
def __eq__(self, other):
return str(self) == str(other)
def __hash__(self):
return hash(self.name)
def __bool__(self):
return True
def _keywords_to_find(self, in_current_server=False, ignore_punctuation=True): def _keywords_to_find(self, in_current_server=False, ignore_punctuation=True):
if in_current_server: if in_current_server:
@ -62,23 +86,23 @@ class Keyword:
return [self.jp] return [self.jp]
case 'tw': case 'tw':
if ignore_punctuation: if ignore_punctuation:
return [self.tw_parsed] return [self.cht_parsed]
else: else:
return [self.tw] return [self.cht]
else: else:
if ignore_punctuation: if ignore_punctuation:
return [ return [
self.cn_parsed, self.cn_parsed,
self.en_parsed, self.en_parsed,
self.jp_parsed, self.jp_parsed,
self.tw_parsed, self.cht_parsed,
] ]
else: else:
return [ return [
self.cn, self.cn,
self.en, self.en,
self.jp, self.jp,
self.tw, self.cht,
] ]
""" """
@ -114,7 +138,10 @@ class Keyword:
except KeyError: except KeyError:
pass pass
if ignore_punctuation:
name = parse_name(name) name = parse_name(name)
else:
name = str(name)
instance: Keyword instance: Keyword
for instance in cls.instances.values(): for instance in cls.instances.values():
for keyword in instance._keywords_to_find( for keyword in instance._keywords_to_find(
@ -123,27 +150,3 @@ class Keyword:
return instance return instance
raise ScriptError(f'Cannot find a {cls.__name__} instance that matches "{name}"') raise ScriptError(f'Cannot find a {cls.__name__} instance that matches "{name}"')
@dataclass
class Zone(Keyword):
shape: str
hazard_level: int
region: int
DIC = {
0: {'shape': 'J10', 'hazard_level': 1, 'cn': 'NY', 'en': 'NY City', 'jp': 'NYシティ', 'tw': '紐約',
'area_pos': (262, 567), 'offset_pos': (15, 15), 'region': 3},
1: {'shape': 'J10', 'hazard_level': 1, 'cn': '利维浦', 'en': 'Liverpool', 'jp': 'リバープール', 'tw': '利物浦',
'area_pos': (1446, 887), 'offset_pos': (15, 15), 'region': 2},
2: {'shape': 'J10', 'hazard_level': 1, 'cn': '直布罗特', 'en': 'Gibraltar', 'jp': 'ジブラルタル', 'tw': '直布羅陀',
'area_pos': (1418, 495), 'offset_pos': (15, 15), 'region': 4},
3: {'shape': 'J10', 'hazard_level': 1, 'cn': '圣彼得伯格', 'en': 'St. Petersburg', 'jp': 'ペテルブルク', 'tw': '聖彼得堡',
'area_pos': (1998, 1095), 'offset_pos': (15, 15), 'region': 2},
}
Zone(id=0, cn='NY', en='NY City', jp='NYシティ', tw='紐約', shape='J10', hazard_level=1, region=3)
Zone(id=1, cn='利维浦', en='Liverpool', jp='リバープール', tw='利物浦', shape='J10', hazard_level=1, region=2)
Zone(id=2, cn='直布罗特', en='Gibraltar', jp='ジブラルタル', tw='直布羅陀', shape='J10', hazard_level=1, region=3)
print(Zone.find('直布羅陀'))

View File

@ -23,7 +23,7 @@ class OcrResultButton:
try: try:
self.matched_keyword = keyword_class.find( self.matched_keyword = keyword_class.find(
boxed_result.ocr_text, in_current_server=True, ignore_punctuation=True) boxed_result.ocr_text, in_current_server=True, ignore_punctuation=True)
self.name = f'{keyword_class.__name__}_{self.matched_keyword.id}' self.name = str(self.matched_keyword)
except ScriptError: except ScriptError:
self.matched_keyword = None self.matched_keyword = None
self.name = boxed_result.ocr_text self.name = boxed_result.ocr_text
@ -148,5 +148,5 @@ class Ocr:
] ]
results = [result for result in results if result.matched_keyword is not None] results = [result for result in results if result.matched_keyword is not None]
logger.attr(name=f'{self.name} matched', logger.attr(name=f'{self.name} matched',
text='[' + ', '.join([result.name for result in results]) + ']') text=results)
return results return results

View File

@ -0,0 +1,3 @@
import tasks.dungeon.keywords.nav as KEYWORDS_DUNGEON_NAV
import tasks.dungeon.keywords.tab as KEYWORDS_DUNGEON_TAB
from tasks.dungeon.keywords.classes import DungeonNav, DungeonTab

View File

@ -0,0 +1,13 @@
from dataclasses import dataclass
from module.ocr.keyword import Keyword
@dataclass
class DungeonNav(Keyword):
pass
@dataclass
class DungeonTab(Keyword):
pass

View File

@ -0,0 +1,47 @@
from .classes import DungeonNav
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.keyword_extract ```
Simulated_Universe = DungeonNav(
id=1,
cn='模拟宇宙',
cht='模擬宇宙',
en='Simulated Universe',
jp='模擬宇宙',
)
Calyx_Golden = DungeonNav(
id=2,
cn='拟造花萼(金)',
cht='擬造花萼(金)',
en='Calyx (Golden)',
jp='疑似花萼(金)',
)
Calyx_Crimson = DungeonNav(
id=3,
cn='拟造花萼(赤)',
cht='擬造花萼(赤)',
en='Calyx (Crimson)',
jp='疑似花萼(赤)',
)
Stagnant_Shadow = DungeonNav(
id=4,
cn='凝滞虚影',
cht='凝滯虛影',
en='Stagnant Shadow',
jp='凝結虚影',
)
Cavern_of_Corrosion = DungeonNav(
id=5,
cn='侵蚀隧洞',
cht='侵蝕隧洞',
en='Cavern of Corrosion',
jp='侵蝕トンネル',
)
Forgotten_Hall = DungeonNav(
id=6,
cn='忘却之庭',
cht='忘卻之庭',
en='Forgotten Hall',
jp='忘却の庭',
)

View File

@ -0,0 +1,26 @@
from .classes import DungeonTab
# This file was auto-generated, do not modify it manually. To generate:
# ``` python -m dev_tools.keyword_extract ```
Operation_Briefing = DungeonTab(
id=1,
cn='行动摘要',
cht='行動摘要',
en='Operation Briefing',
jp='行動要旨',
)
Survival_Index = DungeonTab(
id=2,
cn='生存索引',
cht='生存索引',
en='Survival Index',
jp='生存手引書',
)
Daily_Training = DungeonTab(
id=3,
cn='每日实训',
cht='每日實訓',
en='Daily Training',
jp='デイリー訓練',
)