libs.domain.rule

libs/domain/rule.py

  1"""
  2libs/domain/rule.py
  3"""
  4
  5import logging
  6import sys
  7from configparser import ConfigParser
  8from dataclasses import dataclass, field
  9from typing import TYPE_CHECKING, Any, Literal, Mapping
 10
 11from table2ascii import Alignment, PresetStyle, table2ascii
 12
 13from libs.domain.command import CommandParser
 14from libs.utils import dbutil
 15from libs.utils.timekit import ExtendedDatetime as ExtDt
 16
 17if TYPE_CHECKING:
 18    from pathlib import Path
 19
 20
 21@dataclass
 22class RuleData:
 23    """ルールデータ"""
 24
 25    # ルール
 26    rule_version: str = ""
 27    """ルール識別子"""
 28    mode: Literal[3, 4] = 4
 29    """ 集計モード切替(四人打ち/三人打ち)"""
 30    origin_point: int = 250
 31    """配給原点"""
 32    return_point: int = 300
 33    """返し点"""
 34    rank_point: list[int] = field(default_factory=list)
 35    """順位点"""
 36    ignore_flying: bool = False
 37    """トビカウント
 38    - *True*: なし
 39    - *False*: あり
 40    """
 41    draw_split: bool = False
 42    """同点時の順位点
 43    - *True*: 山分けにする
 44    - *False*: 席順で決める
 45    """
 46    undefined_word: int = 1
 47    """未定義ワードタイプ"""
 48    keywords: list[str] = field(default_factory=list)
 49    """成績記録キーワード"""
 50    remarks: list[str] = field(default_factory=list)
 51    """メモ記録用ワード"""
 52    dropitems: list[str] = field(default_factory=list)
 53    """非表示にする項目"""
 54
 55    # ステータス
 56    first_time: ExtDt = field(default=ExtDt("1900-01-01 00:00:00"))
 57    """記録開始日時"""
 58    last_time: ExtDt = field(default=ExtDt("1900-01-01 00:00:00"))
 59    """最終記録日時"""
 60    count: int = 0
 61    """記録回数"""
 62
 63    def update(self, rule_data: Mapping[str, Any]) -> None:
 64        """
 65        ルール更新
 66
 67        Args:
 68            rule_data (Mapping): 更新データ
 69
 70        """
 71        if "rule_version" in rule_data:
 72            self.rule_version = str(rule_data["rule_version"])
 73        if "origin_point" in rule_data:
 74            self.origin_point = int(rule_data["origin_point"])
 75        if "return_point" in rule_data:
 76            self.return_point = int(rule_data["return_point"])
 77
 78        if rank_point := rule_data.get("rank_point"):
 79            if isinstance(rank_point, str):
 80                rank_point = rank_point.split(",")
 81                self.rank_point = list(map(int, map(float, rank_point[: self.mode])))
 82            elif isinstance(rank_point, list):
 83                self.rank_point = list(map(int, map(float, rank_point[: self.mode])))
 84
 85        if undefined_word := rule_data.get("undefined_word"):
 86            self.undefined_word = int(undefined_word)
 87        else:
 88            self.undefined_word = 1
 89
 90        for item in ["ignore_flying", "draw_split"]:
 91            if flag := rule_data.get(item):
 92                if isinstance(flag, bool):
 93                    setattr(self, item, flag)
 94                else:
 95                    setattr(self, item, str(flag).lower() in {"1", "true", "yes", "on"})
 96
 97        for item in ["keywords", "remarks", "dropitems"]:
 98            if item_list := rule_data.get(item):
 99                if isinstance(item_list, str):
