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])
|
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'):
|
||||||
|
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()
|
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=']')
|
||||||
|
@ -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('直布羅陀'))
|
|
||||||
|
@ -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
|
||||||
|
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