File size: 4,265 Bytes
f446d43
 
 
 
 
 
 
 
 
 
 
 
0db07a9
f446d43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a9c6152
 
 
f446d43
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
a9c6152
 
 
f446d43
 
 
 
568bbca
 
a9c6152
 
 
 
f446d43
 
568bbca
 
f446d43
568bbca
f446d43
 
 
 
568bbca
f446d43
568bbca
 
f446d43
 
0db07a9
f446d43
 
 
 
568bbca
f446d43
 
0db07a9
f446d43
 
 
 
0db07a9
 
f446d43
0db07a9
fa87959
a9c6152
 
f446d43
fa87959
 
f446d43
 
568bbca
 
f446d43
 
 
 
 
568bbca
f446d43
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
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()