100                    setattr(self, item, [x.strip() for x in item_list.split(",")])
101                elif isinstance(item_list, list):
102                    setattr(self, item, item_list)
103
104
105class RuleSet:
106    """ルールセット"""
107
108    def __init__(self) -> None:
109        self.config: ConfigParser = ConfigParser()
110        """ルール設定ファイル"""
111        self.data: dict[str, RuleData] = {}
112        """ルール情報格納辞書"""
113        self.keyword_mapping: dict[str, str] = {}
114        """登録キーワードとルール識別子のマッピング"""
115        self.remarks_words: list[str] = []
116        """メモ記録用ワードリスト"""
117
118    def data_set(self, section_name: str, rule_data: Mapping[str, Any]) -> None:
119        """
120        ルール登録
121
122        Args:
123            section_name (str): セクション名
124            rule_data (Mapping): 更新データ情報
125
126        """
127        rule = RuleData()
128
129        # 初期値セット
130        match int(rule_data.get("mode", 4)):
131            case 3:
132                rule.mode = 3
133                rule.origin_point = 350
134                rule.return_point = 400
135                rule.rank_point = [30, 0, -30]
136            case 4:
137                rule.mode = 4
138                rule.origin_point = 250
139                rule.return_point = 300
140                rule.rank_point = [30, 10, -10, -30]
141            case _:
142                logging.warning("Do not register: %s (invalid mode: %s)", section_name, rule_data.get("mode"))
143                return
144
145        # 設定値取り込み
146        rule.update(rule_data)
147        if not rule.rule_version:
148            rule.rule_version = section_name
149        self.data.update({rule.rule_version: rule})
150
151    def read_config(self, config: "Path") -> None:
152        """
153        設定ファイル読み込み
154
155        Args:
156            config (Path): ルール設定ファイル
157
158        """
159        self.config.read(config, encoding="utf-8")
160        for section_name in map(str, self.config.sections()):
161            if section_name.startswith("regulations_") or section_name.endswith("_regulations"):
162                continue
163            if section_name.startswith("regulations_team_") or section_name.endswith("_regulations_team"):
164                continue
165
166            self.data_set(section_name, dict(self.config[section_name]))
167
168    def status_update(self, params: dict[str, Any]) -> None:
169        """
170        ステータス更新
171
172        Args:
173            params (dict[str, Any]): プレースホルダ
174
175        """
176        # ステータスリセット
177        for rule_version in self.rule_list:
178            self.data[rule_version].count = 0
179            self.data[rule_version].first_time.set("1900-01-01 00:00:00")
180            self.data[rule_version].last_time.set("1900-01-01 00:00:00")
181
182        status = dbutil.execute(
183            """
184            select
185                rule_version,
186                min(ts) as first_time,
187                max(ts) as last_time,
188                count() as count
189            from
190                result
191            --[separate] where source = :source
192            group by
193                rule_version
194            ;
195            """,
196            params,
197        )
198
199        # ステータス更新
200        for status_data in status:
201            if (rule_version := str(status_data.get("rule_version", ""))) and self.data.get(rule_version):
202                if "count" in status_data:
203                    self.data[rule_version].count = int(status_data["count"])
204                if "first_time" in status_data:
205                    self.data[rule_version].first_time = ExtDt(float(status_data["first_time"]))
206                if "last_time" in status_data:
207                    self.data[rule_version].last_time = ExtDt(float(status_data["last_time"]))
208
209    def remarks_words_update(self, suffix: list[str]) -> None:
210        """
211        メモ記録ワードリストを更新する
212
213        Args:
214            suffix (list[str]): メモに追加するサフィックス
215
216        """
217        ret: list[str] = []
218
219        for rule in self.rule_list:
220            if rule_data := self.data.get(rule):
221                if rule_data.remarks:
222                    ret.extend(rule_data.remarks)
223                else:
224                    ret.extend(
225                        [f"{pre}{suf}" for pre in self.keywords(rule) for suf in suffix],
226                    )
227
228        self.remarks_words = list(set(ret))
229
230    def to_dict(self, rule_version: str) -> dict[str, Any]:
231        """
232        指定ルール識別子の情報を辞書で返す
233
234        Args:
235            rule_version (str): ルール識別子
236
237        Returns:
238            dict[str, Any]: ルール情報
239
240        """
241        if rule := self.data.get(rule_version):
242            return {
243                "rule_version": rule.rule_version,
244                "mode": rule.mode,
245                "origin_point": rule.origin_point,
246                "return_point": rule.return_point,
247                "rank_point": rule.rank_point,
248                "ignore_flying": rule.ignore_flying,
249                "draw_split": rule.draw_split,
250                "undefined_word": rule.undefined_word,
251            }
252
253        return {}
254
255    def keywords(self, rule_version: str) -> set[str]:
256        """
257        成績記録キーワードの取得
258
259        Args:
260            rule_version (str): ルール識別子
261
262        Returns:
263            set[str]: 成績記録キーワード
264        """
265        if items := self.data.get(rule_version):
266            return set(items.keywords)
267        else:
268            return set([])
269
270    def dropitems(self, rule_version: str) -> set[str]:
271        """
272        非表示項目の取得
273
274        Args:
275            rule_version (str): ルール識別子
276
277        Returns:
278            set[str]: 非表示項目
279        """
280        if items := self.data.get(rule_version):
281            return set(items.dropitems)
282        else:
283            return set([])
284
285    def get_version(self, mode: int, mapping: bool = True) -> list[str]:
286        """
287        指定した条件のルール識別子をリストで返す
288
289        Args:
290            mode (int): 集計モード
291            mapping (bool, optional): Defaults to True.
292                - *True*: キーワードマッピングに登録されているルールのみ
293                - *False*: ルールとして定義されているものすべて
294
295        Returns:
296            list[str]: ルール識別子
297
298        """
299        ret: list[str] = []
300
301        for keyword, rule in self.data.items():
302            if rule.mode == mode:
303                if mapping:
304                    if keyword in self.keyword_mapping.values():
305                        ret.append(rule.rule_version)
306                else:
307                    ret.append(rule.rule_version)
308
309        return ret
310
311    def get_mode(self, rule_version: str) -> int:
312        """
313        指定ルール識別子の集計モードを返す
314
315        Args:
316            rule_version (str): ルール識別子
317
318        Returns:
319            int: 集計モード
320
321        """
322        return int(self.to_dict(rule_version).get("mode", 0))
323
324    def get_ignore_flying(self, rule_version: str) -> bool:
325        """
326        指定ルール識別子のトビカウントフラグを返す
327
328        Args:
329            rule_version (str): ルール識別子
330
331        Returns:
332            bool: トビカウントフラグ
333
334        """
335        return bool(self.to_dict(rule_version).get("ignore_flying", False))
336
337    def get_undefined_word(self, rule_version: str) -> int:
338        """
339        指定ルール識別子の未定義ワードタイプを返す
340
341        Args:
342            rule_version (str): ルール識別子
343
344        Returns:
345            int: 未定義ワードタイプ
346
347        """
348        return int(self.to_dict(rule_version).get("undefined_word", 1))
349
350    def print(self, rule_version: str) -> str:
351        """
352        指定ルール識別子の内容を出力する
353
354        Args:
355            rule_version (str): ルール識別子
356
357        Returns:
358            str: 内容
359
360        """
361        ret: str = ""
362        body_data: list[list[str]] = []
363
364        if rule := self.data.get(rule_version):
365            body_data.append(["ルール識別子", rule.rule_version])
366
367            # 集計モード
368            match rule.mode:
369                case 3:
370                    body_data.append(["集計モード", "三人打ち"])
371                case 4:
372                    body_data.append(["集計モード", "四人打ち"])
373                case _:
374                    body_data.append(["集計モード", "未定義"])
375
376            body_data.extend(
377                [
378                    ["素点", f"{rule.origin_point * 100}点持ち / {rule.return_point * 100}点返し"],
379                    ["順位点", " / ".join([f"{pt}pt".replace("-", "▲") for pt in rule.rank_point])],
380                    ["同点時", "順位点山分け" if rule.draw_split else "席順"],
381                ]
382            )
383
384            # マッピング情報
385            if keyword := [word for word, mapping_rule in self.keyword_mapping.items() if mapping_rule == rule_version]:
386                body_data.append(["成績登録ワード", "、".join(keyword)])
387            else:
388                body_data.append(["成績登録ワード", "---"])
389
390            # 記録時間
391            body_data.append(["記録数", f"{rule.count} ゲーム"])
392            if rule.count:
393                body_data.extend(
394                    [
395                        ["記録開始日時", rule.first_time.format(ExtDt.FMT.YMDHMS)],
396                        ["最終記録日時", rule.last_time.format(ExtDt.FMT.YMDHMS)],
397                    ]
398                )
399
400            ret = table2ascii(
401                body=body_data,
402                alignments=[Alignment.LEFT, Alignment.LEFT],
403                style=PresetStyle.plain,
404            )
405
406        return ret
407
408    def info(self) -> None:
409        """定義ルールをログに出力する"""
410        for rule in self.data.values():
411            logging.info(
412                "%s: mode=%s, origin_point=%s, return_point=%s, rank_point=%s, draw_split=%s, ignore_flying=%s, undefined_word=%s",
413                rule.rule_version,
414                rule.mode,
415                rule.origin_point,
416                rule.return_point,
417                rule.rank_point,
418                rule.draw_split,
419                rule.ignore_flying,
420                rule.undefined_word,
421            )
422        if self.keyword_mapping:
423            logging.info("keyword_mapping: %s", self.keyword_mapping)
424        else:
425            logging.warning("keyword_mapping: empty")
426        if self.remarks_words:
427            logging.info("remarks_words: %s", self.remarks_words)
428        else:
429            logging.warning("remarks_words: empty")
430
431    def check(self, chk_commands: set[str], chk_members: set[str], default_rule: str) -> None:
432        """
433        キーワード重複チェック
434
435        Args:
436            chk_commands (set[str]): チェック対象コマンド名
437            chk_members (set[str]): チェック対象メンバー名/チーム名
438            default_rule (str): デフォルトルールバージョン
439
440        Raises:
441            RuntimeError: 重複あり
442
443        """
444        chk_word: str | RuleData
445
446        try:
447            # ルール識別子チェック
448            for chk_word in self.data.values():
449                if CommandParser().is_valid_command(chk_word.rule_version):
450                    raise RuntimeError(f"ルール識別子にオプションに使用される単語が使用されています。({chk_word.rule_version})")
451                if chk_word.rule_version in ExtDt.valid_keywords():
452                    raise RuntimeError(f"ルール識別子に検索範囲指定に使用される単語が使用されています。({chk_word.rule_version})")
453                if chk_word.rule_version in chk_commands:
454                    raise RuntimeError(f"ルール識別子と定義済みコマンドに重複があります。({chk_word.rule_version})")
455                if chk_word.rule_version in chk_members:
456                    raise RuntimeError(f"ルール識別子と登録メンバー(チーム)に重複があります。({chk_word.rule_version})")
457            # 成績登録ワードチェック
458            for chk_word in self.keyword_mapping.keys():
459                if CommandParser().is_valid_command(chk_word):
460                    raise RuntimeError(f"成績登録ワードにオプションに使用される単語が使用されています。({chk_word})")
461                if chk_word in ExtDt.valid_keywords():
462                    raise RuntimeError(f"成績登録ワードに検索範囲指定に使用される単語が使用されています。({chk_word})")
463                if chk_word in chk_commands:
464                    raise RuntimeError(f"成績登録ワードと定義済みコマンドに重複があります。({chk_word})")
465                if chk_word in chk_members:
466                    raise RuntimeError(f"成績登録ワードと登録メンバー(チーム)に重複があります。({chk_word})")
467            # デフォルトルールバージョンチェック
468            if default_rule not in self.rule_list:
469                raise RuntimeError(f"デフォルトルールバージョンに指定されているルールセットが見つかりません。({default_rule})")
470        except RuntimeError as err:
471            logging.critical("%s", err)
472            sys.exit(1)
473
474    def register_to_database(self) -> None:
475        """ルールセット情報をDBに登録する"""
476        dbutil.execute("delete from rule;")
477        for rule in self.rule_list:
478            params = self.to_dict(rule)
479            params.update(rank_point=" ".join(map(str, params["rank_point"])))
480            dbutil.execute(
481                """
482                insert into
483                rule (
484                    rule_version, mode, origin_point, return_point, rank_point, ignore_flying, draw_split, undefined_word
485                ) values (
486                    :rule_version, :mode, :origin_point, :return_point, :rank_point, :ignore_flying, :draw_split, :undefined_word
487                );
488                """,
489                params,
490            )
491
492    @property
493    def rule_list(self) -> list[str]:
494        """
495        定義済みルール識別子の列挙
496
497        Returns:
498            list[str]: ルール識別子
499
500        """
501        return [x.rule_version for x in self.data.values()]
@dataclass
class RuleData:
 22@dataclass
 23class RuleData:
 24    """ルールデータ"""
 25
 26    # ルール
 27    rule_version: str = ""
 28    """ルール識別子"""
 29    mode: Literal[3, 4] = 4
 30    """ 集計モード切替(四人打ち/三人打ち)"""
 31    origin_point: int = 250
 32    """配給原点"""
 33    return_point: int = 300
 34    """返し点"""
 35    rank_point: list[int] = field(default_factory=list)
 36    """順位点"""
 37    ignore_flying: bool = False
 38    """トビカウント
 39    - *True*: なし
 40    - *False*: あり
 41    """
 42    draw_split: bool = False
 43    """同点時の順位点
 44    - *True*: 山分けにする
 45    - *False*: 席順で決める
 46    """
 47    undefined_word: int = 1
 48    """未定義ワードタイプ"""
 49    keywords: list[str] = field(default_factory=list)
 50    """成績記録キーワード"""
 51    remarks: list[str] = field(default_factory=list)
 52    """メモ記録用ワード"""
 53    dropitems: list[str] = field(default_factory=list)
 54    """非表示にする項目"""
 55
 56    # ステータス
 57    first_time: ExtDt = field(default=ExtDt("1900-01-01 00:00:00"))
 58    """記録開始日時"""
 59    last_time: ExtDt = field(default=ExtDt("1900-01-01 00:00:00"))
 60    """最終記録日時"""
 61    count: int = 0
 62    """記録回数"""
 63
 64    def update(self, rule_data: Mapping[str, Any]) -> None:
 65        """
 66        ルール更新
 67
 68        Args:
 69            rule_data (Mapping): 更新データ
 70
 71        """
 72        if "rule_version" in rule_data:
 73            self.rule_version = str(rule_data["rule_version"])
 74        if "origin_point" in rule_data:
 75            self.origin_point = int(rule_data["origin_point"])
 76        if "return_point" in rule_data:
 77            self.return_point = int(rule_data["return_point"])
 78
 79        if rank_point := rule_data.get("rank_point"):
 80            if isinstance(rank_point, str):
 81                rank_point = rank_point.split(",")
 82                self.rank_point = list(map(int, map(float, rank_point[: self.mode])))
 83            elif isinstance(rank_point, list):
 84                self.rank_point = list(map(int, map(float, rank_point[: self.mode])))
 85
 86        if undefined_word := rule_data.get("undefined_word"):
 87            self.undefined_word = int(undefined_word)
 88        else:
 89            self.undefined_word = 1
 90
 91        for item in ["ignore_flying", "draw_split"]:
 92            if flag := rule_data.get(item):
 93                if isinstance(flag, bool):
 94                    setattr(self, item, flag)
 95                else:
 96                    setattr(self, item, str(flag).lower() in {"1", "true", "yes", "on"})
 97
 98        for item in ["keywords", "remarks", "dropitems"]:
 99            if item_list := rule_data.get(item):
