mirror of
https://github.com/LmeSzinc/StarRailCopilot.git
synced 2024-11-16 06:25:24 +00:00
Add: Keyword extract
This commit is contained in:
parent
36d898b038
commit
50e0ddb954
@ -206,11 +206,10 @@ def generate_code():
|
||||
path = os.path.join(AzurLaneConfig.ASSETS_MODULE, module.split('/', maxsplit=1)[0])
|
||||
output = os.path.join(path, 'assets')
|
||||
gen = CodeGenerator()
|
||||
gen.add('from module.base.button import Button, ButtonWrapper')
|
||||
gen.Empty()
|
||||
gen.Comment('This file was auto-generated, do not modify it manually. To generate:')
|
||||
gen.Comment('``` python -m dev_tools.button_extract ```')
|
||||
gen.Empty()
|
||||
gen.Import("""
|
||||
from module.base.button import Button, ButtonWrapper
|
||||
""")
|
||||
gen.CommentAutoGenerage('dev_tools.button_extract')
|
||||
for assets, assets_data in module_data.items():
|
||||
has_share = SHARE_SERVER in assets_data
|
||||
with gen.Object(key=assets, object_class='ButtonWrapper'):
|
||||
|
108
dev_tools/keyword_extract.py
Normal file
108
dev_tools/keyword_extract.py
Normal 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()
|
@ -100,6 +100,19 @@ class CodeGenerator:
|
||||
line = line.strip()
|
||||
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):
|
||||
if key is not None:
|
||||
return TabWrapper(self, prefix=str(key) + ' = [', suffix=']')
|
||||
|
@ -6,7 +6,7 @@ from typing import ClassVar
|
||||
from module.exception import ScriptError
|
||||
import module.config.server as server
|
||||
|
||||
REGEX_PUNCTUATION = re.compile(r'[,.\'",。\-—/\\\n\t]')
|
||||
REGEX_PUNCTUATION = re.compile(r'[ ,.\'",。\-—/\\\n\t()()]')
|
||||
|
||||
|
||||
def parse_name(n):
|
||||
@ -14,13 +14,19 @@ def parse_name(n):
|
||||
return n
|
||||
|
||||
|
||||
def text_to_variable(text):
|
||||
text = re.sub('[ \-]', '_', text)
|
||||
text = re.sub('[()]', '', text)
|
||||
return text
|
||||
|
||||
|
||||
@dataclass
|
||||
class Keyword:
|
||||
id: int
|
||||
cn: str
|
||||
en: str
|
||||
jp: str
|
||||
tw: str
|
||||
cht: str
|
||||
|
||||
"""
|
||||
Instance attributes and methods
|
||||
@ -39,8 +45,26 @@ class Keyword:
|
||||
return parse_name(self.jp)
|
||||
|
||||
@cached_property
|
||||
def tw_parsed(self) -> str:
|
||||
return parse_name(self.tw)
|
||||
def cht_parsed(self) -> str:
|
||||
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):
|
||||
if in_current_server:
|
||||
@ -62,23 +86,23 @@ class Keyword:
|
||||
return [self.jp]
|
||||
case 'tw':
|
||||
if ignore_punctuation:
|
||||
return [self.tw_parsed]
|
||||
return [self.cht_parsed]
|
||||
else:
|
||||
return [self.tw]
|
||||
return [self.cht]
|
||||
else:
|
||||
if ignore_punctuation:
|
||||
return [
|
||||
self.cn_parsed,
|
||||
self.en_parsed,
|
||||
self.jp_parsed,
|
||||
self.tw_parsed,
|
||||
self.cht_parsed,
|
||||
]
|
||||
else:
|
||||
return [
|
||||
self.cn,
|
||||
self.en,
|
||||
self.jp,
|
||||
self.tw,
|
||||
self.cht,
|
||||
]
|
||||
|
||||
"""
|
||||
@ -114,7 +138,10 @@ class Keyword:
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
if ignore_punctuation:
|
||||
name = parse_name(name)
|
||||
else:
|
||||
name = str(name)
|
||||
instance: Keyword
|
||||
for instance in cls.instances.values():
|
||||
for keyword in instance._keywords_to_find(
|
||||
@ -123,27 +150,3 @@ class Keyword:
|
||||
return instance
|
||||
|
||||
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('直布羅陀'))
|
||||
|
@ -23,7 +23,7 @@ class OcrResultButton:
|
||||
try:
|
||||
self.matched_keyword = keyword_class.find(
|
||||
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:
|
||||
self.matched_keyword = None
|
||||
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]
|
||||
logger.attr(name=f'{self.name} matched',
|
||||
text='[' + ', '.join([result.name for result in results]) + ']')
|
||||
text=results)
|
||||
return results
|
||||
|
3
tasks/dungeon/keywords/__init__.py
Normal file
3
tasks/dungeon/keywords/__init__.py
Normal 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
|
13
tasks/dungeon/keywords/classes.py
Normal file
13
tasks/dungeon/keywords/classes.py
Normal 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
|
47
tasks/dungeon/keywords/nav.py
Normal file
47
tasks/dungeon/keywords/nav.py
Normal 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='忘却の庭',
|
||||
)
|
26
tasks/dungeon/keywords/tab.py
Normal file
26
tasks/dungeon/keywords/tab.py
Normal 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='デイリー訓練',
|
||||
)
|
Loading…
Reference in New Issue
Block a user