Spaces:
Sleeping
Sleeping
| import os | |
| import sys | |
| import json | |
| from collections import Counter | |
| from typing import Dict, List | |
| from sudachipy import dictionary, tokenizer | |
| sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))) | |
| from utils.logger import setup_logger | |
| from utils.json import get_file_path_from_config, field_getter, json_dumps | |
| from schemas import tf_token | |
| log = setup_logger(__name__) | |
| def load_configs(): | |
| search = field_getter("config/search_model.json") | |
| files = field_getter("config/files.json") | |
| target_pos_l1: List[str] = search("target_pos_l1") | |
| target_fields: List[str] = search("target_fields") | |
| ban_list: List[str] = search("synonyms.banlist") | |
| stopwords_path = files("sudachi.stopwords") | |
| with open(stopwords_path, encoding="utf-8") as f: | |
| stopwords = set(json.load(f)) | |
| sudachi_config_path = files("sudachi.sudachi_config") | |
| return target_pos_l1, target_fields, set(ban_list), stopwords, sudachi_config_path | |
| def build_tokenizer(sudachi_config_path: str): | |
| tok = dictionary.Dictionary(config_path=sudachi_config_path).create() | |
| mode = tokenizer.Tokenizer.SplitMode.A | |
| return tok, mode | |
| def tokenize( | |
| text: str, tok, mode, target_pos_l1: List[str], ban_list: set, stopwords: set | |
| ) -> List[str]: | |
| if not text: | |
| return [] | |
| out: List[str] = [] | |
| for m in tok.tokenize(text, mode): | |
| base = m.normalized_form().lower().strip() | |
| if not base: | |
| continue | |
| pos = m.part_of_speech() | |
| if pos[0] not in target_pos_l1: | |
| continue | |
| if base in stopwords or base in ban_list: | |
| continue | |
| out.append(base) | |
| return out | |
| def main(): | |
| log.info("フィールド別TF/トークン(tf_token.json)を生成します") | |
| try: | |
| target_pos_l1, target_fields, ban_list, stopwords, sudachi_config_path = ( | |
| load_configs() | |
| ) | |
| except Exception as e: | |
| log.error(f"設定の読み込みに失敗しました: {e}") | |
| sys.exit(1) | |
| circles_path = get_file_path_from_config( | |
| "circles.circles_json", "data/generated/circles.json" | |
| ) | |
| output_path = get_file_path_from_config( | |
| "bm25.tf_token", "data/generated/tf_token.json" | |
| ) | |
| try: | |
| with open(circles_path, encoding="utf-8") as f: | |
| circles = json.load(f) | |
| except Exception as e: | |
| log.error(f"circles.jsonの読み込みに失敗しました: {e}") | |
| sys.exit(1) | |
| tok, mode = build_tokenizer(sudachi_config_path) | |
| results: List[tf_token.Circle] = [] | |
| for c in circles: | |
| circle_id = c.get("circleId") | |
| # 各フィールドのトークン化とTF | |
| field_objs: Dict[str, tf_token.TfOfField] = {} | |
| doc_tf_counter: Counter = Counter() | |
| doc_token_set: set = set() | |
| for field in target_fields: | |
| text = c.get(field) or "" | |
| toks = tokenize(str(text), tok, mode, target_pos_l1, ban_list, stopwords) | |
| tf = Counter(toks) | |
| field_objs[field] = tf_token.TfOfField(len=len(toks), tf=dict(tf)) | |
| doc_tf_counter.update(tf) | |
| doc_token_set.update(tf.keys()) | |
| # スキーマ Fields へ詰める(未定義フィールドは長さ0/空dictで埋める) | |
| def get_field(name: str) -> tf_token.TfOfField: | |
| return field_objs.get(name, tf_token.TfOfField(len=0, tf={})) | |
| fields_obj = tf_token.Fields( | |
| name=get_field("name"), | |
| circleName=get_field("circleName"), | |
| circleNameKana=get_field("circleNameKana"), | |
| description=get_field("description"), | |
| prSummary=get_field("prSummary"), | |
| prDetail=get_field("prDetail"), | |
| ) | |
| circle_entry = tf_token.Circle( | |
| circleId=circle_id, | |
| fields=fields_obj, | |
| tf=dict(doc_tf_counter), | |
| # tokens はユニーク語彙の存在フラグ(1)とする | |
| tokens={t: 1 for t in sorted(doc_token_set)}, | |
| ) | |
| results.append(circle_entry) | |
| os.makedirs(os.path.dirname(output_path), exist_ok=True) | |
| json_dumps([r.model_dump() for r in results], output_path) | |
| log.info(f"tf_token.json を出力しました: {output_path}") | |
| if __name__ == "__main__": | |
| main() | |