100                if isinstance(item_list, str):
101                    setattr(self, item, [x.strip() for x in item_list.split(",")])
102                elif isinstance(item_list, list):
103                    setattr(self, item, item_list)

ルールデータ

RuleData( rule_version: str = '', mode: Literal[3, 4] = 4, origin_point: int = 250, return_point: int = 300, rank_point: list[int] = <factory>, ignore_flying: bool = False, draw_split: bool = False, undefined_word: int = 1, keywords: list[str] = <factory>, remarks: list[str] = <factory>, dropitems: list[str] = <factory>, first_time: libs.utils.timekit.ExtendedDatetime = 1900-01-01 00:00:00.000000, last_time: libs.utils.timekit.ExtendedDatetime = 1900-01-01 00:00:00.000000, count: int = 0)
rule_version: str = ''

ルール識別子

mode: Literal[3, 4] = 4

集計モード切替(四人打ち/三人打ち)

origin_point: int = 250

配給原点

return_point: int = 300

返し点

rank_point: list[int]

順位点

ignore_flying: bool = False

トビカウント

  • True: なし
  • False: あり
draw_split: bool = False

同点時の順位点

  • True: 山分けにする
  • False: 席順で決める
undefined_word: int = 1

未定義ワードタイプ

keywords: list[str]

成績記録キーワード

remarks: list[str]

メモ記録用ワード

dropitems: list[str]

非表示にする項目

first_time: libs.utils.timekit.ExtendedDatetime = 1900-01-01 00:00:00.000000

記録開始日時

last_time: libs.utils.timekit.ExtendedDatetime = 1900-01-01 00:00:00.000000

最終記録日時

count: int = 0

記録回数

def update(self, rule_data: Mapping[str, Any]) -> None:
 64    def update(self, rule_data: Mapping[str, Any]) -> None:
 65        """
 66        ルール更新
 67
 68        Args:
 69            rule_data (Mapping): 更新データ
 70
 71        """
 72        if "rule_version" in rule_data:
 73            self.rule_version = str(rule_data["rule_version"])
 74        if "origin_point" in rule_data:
 75            self.origin_point = int(rule_data["origin_point"])
 76        if "return_point" in rule_data:
 77            self.return_point = int(rule_data["return_point"])
 78
 79        if rank_point := rule_data.get("rank_point"):
 80            if isinstance(rank_point, str):
 81                rank_point = rank_point.split(",")
 82                self.rank_point = list(map(int, map(float, rank_point[: self.mode])))
 83            elif isinstance(rank_point, list):
 84                self.rank_point = list(map(int, map(float, rank_point[: self.mode])))
 85
 86        if undefined_word := rule_data.get("undefined_word"):
 87            self.undefined_word = int(undefined_word)
 88        else:
 89            self.undefined_word = 1
 90
 91        for item in ["ignore_flying", "draw_split"]:
 92            if flag := rule_data.get(item):
 93                if isinstance(flag, bool):
 94                    setattr(self, item, flag)
 95                else:
 96                    setattr(self, item, str(flag).lower() in {"1", "true", "yes", "on"})
 97
 98        for item in ["keywords", "remarks", "dropitems"]:
 99            if item_list := rule_data.get(item):
100                if isinstance(item_list, str):
101                    setattr(self, item, [x.strip() for x in item_list.split(",")])
102                elif isinstance(item_list, list):
103                    setattr(self, item, item_list)

ルール更新

Arguments:
  • rule_data (Mapping): 更新データ
class RuleSet:
106class RuleSet:
107    """ルールセット"""
108
109    def __init__(self) -> None:
110        self.config: ConfigParser = ConfigParser()
111        """ルール設定ファイル"""
112        self.data: dict[str, RuleData] = {}
113        """ルール情報格納辞書"""
114        self.keyword_mapping: dict[str, str] = {}
115        """登録キーワードとルール識別子のマッピング"""
116        self.remarks_words: list[str] = []
117        """メモ記録用ワードリスト"""
118
119    def data_set(self, section_name: str, rule_data: Mapping[str, Any]) -> None:
120        """
121        ルール登録
122
123        Args:
124            section_name (str): セクション名
125            rule_data (Mapping): 更新データ情報
126
127        """
128        rule = RuleData()
129
130        # 初期値セット
131        match int(rule_data.get("mode", 4)):
132            case 3:
133                rule.mode = 3
134                rule.origin_point = 350
135                rule.return_point = 400
136                rule.rank_point = [30, 0, -30]
137            case 4:
138                rule.mode = 4
139                rule.origin_point = 250
140                rule.return_point = 300
141                rule.rank_point = [30, 10, -10, -30]
142            case _:
143                logging.warning("Do not register: %s (invalid mode: %s)", section_name, rule_data.get("mode"))
144                return
145
146        # 設定値取り込み
147        rule.update(rule_data)
148        if not rule.rule_version:
149            rule.rule_version = section_name
150        self.data.update({rule.rule_version: rule})
151
152    def read_config(self, config: "Path") -> None:
153        """
154        設定ファイル読み込み
155
156        Args:
157            config (Path): ルール設定ファイル
158
159        """
160        self.config.read(config, encoding="utf-8")
161        for section_name in map(str, self.config.sections()):
162            if section_name.startswith("regulations_") or section_name.endswith("_regulations"):
163                continue
164            if section_name.startswith("regulations_team_") or section_name.endswith("_regulations_team"):
165                continue
166
167            self.data_set(section_name, dict(self.config[section_name]))
168
169    def status_update(self, params: dict[str, Any]) -> None:
170        """
171        ステータス更新
172
173        Args:
174            params (dict[str, Any]): プレースホルダ
175
176        """
177        # ステータスリセット
178        for rule_version in self.rule_list:
179            self.data[rule_version].count = 0
180            self.data[rule_version].first_time.set("1900-01-01 00:00:00")
181            self.data[rule_version].last_time.set("1900-01-01 00:00:00")
182
183        status = dbutil.execute(
184            """
185            select
186                rule_version,
187                min(ts) as first_time,
188                max(ts) as last_time,
189                count() as count
190            from
191                result
192            --[separate] where source = :source
193            group by
194                rule_version
195            ;
196            """,
197            params,
198        )
199
200        # ステータス更新
201        for status_data in status:
202            if (rule_version := str(status_data.get("rule_version", ""))) and self.data.get(rule_version):
203                if "count" in status_data:
204                    self.data[rule_version].count = int(status_data["count"])
205                if "first_time" in status_data:
206                    self.data[rule_version].first_time = ExtDt(float(status_data["first_time"]))
207                if "last_time" in status_data:
208                    self.data[rule_version].last_time = ExtDt(float(status_data["last_time"]))
209
210    def remarks_words_update(self, suffix: list[str]) -> None:
211        """
212        メモ記録ワードリストを更新する
213
214        Args:
215            suffix (list[str]): メモに追加するサフィックス
216
217        """
218        ret: list[str] = []
219
220        for rule in self.rule_list:
221            if rule_data := self.data.get(rule):
222                if rule_data.remarks:
223                    ret.extend(rule_data.remarks)
224                else:
225                    ret.extend(
226                        [f"{pre}{suf}" for pre in self.keywords(rule) for suf in suffix],
227                    )
228
229        self.remarks_words = list(set(ret))
230
231    def to_dict(self, rule_version: str) -> dict[str, Any]:
232        """
233        指定ルール識別子の情報を辞書で返す
234
235        Args:
236            rule_version (str): ルール識別子
237
238        Returns:
239            dict[str, Any]: ルール情報
240
241        """
242        if rule := self.data.get(rule_version):
243            return {
244                "rule_version": rule.rule_version,
245                "mode": rule.mode,
246                "origin_point": rule.origin_point,
247                "return_point": rule.return_point,
248                "rank_point": rule.rank_point,
249                "ignore_flying": rule.ignore_flying,
250                "draw_split": rule.draw_split,
251                "undefined_word": rule.undefined_word,
252            }
253
254        return {}
255
256    def keywords(self, rule_version: str) -> set[str]:
257        """
258        成績記録キーワードの取得
259
260        Args:
261            rule_version (str): ルール識別子
262
263        Returns:
264            set[str]: 成績記録キーワード
265        """
266        if items := self.data.get(rule_version):
267            return set(items.keywords)
268        else:
269            return set([])
270
271    def dropitems(self, rule_version: str) -> set[str]:
272        """
273        非表示項目の取得
274
275        Args:
276            rule_version (str): ルール識別子
277
278        Returns:
279            set[str]: 非表示項目
280        """
281        if items := self.data.get(rule_version):
282            return set(items.dropitems)
283        else:
284            return set([])
285
286    def get_version(self, mode: int, mapping: bool = True) -> list[str]:
287        """
288        指定した条件のルール識別子をリストで返す
289
290        Args:
291            mode (int): 集計モード
292            mapping (bool, optional): Defaults to True.
293                - *True*: キーワードマッピングに登録されているルールのみ
294                - *False*: ルールとして定義されているものすべて
295
296        Returns:
297            list[str]: ルール識別子
298
299        """
300        ret: list[str] = []
301
302        for keyword, rule in self.data.items():
303            if rule.mode == mode:
304                if mapping:
305                    if keyword in self.keyword_mapping.values():
306                        ret.append(rule.rule_version)
307                else:
308                    ret.append(rule.rule_version)
309
310        return ret
311
312    def get_mode(self, rule_version: str) -> int:
313        """
314        指定ルール識別子の集計モードを返す
315
316        Args:
317            rule_version (str): ルール識別子
318
319        Returns:
320            int: 集計モード
321
322        """
323        return int(self.to_dict(rule_version).get("mode", 0))
324
325    def get_ignore_flying(self, rule_version: str) -> bool:
326        """
327        指定ルール識別子のトビカウントフラグを返す
328
329        Args:
330            rule_version (str): ルール識別子
331
332        Returns:
333            bool: トビカウントフラグ
334
335        """
336        return bool(self.to_dict(rule_version).get("ignore_flying", False))
337
338    def get_undefined_word(self, rule_version: str) -> int:
339        """
340        指定ルール識別子の未定義ワードタイプを返す
341
342        Args:
343            rule_version (str): ルール識別子
344
345        Returns:
346            int: 未定義ワードタイプ
347
348        """
349        return int(self.to_dict(rule_version).get("undefined_word", 1))
350
351    def print(self, rule_version: str) -> str:
352        """
353        指定ルール識別子の内容を出力する
354
355        Args:
356            rule_version (str): ルール識別子
357
358        Returns:
359            str: 内容
360
361        """
362        ret: str = ""
363        body_data: list[list[str]] = []
364
365        if rule := self.data.get(rule_version):
366            body_data.append(["ルール識別子", rule.rule_version])
367
368            # 集計モード
369            match rule.mode:
370                case 3:
371                    body_data.append(["集計モード", "三人打ち"])
372                case 4:
373                    body_data.append(["集計モード", "四人打ち"])
374                case _:
375                    body_data.append(["集計モード", "未定義"])
376
377            body_data.extend(
378                [
379                    ["素点", f"{rule.origin_point * 100}点持ち / {rule.return_point * 100}点返し"],
380                    ["順位点", " / ".join([f"{pt}pt".replace("-", "▲") for pt in rule.rank_point])],
381                    ["同点時", "順位点山分け" if rule.draw_split else "席順"],
382                ]
383            )
384
385            # マッピング情報
386            if keyword := [word for word, mapping_rule in self.keyword_mapping.items() if mapping_rule == rule_version]:
387                body_data.append(["成績登録ワード", "、".join(keyword)])
388            else:
389                body_data.append(["成績登録ワード", "---"])
390
391            # 記録時間
392            body_data.append(["記録数", f"{rule.count} ゲーム"])
393            if rule.count:
394                body_data.extend(
395                    [
396                        ["記録開始日時", rule.first_time.format(ExtDt.FMT.YMDHMS)],
397                        ["最終記録日時", rule.last_time.format(ExtDt.FMT.YMDHMS)],
398                    ]
399                )
400
401            ret = table2ascii(
402                body=body_data,
403                alignments=[Alignment.LEFT, Alignment.LEFT],
404                style=PresetStyle.plain,
405            )
406
407        return ret
408
409    def info(self) -> None:
410        """定義ルールをログに出力する"""
411        for rule in self.data.values():
412            logging.info(
413                "%s: mode=%s, origin_point=%s, return_point=%s, rank_point=%s, draw_split=%s, ignore_flying=%s, undefined_word=%s",
414                rule.rule_version,
415                rule.mode,
416                rule.origin_point,
417                rule.return_point,
418                rule.rank_point,
419                rule.draw_split,
420                rule.ignore_flying,
421                rule.undefined_word,
422            )
423        if self.keyword_mapping:
424            logging.info("keyword_mapping: %s", self.keyword_mapping)
425        else:
426            logging.warning("keyword_mapping: empty")
427        if self.remarks_words:
428            logging.info("remarks_words: %s", self.remarks_words)
429        else:
430            logging.warning("remarks_words: empty")
431
432    def check(self, chk_commands: set[str], chk_members: set[str], default_rule: str) -> None:
433        """
434        キーワード重複チェック
435
436        Args:
437            chk_commands (set[str]): チェック対象コマンド名
438            chk_members (set[str]): チェック対象メンバー名/チーム名
439            default_rule (str): デフォルトルールバージョン
440
441        Raises:
442            RuntimeError: 重複あり
443
444        """
445        chk_word: str | RuleData
446
447        try:
448            # ルール識別子チェック
449            for chk_word in self.data.values():
450                if CommandParser().is_valid_command(chk_word.rule_version):
451                    raise RuntimeError(f"ルール識別子にオプションに使用される単語が使用されています。({chk_word.rule_version})")
452                if chk_word.rule_version in ExtDt.valid_keywords():
453                    raise RuntimeError(f"ルール識別子に検索範囲指定に使用される単語が使用されています。({chk_word.rule_version})")
454                if chk_word.rule_version in chk_commands:
455                    raise RuntimeError(f"ルール識別子と定義済みコマンドに重複があります。({chk_word.rule_version})")
456                if chk_word.rule_version in chk_members:
457                    raise RuntimeError(f"ルール識別子と登録メンバー(チーム)に重複があります。({chk_word.rule_version})")
458            # 成績登録ワードチェック
459            for chk_word in self.keyword_mapping.keys():
460                if CommandParser().is_valid_command(chk_word):
461                    raise RuntimeError(f"成績登録ワードにオプションに使用される単語が使用されています。({chk_word})")
462                if chk_word in ExtDt.valid_keywords():
463                    raise RuntimeError(f"成績登録ワードに検索範囲指定に使用される単語が使用されています。({chk_word})")
464                if chk_word in chk_commands:
465                    raise RuntimeError(f"成績登録ワードと定義済みコマンドに重複があります。({chk_word})")
466                if chk_word in chk_members:
467                    raise RuntimeError(f"成績登録ワードと登録メンバー(チーム)に重複があります。({chk_word})")
468            # デフォルトルールバージョンチェック
469            if default_rule not in self.rule_list:
470                raise RuntimeError(f"デフォルトルールバージョンに指定されているルールセットが見つかりません。({default_rule})")
471        except RuntimeError as err:
472            logging.critical("%s", err)
473            sys.exit(1)
474
475    def register_to_database(self) -> None:
476        """ルールセット情報をDBに登録する"""
477        dbutil.execute("delete from rule;")
478        for rule in self.rule_list:
479            params = self.to_dict(rule)
480            params.update(rank_point=" ".join(map(str, params["rank_point"])))
481            dbutil.execute(
482                """
483                insert into
484                rule (
485                    rule_version, mode, origin_point, return_point, rank_point, ignore_flying, draw_split, undefined_word
486                ) values (
487                    :rule_version, :mode, :origin_point, :return_point, :rank_point, :ignore_flying, :draw_split, :undefined_word
488                );
489                """,
490                params,
491            )
492
493    @property
494    def rule_list(self) -> list[str]:
495        """
496        定義済みルール識別子の列挙
497
498        Returns:
499            list[str]: ルール識別子
500
501        """
502        return [x.rule_version for x in self.data.values()]

ルールセット

config: configparser.ConfigParser

ルール設定ファイル

data: dict[str, RuleData]

ルール情報格納辞書

keyword_mapping: dict[str, str]

登録キーワードとルール識別子のマッピング

remarks_words: list[str]

メモ記録用ワードリスト

def data_set(self, section_name: str, rule_data: Mapping[str, Any]) -> None:
119    def data_set(self, section_name: str, rule_data: Mapping[str, Any]) -> None:
120        """
121        ルール登録
122
123        Args:
124            section_name (str): セクション名
125            rule_data (Mapping): 更新データ情報
126
127        """
128        rule = RuleData()
129
130        # 初期値セット
131        match int(rule_data.get("mode", 4)):
132            case 3:
133                rule.mode = 3
134                rule.origin_point = 350
135                rule.return_point = 400
136                rule.rank_point = [30, 0, -30]
137            case 4:
138                rule.mode = 4
139                rule.origin_point = 250
140                rule.return_point = 300
141                rule.rank_point = [30, 10, -10, -30]
142            case _:
143                logging.warning("Do not register: %s (invalid mode: %s)", section_name, rule_data.get("mode"))
144                return
145
146        # 設定値取り込み
147        rule.update(rule_data)
148        if not rule.rule_version:
149            rule.rule_version = section_name
150        self.data.update({rule.rule_version: rule})

ルール登録

Arguments:
  • section_name (str): セクション名
  • rule_data (Mapping): 更新データ情報
def read_config(self, config: pathlib.Path) -> None:
152    def read_config(self, config: "Path") -> None:
153        """
154        設定ファイル読み込み
155
156        Args:
157            config (Path): ルール設定ファイル
158
159        """
160        self.config.read(config, encoding="utf-8")
161        for section_name in map(str, self.config.sections()):
162            if section_name.startswith("regulations_") or section_name.endswith("_regulations"):
163                continue
164            if section_name.startswith("regulations_team_") or section_name.endswith("_regulations_team"):
165                continue
166
167            self.data_set(section_name, dict(self.config[section_name]))

設定ファイル読み込み

Arguments:
  • config (Path): ルール設定ファイル
def status_update(self, params: dict[str, typing.Any]) -> None:
169    def status_update(self, params: dict[str, Any]) -> None:
170        """
171        ステータス更新
172
173        Args:
174            params (dict[str, Any]): プレースホルダ
175
176        """
177        # ステータスリセット
178        for rule_version in self.rule_list:
179            self.data[rule_version].count = 0
180            self.data[rule_version].first_time.set("1900-01-01 00:00:00")
181            self.data[rule_version].last_time.set("1900-01-01 00:00:00")
182
183        status = dbutil.execute(
184            """
185            select
186                rule_version,
187                min(ts) as first_time,
188                max(ts) as last_time,
189                count() as count
190            from
191                result
192            --[separate] where source = :source
193            group by
194                rule_version
195            ;
196            """,
197            params,
198        )
199
200        # ステータス更新
201        for status_data in status:
202            if (rule_version := str(status_data.get("rule_version", ""))) and self.data.get(rule_version):
203                if "count" in status_data:
204                    self.data[rule_version].count = int(status_data["count"])
205                if "first_time" in status_data:
206                    self.data[rule_version].first_time = ExtDt(float(status_data["first_time"]))
207                if "last_time" in status_data:
208                    self.data[rule_version].last_time = ExtDt(float(status_data["last_time"]))

ステータス更新

Arguments:
  • params (dict[str, Any]): プレースホルダ
def remarks_words_update(self, suffix: list[str]) -> None:
210    def remarks_words_update(self, suffix: list[str]) -> None:
211        """
212        メモ記録ワードリストを更新する
213
214        Args:
215            suffix (list[str]): メモに追加するサフィックス
216
217        """
218        ret: list[str] = []
219
220        for rule in self.rule_list:
221            if rule_data := self.data.get(rule):
222                if rule_data.remarks:
223                    ret.extend(rule_data.remarks)
224                else:
225                    ret.extend(
226                        [f"{pre}{suf}" for pre in self.keywords(rule) for suf in suffix],
227                    )
228
229        self.remarks_words = list(set(ret))

メモ記録ワードリストを更新する

Arguments:
  • suffix (list[str]): メモに追加するサフィックス
def to_dict(self, rule_version: str) -> dict[str, typing.Any]:
231    def to_dict(self, rule_version: str) -> dict[str, Any]:
232        """
233        指定ルール識別子の情報を辞書で返す
234
235        Args:
236            rule_version (str): ルール識別子
237
238        Returns:
239            dict[str, Any]: ルール情報
240
241        """
242        if rule := self.data.get(rule_version):
243            return {
244                "rule_version": rule.rule_version,
245                "mode": rule.mode,
246                "origin_point": rule.origin_point,
247                "return_point": rule.return_point,
248                "rank_point": rule.rank_point,
249                "ignore_flying": rule.ignore_flying,
250                "draw_split": rule.draw_split,
251                "undefined_word": rule.undefined_word,
252            }
253
254        return {}

指定ルール識別子の情報を辞書で返す

Arguments:
  • rule_version (str): ルール識別子
Returns:

dict[str, Any]: ルール情報

def keywords(self, rule_version: str) -> set[str]:
256    def keywords(self, rule_version: str) -> set[str]:
257        """
258        成績記録キーワードの取得
259
260        Args:
261            rule_version (str): ルール識別子
262
263        Returns:
264            set[str]: 成績記録キーワード
265        """
266        if items := self.data.get(rule_version):
267            return set(items.keywords)
268        else:
269            return set([])

成績記録キーワードの取得

Arguments:
  • rule_version (str): ルール識別子
Returns:

set[str]: 成績記録キーワード

def dropitems(self, rule_version: str) -> set[str]:
271    def dropitems(self, rule_version: str) -> set[str]:
272        """
273        非表示項目の取得
274
275        Args:
276            rule_version (str): ルール識別子
277
278        Returns:
279            set[str]: 非表示項目
280        """
281        if items := self.data.get(rule_version):
282            return set(items.dropitems)
283        else:
284            return set([])

非表示項目の取得

Arguments:
  • rule_version (str): ルール識別子
Returns:

set[str]: 非表示項目

def get_version(self, mode: int, mapping: bool = True) -> list[str]:
286    def get_version(self, mode: int, mapping: bool = True) -> list[str]:
287        """
288        指定した条件のルール識別子をリストで返す
289
290        Args:
291            mode (int): 集計モード
292            mapping (bool, optional): Defaults to True.
293                - *True*: キーワードマッピングに登録されているルールのみ
294                - *False*: ルールとして定義されているものすべて
295
296        Returns:
297            list[str]: ルール識別子
298
299        """
300        ret: list[str] = []
301
302        for keyword, rule in self.data.items():
303            if rule.mode == mode:
304                if mapping:
305                    if keyword in self.keyword_mapping.values():
306                        ret.append(rule.rule_version)
307                else:
308                    ret.append(rule.rule_version)
309
310        return ret

指定した条件のルール識別子をリストで返す

Arguments:
  • mode (int): 集計モード
  • mapping (bool, optional): Defaults to True.
    • True: キーワードマッピングに登録されているルールのみ
    • False: ルールとして定義されているものすべて
Returns:

list[str]: ルール識別子

def get_mode(self, rule_version: str) -> int:
312    def get_mode(self, rule_version: str) -> int:
313        """
314        指定ルール識別子の集計モードを返す
315
316        Args:
317            rule_version (str): ルール識別子
318
319        Returns:
320            int: 集計モード
321
322        """
323        return int(self.to_dict(rule_version).get("mode", 0))

指定ルール識別子の集計モードを返す

Arguments:
  • rule_version (str): ルール識別子
Returns:

int: 集計モード

def get_ignore_flying(self, rule_version: str) -> bool:
325    def get_ignore_flying(self, rule_version: str) -> bool:
326        """
327        指定ルール識別子のトビカウントフラグを返す
328
329        Args:
330            rule_version (str): ルール識別子
331
332        Returns:
333            bool: トビカウントフラグ
334
335        """
336        return bool(self.to_dict(rule_version).get("ignore_flying", False))

指定ルール識別子のトビカウントフラグを返す

Arguments:
  • rule_version (str): ルール識別子
Returns:

bool: トビカウントフラグ

def get_undefined_word(self, rule_version: str) -> int:
338    def get_undefined_word(self, rule_version: str) -> int:
339        """
340        指定ルール識別子の未定義ワードタイプを返す
341
342        Args:
343            rule_version (str): ルール識別子
344
345        Returns:
346            int: 未定義ワードタイプ
347
348        """
349        return int(self.to_dict(rule_version).get("undefined_word", 1))

指定ルール識別子の未定義ワードタイプを返す

Arguments:
  • rule_version (str): ルール識別子
Returns:

int: 未定義ワードタイプ

def print(self, rule_version: str) -> str:
351    def print(self, rule_version: str) -> str:
352        """
353        指定ルール識別子の内容を出力する
354
355        Args:
356            rule_version (str): ルール識別子
357
358        Returns:
359            str: 内容
360
361        """
362        ret: str = ""
363        body_data: list[list[str]] = []
364
365        if rule := self.data.get(rule_version):
366            body_data.append(["ルール識別子", rule.rule_version])
367
368            # 集計モード
369            match rule.mode:
370                case 3:
371                    body_data.append(["集計モード", "三人打ち"])
372                case 4:
373                    body_data.append(["集計モード", "四人打ち"])
374                case _:
375                    body_data.append(["集計モード", "未定義"])
376
377            body_data.extend(
378                [
379                    ["素点", f"{rule.origin_point * 100}点持ち / {rule.return_point * 100}点返し"],
380                    ["順位点", " / ".join([f"{pt}pt".replace("-", "▲") for pt in rule.rank_point])],
381                    ["同点時", "順位点山分け" if rule.draw_split else "席順"],
382                ]
383            )
384
385            # マッピング情報
386            if keyword := [word for word, mapping_rule in self.keyword_mapping.items() if mapping_rule == rule_version]:
387                body_data.append(["成績登録ワード", "、".join(keyword)])
388            else:
389                body_data.append(["成績登録ワード", "---"])
390
391            # 記録時間
392            body_data.append(["記録数", f"{rule.count} ゲーム"])
393            if rule.count:
394                body_data.extend(
395                    [
396                        ["記録開始日時", rule.first_time.format(ExtDt.FMT.YMDHMS)],
397                        ["最終記録日時", rule.last_time.format(ExtDt.FMT.YMDHMS)],
398                    ]
399                )
400
401            ret = table2ascii(
402                body=body_data,
403                alignments=[Alignment.LEFT, Alignment.LEFT],
404                style=PresetStyle.plain,
405            )
406
407        return ret

指定ルール識別子の内容を出力する

Arguments:
  • rule_version (str): ルール識別子
Returns:

str: 内容

def info(self) -> None:
409    def info(self) -> None:
410        """定義ルールをログに出力する"""
411        for rule in self.data.values():
412            logging.info(
413                "%s: mode=%s, origin_point=%s, return_point=%s, rank_point=%s, draw_split=%s, ignore_flying=%s, undefined_word=%s",
414                rule.rule_version,
415                rule.mode,
416                rule.origin_point,
417                rule.return_point,
418                rule.rank_point,
419                rule.draw_split,
420                rule.ignore_flying,
421                rule.undefined_word,
422            )
423        if self.keyword_mapping:
424            logging.info("keyword_mapping: %s", self.keyword_mapping)
425        else:
426            logging.warning("keyword_mapping: empty")
427        if self.remarks_words:
428            logging.info("remarks_words: %s", self.remarks_words)
429        else:
430            logging.warning("remarks_words: empty")

定義ルールをログに出力する

def check( self, chk_commands: set[str], chk_members: set[str], default_rule: str) -> None:
432    def check(self, chk_commands: set[str], chk_members: set[str], default_rule: str) -> None:
433        """
434        キーワード重複チェック
435
436        Args:
437            chk_commands (set[str]): チェック対象コマンド名
438            chk_members (set[str]): チェック対象メンバー名/チーム名
439            default_rule (str): デフォルトルールバージョン
440
441        Raises:
442            RuntimeError: 重複あり
443
444        """
445        chk_word: str | RuleData
446
447        try:
448            # ルール識別子チェック
449            for chk_word in self.data.values():
450                if CommandParser().is_valid_command(chk_word.rule_version):
451                    raise RuntimeError(f"ルール識別子にオプションに使用される単語が使用されています。({chk_word.rule_version})")
452                if chk_word.rule_version in ExtDt.valid_keywords():
453                    raise RuntimeError(f"ルール識別子に検索範囲指定に使用される単語が使用されています。({chk_word.rule_version})")
454                if chk_word.rule_version in chk_commands:
455                    raise RuntimeError(f"ルール識別子と定義済みコマンドに重複があります。({chk_word.rule_version})")
456                if chk_word.rule_version in chk_members:
457                    raise RuntimeError(f"ルール識別子と登録メンバー(チーム)に重複があります。({chk_word.rule_version})")
458            # 成績登録ワードチェック
459            for chk_word in self.keyword_mapping.keys():
460                if CommandParser().is_valid_command(chk_word):
461                    raise RuntimeError(f"成績登録ワードにオプションに使用される単語が使用されています。({chk_word})")
462                if chk_word in ExtDt.valid_keywords():
463                    raise RuntimeError(f"成績登録ワードに検索範囲指定に使用される単語が使用されています。({chk_word})")
464                if chk_word in chk_commands:
465                    raise RuntimeError(f"成績登録ワードと定義済みコマンドに重複があります。({chk_word})")
466                if chk_word in chk_members:
467                    raise RuntimeError(f"成績登録ワードと登録メンバー(チーム)に重複があります。({chk_word})")
468            # デフォルトルールバージョンチェック
469            if default_rule not in self.rule_list:
470                raise RuntimeError(f"デフォルトルールバージョンに指定されているルールセットが見つかりません。({default_rule})")
471        except RuntimeError as err:
472            logging.critical("%s", err)
473            sys.exit(1)

キーワード重複チェック

Arguments:
  • chk_commands (set[str]): チェック対象コマンド名
  • chk_members (set[str]): チェック対象メンバー名/チーム名
  • default_rule (str): デフォルトルールバージョン
Raises:
  • RuntimeError: 重複あり
def register_to_database(self) -> None:
475    def register_to_database(self) -> None:
476        """ルールセット情報をDBに登録する"""
477        dbutil.execute("delete from rule;")
478        for rule in self.rule_list:
479            params = self.to_dict(rule)
480            params.update(rank_point=" ".join(map(str, params["rank_point"])))
481            dbutil.execute(
482                """
483                insert into
484                rule (
485                    rule_version, mode, origin_point, return_point, rank_point, ignore_flying, draw_split, undefined_word
486                ) values (
487                    :rule_version, :mode, :origin_point, :return_point, :rank_point, :ignore_flying, :draw_split, :undefined_word
488                );
489                """,
490                params,
491            )

ルールセット情報をDBに登録する

rule_list: list[str]
493    @property
494    def rule_list(self) -> list[str]:
495        """
496        定義済みルール識別子の列挙
497
498        Returns:
499            list[str]: ルール識別子
500
501        """
502        return [x.rule_version for x in self.data.values()]

定義済みルール識別子の列挙

Returns:

list[str]: ルール識別子