libs.domain.datamodels
libs/domain/datamodels.py
1""" 2libs/domain/datamodels.py 3""" 4 5import logging 6from dataclasses import MISSING, dataclass, field, fields 7from math import ceil 8from typing import TYPE_CHECKING, Literal, Optional 9 10import pandas as pd 11 12import libs.global_value as g 13from libs.utils.timekit import ExtendedDatetime as ExtDt 14 15if TYPE_CHECKING: 16 from pathlib import Path 17 18 from integrations.protocols import MessageParserProtocol 19 from libs.domain.score import GameResult 20 from libs.types import RemarkDict 21 22 23@dataclass 24class Args: 25 """コマンドラインオプション""" 26 27 service: str 28 config: "Path" 29 """設定ファイルパス""" 30 no_cleanup: bool 31 """作業ディレクトリの内容を削除""" 32 33 debug: int 34 """デバッグ出力フラグ""" 35 verbose: int 36 """詳細出力フラグ""" 37 38 moderate: bool 39 """INFO以下のログレベル出力を抑止""" 40 notime: bool 41 """ログに日付を付与しない""" 42 43 # Only allowed when --service=standard_io 44 text: str 45 46 # Only allowed when --service=web 47 host: str 48 port: int 49 50 # dbtools 51 compar: bool 52 unification: "Path" 53 recalculation: bool 54 export_data: str 55 import_data: str 56 vacuum: bool 57 gen_test_data: int 58 testcase: Optional["Path"] 59 60 61@dataclass 62class GameInfo: 63 """ゲーム集計情報""" 64 65 count: int = field(default=0) 66 """集計範囲のゲーム数""" 67 first_game: Optional[ExtDt] = field(default=None) 68 """集計範囲の最初のゲーム時間""" 69 last_game: Optional[ExtDt] = field(default=None) 70 """集計範囲の最後のゲーム時間""" 71 first_comment: Optional[str] = field(default=None) 72 """集計範囲の最初のゲームコメント""" 73 last_comment: Optional[str] = field(default=None) 74 """集計範囲の最後のゲームコメント""" 75 76 def __post_init__(self) -> None: 77 self.get() 78 79 def get(self) -> None: 80 """指定条件を満たすゲーム数のカウント、最初と最後の時刻とコメントを取得""" 81 # グローバルパラメータチェック 82 if not g.params.rule_version: 83 g.params.rule_version = g.cfg.setting.default_rule 84 if not g.params.starttime: 85 g.params.starttime = ExtDt().range("全部").start 86 if not g.params.endtime: 87 g.params.endtime = ExtDt().range("全部").end 88 89 # データ収集 90 df = g.params.read_data("GAME_INFO") 91 if df.empty: 92 self.count = 0 93 self.first_game = ExtDt() 94 self.last_game = ExtDt() 95 self.first_comment = None 96 self.last_comment = None 97 else: 98 self.count = int(df["count"].iloc[0]) 99 self.first_game = ExtDt(str(df["first_game"].iloc[0])) 100 self.last_game = ExtDt(str(df["last_game"].iloc[0])) 101 first_comment_val = df["first_comment"].iloc[0] 102 self.first_comment = str(first_comment_val) if pd.notna(first_comment_val) else None 103 last_comment_val = df["last_comment"].iloc[0] 104 self.last_comment = str(last_comment_val) if pd.notna(last_comment_val) else None 105 106 # 規定打数更新 107 if not g.params.stipulated: # 規定打数0はレートから計算 108 match g.params.command: 109 case "results": 110 g.params.stipulated = g.cfg.results.stipulated_calculation(self.count) 111 case "graph": 112 g.params.stipulated = g.cfg.graph.stipulated_calculation(self.count) 113 case "ranking": 114 g.params.stipulated = g.cfg.ranking.stipulated_calculation(self.count) 115 case "report": 116 g.params.stipulated = g.cfg.report.stipulated_calculation(self.count) 117 case _: 118 pass 119 120 logging.debug(self) 121 122 def clear(self) -> None: 123 """情報削除""" 124 self.count = 0 125 self.first_game = None 126 self.first_comment = None 127 self.last_game = None 128 self.last_comment = None 129 130 131@dataclass 132class ComparisonResults: 133 """突合結果""" 134 135 search_after: int = field(default=-7) 136 """突合範囲(日数)""" 137 score_list: dict[str, "MessageParserProtocol"] = field(default_factory=dict) 138 """スコアリスト(一時保管用)""" 139 140 mismatch: list[dict[str, "GameResult"]] = field(default_factory=list) 141 """スコア差分""" 142 missing: list["GameResult"] = field(default_factory=list) 143 """スコア追加""" 144 delete: list["GameResult"] = field(default_factory=list) 145 """スコア削除""" 146 remark_mod: list["RemarkDict"] = field(default_factory=list) 147 """メモ変更""" 148 remark_del: list["RemarkDict"] = field(default_factory=list) 149 """メモ削除""" 150 invalid_score: list["GameResult"] = field(default_factory=list) 151 """素点合計不一致""" 152 pending: list["GameResult"] = field(default_factory=list) 153 """処理保留データ""" 154 155 @property 156 def after(self) -> ExtDt: 157 """突合開始日時""" 158 return ExtDt(days=self.search_after, hours=g.cfg.setting.time_adjust) 159 160 @property 161 def before(self) -> ExtDt: 162 """突合終了日時""" 163 return ExtDt() 164 165 def output( 166 self, 167 kind: Literal[ 168 "summary", 169 "headline", 170 "pending", 171 "mismatch", 172 "missing", 173 "delete", 174 "remark_mod", 175 "remark_del", 176 "invalid_score", 177 ], 178 ) -> str: 179 """ 180 出力メッセージ生成 181 182 Args: 183 kind (Literal[summary, headline, pending, mismatch, missing, delete, remark_mod, remark_del, invalid_score]): 種類 184 185 Returns: 186 str: 生成文字列 187 188 """ # noqa: E501 189 ret: str = "" 190 match kind: 191 case "summary": 192 ret += f"pending:{len(self.pending)} " 193 ret += f"mismatch:{len(self.mismatch)} " 194 ret += f"missing:{len(self.missing)} " 195 ret += f"delete:{len(self.delete)} " 196 ret += f"remark_mod:{len(self.remark_mod)} " 197 ret += f"remark_del:{len(self.remark_del)} " 198 ret += f"invalid_score:{len(self.invalid_score)} " 199 case "headline": 200 ret = f"突合範囲:{self.after.format(ExtDt.FMT.YMDHMS)} - {self.before.format(ExtDt.FMT.YMDHMS)}" 201 case "pending": 202 ret += f"* 保留:{len(self.pending)}件\n" 203 for score in self.pending: 204 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 205 case "mismatch": 206 ret += f"* 不一致:{len(self.mismatch)}件\n" 207 for score_dict in self.mismatch: 208 ret += f"{ExtDt(float(score_dict['before'].ts)).format(ExtDt.FMT.YMDHMS)}\n" 209 ret += f"\t修正前:{score_dict['before'].to_text()}\n" 210 ret += f"\t修正後:{score_dict['after'].to_text()}\n" 211 case "missing": 212 ret += f"* 取りこぼし:{len(self.missing)}件\n" 213 for score in self.missing: 214 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 215 case "delete": 216 ret += f"* 削除漏れ:{len(self.delete)}件\n" 217 for score in self.delete: 218 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 219 case "remark_mod": 220 ret += f"* メモ更新:{len(self.remark_mod)}件\n" 221 for remark in self.remark_mod: 222 ret += f"{ExtDt(float(remark['thread_ts'])).format(ExtDt.FMT.YMDHMS)} " 223 ret += f"{remark['name']} {remark['matter']}\n" 224 case "remark_del": 225 ret += f"* メモ削除:{len(self.remark_del)}件\n" 226 case "invalid_score": 227 ret += f"* 素点合計不一致:{len(self.invalid_score)}件\n" 228 for score in self.invalid_score: 229 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 230 231 return ret 232 233 234@dataclass 235class ParameterData: 236 """動作パラメータ""" 237 238 # 検索条件変更フラグ 239 individual: bool = field(default=True) 240 """個人/チーム集計切替フラグ 241 - *True*: 個人集計 242 - *False*: チーム集計 243 """ 244 guest_skip: bool = field(default=True) 245 """ゲストアリ/ナシフラグ(サマリ集計用)""" 246 guest_skip2: bool = field(default=True) 247 """ゲストアリ/ナシフラグ(詳細集計用)""" 248 unregistered_replace: bool = field(default=True) 249 """メンバー未登録プレイヤー名をゲストに置き換えるかフラグ 250 - *True*: 置き換える 251 - *False*: 置き換えない 252 """ 253 friendly_fire: bool = field(default=False) 254 """チーム戦集計時のチーム同卓ゲームの扱い 255 - *True*: チーム同卓ゲームを集計(同じチームのポイントは合算される) 256 - *False*: チーム同卓ゲームを集計対象外にする 257 """ 258 259 # 動作変更フラグ 260 statistics: bool = field(default=False) 261 """統計情報表示""" 262 263 # 表示情報変更フラグ 264 ranked: int = field(default=3) 265 """ランキング/レーティングで表示する順位""" 266 stipulated: int = field(default=0) 267 """規定打数指定""" 268 stipulated_rate: float = field(default=0.05) 269 """規定打数計算レート""" 270 271 # 集約条件変更フラグ 272 interval: int = field(default=80) 273 """移動平均算出ゲーム数指定""" 274 275 # コメント検索 276 search_word: str = field(default="") 277 """コメント検索文字列""" 278 group_length: int = field(default=0) 279 """コメント検索時に指定文字数でグループ化する""" 280 281 def default_reset(self) -> None: 282 """デフォルト値にリセット""" 283 for f in fields(self): 284 if f.default is not MISSING: 285 setattr(self, f.name, f.default) 286 elif f.default_factory is not MISSING: 287 setattr(self, f.name, f.default_factory()) 288 289 290@dataclass 291class CommandAttrs(ParameterData): 292 """サブコマンド設定パラメータ""" 293 294 commandword: list[str] = field(default_factory=list) 295 """呼び出しキーワード""" 296 command_suffix: list[str] = field(default_factory=list) 297 """コマンド接尾辞(登録キーワード+接尾辞を呼び出しキーワードとして扱う)""" 298 299 aggregation_range: str = field(default="当日") 300 """検索範囲未指定時に使用される範囲""" 301 always_argument: list[str] = field(default_factory=list) 302 """オプションとして常に付与される文字列""" 303 dropitems: list[str] = field(default_factory=list) 304 """非表示にする項目""" 305 306 def stipulated_calculation(self, game_count: int) -> int: 307 """ 308 規定打数をゲーム数から計算 309 310 Args: 311 game_count (int): 指定ゲーム数 312 313 Returns: 314 int: 規定ゲーム数 315 316 """ 317 return int(ceil(game_count * self.stipulated_rate) + 1)
@dataclass
class
Args:
24@dataclass 25class Args: 26 """コマンドラインオプション""" 27 28 service: str 29 config: "Path" 30 """設定ファイルパス""" 31 no_cleanup: bool 32 """作業ディレクトリの内容を削除""" 33 34 debug: int 35 """デバッグ出力フラグ""" 36 verbose: int 37 """詳細出力フラグ""" 38 39 moderate: bool 40 """INFO以下のログレベル出力を抑止""" 41 notime: bool 42 """ログに日付を付与しない""" 43 44 # Only allowed when --service=standard_io 45 text: str 46 47 # Only allowed when --service=web 48 host: str 49 port: int 50 51 # dbtools 52 compar: bool 53 unification: "Path" 54 recalculation: bool 55 export_data: str 56 import_data: str 57 vacuum: bool 58 gen_test_data: int 59 testcase: Optional["Path"]
コマンドラインオプション
Args( service: str, config: pathlib.Path, no_cleanup: bool, debug: int, verbose: int, moderate: bool, notime: bool, text: str, host: str, port: int, compar: bool, unification: pathlib.Path, recalculation: bool, export_data: str, import_data: str, vacuum: bool, gen_test_data: int, testcase: pathlib.Path | None)
@dataclass
class
GameInfo:
62@dataclass 63class GameInfo: 64 """ゲーム集計情報""" 65 66 count: int = field(default=0) 67 """集計範囲のゲーム数""" 68 first_game: Optional[ExtDt] = field(default=None) 69 """集計範囲の最初のゲーム時間""" 70 last_game: Optional[ExtDt] = field(default=None) 71 """集計範囲の最後のゲーム時間""" 72 first_comment: Optional[str] = field(default=None) 73 """集計範囲の最初のゲームコメント""" 74 last_comment: Optional[str] = field(default=None) 75 """集計範囲の最後のゲームコメント""" 76 77 def __post_init__(self) -> None: 78 self.get() 79 80 def get(self) -> None: 81 """指定条件を満たすゲーム数のカウント、最初と最後の時刻とコメントを取得""" 82 # グローバルパラメータチェック 83 if not g.params.rule_version: 84 g.params.rule_version = g.cfg.setting.default_rule 85 if not g.params.starttime: 86 g.params.starttime = ExtDt().range("全部").start 87 if not g.params.endtime: 88 g.params.endtime = ExtDt().range("全部").end 89 90 # データ収集 91 df = g.params.read_data("GAME_INFO") 92 if df.empty: 93 self.count = 0 94 self.first_game = ExtDt() 95 self.last_game = ExtDt() 96 self.first_comment = None 97 self.last_comment = None 98 else: 99 self.count = int(df["count"].iloc[0]) 100 self.first_game = ExtDt(str(df["first_game"].iloc[0])) 101 self.last_game = ExtDt(str(df["last_game"].iloc[0])) 102 first_comment_val = df["first_comment"].iloc[0] 103 self.first_comment = str(first_comment_val) if pd.notna(first_comment_val) else None 104 last_comment_val = df["last_comment"].iloc[0] 105 self.last_comment = str(last_comment_val) if pd.notna(last_comment_val) else None 106 107 # 規定打数更新 108 if not g.params.stipulated: # 規定打数0はレートから計算 109 match g.params.command: 110 case "results": 111 g.params.stipulated = g.cfg.results.stipulated_calculation(self.count) 112 case "graph": 113 g.params.stipulated = g.cfg.graph.stipulated_calculation(self.count) 114 case "ranking": 115 g.params.stipulated = g.cfg.ranking.stipulated_calculation(self.count) 116 case "report": 117 g.params.stipulated = g.cfg.report.stipulated_calculation(self.count) 118 case _: 119 pass 120 121 logging.debug(self) 122 123 def clear(self) -> None: 124 """情報削除""" 125 self.count = 0 126 self.first_game = None 127 self.first_comment = None 128 self.last_game = None 129 self.last_comment = None
ゲーム集計情報
GameInfo( count: int = 0, first_game: libs.utils.timekit.ExtendedDatetime | None = None, last_game: libs.utils.timekit.ExtendedDatetime | None = None, first_comment: str | None = None, last_comment: str | None = None)
def
get(self) -> None:
80 def get(self) -> None: 81 """指定条件を満たすゲーム数のカウント、最初と最後の時刻とコメントを取得""" 82 # グローバルパラメータチェック 83 if not g.params.rule_version: 84 g.params.rule_version = g.cfg.setting.default_rule 85 if not g.params.starttime: 86 g.params.starttime = ExtDt().range("全部").start 87 if not g.params.endtime: 88 g.params.endtime = ExtDt().range("全部").end 89 90 # データ収集 91 df = g.params.read_data("GAME_INFO") 92 if df.empty: 93 self.count = 0 94 self.first_game = ExtDt() 95 self.last_game = ExtDt() 96 self.first_comment = None 97 self.last_comment = None 98 else: 99 self.count = int(df["count"].iloc[0]) 100 self.first_game = ExtDt(str(df["first_game"].iloc[0])) 101 self.last_game = ExtDt(str(df["last_game"].iloc[0])) 102 first_comment_val = df["first_comment"].iloc[0] 103 self.first_comment = str(first_comment_val) if pd.notna(first_comment_val) else None 104 last_comment_val = df["last_comment"].iloc[0] 105 self.last_comment = str(last_comment_val) if pd.notna(last_comment_val) else None 106 107 # 規定打数更新 108 if not g.params.stipulated: # 規定打数0はレートから計算 109 match g.params.command: 110 case "results": 111 g.params.stipulated = g.cfg.results.stipulated_calculation(self.count) 112 case "graph": 113 g.params.stipulated = g.cfg.graph.stipulated_calculation(self.count) 114 case "ranking": 115 g.params.stipulated = g.cfg.ranking.stipulated_calculation(self.count) 116 case "report": 117 g.params.stipulated = g.cfg.report.stipulated_calculation(self.count) 118 case _: 119 pass 120 121 logging.debug(self)
指定条件を満たすゲーム数のカウント、最初と最後の時刻とコメントを取得
@dataclass
class
ComparisonResults:
132@dataclass 133class ComparisonResults: 134 """突合結果""" 135 136 search_after: int = field(default=-7) 137 """突合範囲(日数)""" 138 score_list: dict[str, "MessageParserProtocol"] = field(default_factory=dict) 139 """スコアリスト(一時保管用)""" 140 141 mismatch: list[dict[str, "GameResult"]] = field(default_factory=list) 142 """スコア差分""" 143 missing: list["GameResult"] = field(default_factory=list) 144 """スコア追加""" 145 delete: list["GameResult"] = field(default_factory=list) 146 """スコア削除""" 147 remark_mod: list["RemarkDict"] = field(default_factory=list) 148 """メモ変更""" 149 remark_del: list["RemarkDict"] = field(default_factory=list) 150 """メモ削除""" 151 invalid_score: list["GameResult"] = field(default_factory=list) 152 """素点合計不一致""" 153 pending: list["GameResult"] = field(default_factory=list) 154 """処理保留データ""" 155 156 @property 157 def after(self) -> ExtDt: 158 """突合開始日時""" 159 return ExtDt(days=self.search_after, hours=g.cfg.setting.time_adjust) 160 161 @property 162 def before(self) -> ExtDt: 163 """突合終了日時""" 164 return ExtDt() 165 166 def output( 167 self, 168 kind: Literal[ 169 "summary", 170 "headline", 171 "pending", 172 "mismatch", 173 "missing", 174 "delete", 175 "remark_mod", 176 "remark_del", 177 "invalid_score", 178 ], 179 ) -> str: 180 """ 181 出力メッセージ生成 182 183 Args: 184 kind (Literal[summary, headline, pending, mismatch, missing, delete, remark_mod, remark_del, invalid_score]): 種類 185 186 Returns: 187 str: 生成文字列 188 189 """ # noqa: E501 190 ret: str = "" 191 match kind: 192 case "summary": 193 ret += f"pending:{len(self.pending)} " 194 ret += f"mismatch:{len(self.mismatch)} " 195 ret += f"missing:{len(self.missing)} " 196 ret += f"delete:{len(self.delete)} " 197 ret += f"remark_mod:{len(self.remark_mod)} " 198 ret += f"remark_del:{len(self.remark_del)} " 199 ret += f"invalid_score:{len(self.invalid_score)} " 200 case "headline": 201 ret = f"突合範囲:{self.after.format(ExtDt.FMT.YMDHMS)} - {self.before.format(ExtDt.FMT.YMDHMS)}" 202 case "pending": 203 ret += f"* 保留:{len(self.pending)}件\n" 204 for score in self.pending: 205 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 206 case "mismatch": 207 ret += f"* 不一致:{len(self.mismatch)}件\n" 208 for score_dict in self.mismatch: 209 ret += f"{ExtDt(float(score_dict['before'].ts)).format(ExtDt.FMT.YMDHMS)}\n" 210 ret += f"\t修正前:{score_dict['before'].to_text()}\n" 211 ret += f"\t修正後:{score_dict['after'].to_text()}\n" 212 case "missing": 213 ret += f"* 取りこぼし:{len(self.missing)}件\n" 214 for score in self.missing: 215 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 216 case "delete": 217 ret += f"* 削除漏れ:{len(self.delete)}件\n" 218 for score in self.delete: 219 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 220 case "remark_mod": 221 ret += f"* メモ更新:{len(self.remark_mod)}件\n" 222 for remark in self.remark_mod: 223 ret += f"{ExtDt(float(remark['thread_ts'])).format(ExtDt.FMT.YMDHMS)} " 224 ret += f"{remark['name']} {remark['matter']}\n" 225 case "remark_del": 226 ret += f"* メモ削除:{len(self.remark_del)}件\n" 227 case "invalid_score": 228 ret += f"* 素点合計不一致:{len(self.invalid_score)}件\n" 229 for score in self.invalid_score: 230 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 231 232 return ret
突合結果
ComparisonResults( search_after: int = -7, score_list: dict[str, integrations.protocols.MessageParserProtocol] = <factory>, mismatch: list[dict[str, libs.domain.score.GameResult]] = <factory>, missing: list[libs.domain.score.GameResult] = <factory>, delete: list[libs.domain.score.GameResult] = <factory>, remark_mod: list[libs.types.RemarkDict] = <factory>, remark_del: list[libs.types.RemarkDict] = <factory>, invalid_score: list[libs.domain.score.GameResult] = <factory>, pending: list[libs.domain.score.GameResult] = <factory>)
156 @property 157 def after(self) -> ExtDt: 158 """突合開始日時""" 159 return ExtDt(days=self.search_after, hours=g.cfg.setting.time_adjust)
突合開始日時
def
output( self, kind: Literal['summary', 'headline', 'pending', 'mismatch', 'missing', 'delete', 'remark_mod', 'remark_del', 'invalid_score']) -> str:
166 def output( 167 self, 168 kind: Literal[ 169 "summary", 170 "headline", 171 "pending", 172 "mismatch", 173 "missing", 174 "delete", 175 "remark_mod", 176 "remark_del", 177 "invalid_score", 178 ], 179 ) -> str: 180 """ 181 出力メッセージ生成 182 183 Args: 184 kind (Literal[summary, headline, pending, mismatch, missing, delete, remark_mod, remark_del, invalid_score]): 種類 185 186 Returns: 187 str: 生成文字列 188 189 """ # noqa: E501 190 ret: str = "" 191 match kind: 192 case "summary": 193 ret += f"pending:{len(self.pending)} " 194 ret += f"mismatch:{len(self.mismatch)} " 195 ret += f"missing:{len(self.missing)} " 196 ret += f"delete:{len(self.delete)} " 197 ret += f"remark_mod:{len(self.remark_mod)} " 198 ret += f"remark_del:{len(self.remark_del)} " 199 ret += f"invalid_score:{len(self.invalid_score)} " 200 case "headline": 201 ret = f"突合範囲:{self.after.format(ExtDt.FMT.YMDHMS)} - {self.before.format(ExtDt.FMT.YMDHMS)}" 202 case "pending": 203 ret += f"* 保留:{len(self.pending)}件\n" 204 for score in self.pending: 205 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 206 case "mismatch": 207 ret += f"* 不一致:{len(self.mismatch)}件\n" 208 for score_dict in self.mismatch: 209 ret += f"{ExtDt(float(score_dict['before'].ts)).format(ExtDt.FMT.YMDHMS)}\n" 210 ret += f"\t修正前:{score_dict['before'].to_text()}\n" 211 ret += f"\t修正後:{score_dict['after'].to_text()}\n" 212 case "missing": 213 ret += f"* 取りこぼし:{len(self.missing)}件\n" 214 for score in self.missing: 215 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 216 case "delete": 217 ret += f"* 削除漏れ:{len(self.delete)}件\n" 218 for score in self.delete: 219 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 220 case "remark_mod": 221 ret += f"* メモ更新:{len(self.remark_mod)}件\n" 222 for remark in self.remark_mod: 223 ret += f"{ExtDt(float(remark['thread_ts'])).format(ExtDt.FMT.YMDHMS)} " 224 ret += f"{remark['name']} {remark['matter']}\n" 225 case "remark_del": 226 ret += f"* メモ削除:{len(self.remark_del)}件\n" 227 case "invalid_score": 228 ret += f"* 素点合計不一致:{len(self.invalid_score)}件\n" 229 for score in self.invalid_score: 230 ret += f"{ExtDt(float(score.ts)).format(ExtDt.FMT.YMDHMS)} {score.to_text()}\n" 231 232 return ret
出力メッセージ生成
Arguments:
- kind (Literal[summary, headline, pending, mismatch, missing, delete, remark_mod, remark_del, invalid_score]): 種類
Returns:
str: 生成文字列
@dataclass
class
ParameterData:
235@dataclass 236class ParameterData: 237 """動作パラメータ""" 238 239 # 検索条件変更フラグ 240 individual: bool = field(default=True) 241 """個人/チーム集計切替フラグ 242 - *True*: 個人集計 243 - *False*: チーム集計 244 """ 245 guest_skip: bool = field(default=True) 246 """ゲストアリ/ナシフラグ(サマリ集計用)""" 247 guest_skip2: bool = field(default=True) 248 """ゲストアリ/ナシフラグ(詳細集計用)""" 249 unregistered_replace: bool = field(default=True) 250 """メンバー未登録プレイヤー名をゲストに置き換えるかフラグ 251 - *True*: 置き換える 252 - *False*: 置き換えない 253 """ 254 friendly_fire: bool = field(default=False) 255 """チーム戦集計時のチーム同卓ゲームの扱い 256 - *True*: チーム同卓ゲームを集計(同じチームのポイントは合算される) 257 - *False*: チーム同卓ゲームを集計対象外にする 258 """ 259 260 # 動作変更フラグ 261 statistics: bool = field(default=False) 262 """統計情報表示""" 263 264 # 表示情報変更フラグ 265 ranked: int = field(default=3) 266 """ランキング/レーティングで表示する順位""" 267 stipulated: int = field(default=0) 268 """規定打数指定""" 269 stipulated_rate: float = field(default=0.05) 270 """規定打数計算レート""" 271 272 # 集約条件変更フラグ 273 interval: int = field(default=80) 274 """移動平均算出ゲーム数指定""" 275 276 # コメント検索 277 search_word: str = field(default="") 278 """コメント検索文字列""" 279 group_length: int = field(default=0) 280 """コメント検索時に指定文字数でグループ化する""" 281 282 def default_reset(self) -> None: 283 """デフォルト値にリセット""" 284 for f in fields(self): 285 if f.default is not MISSING: 286 setattr(self, f.name, f.default) 287 elif f.default_factory is not MISSING: 288 setattr(self, f.name, f.default_factory())
動作パラメータ
ParameterData( individual: bool = True, guest_skip: bool = True, guest_skip2: bool = True, unregistered_replace: bool = True, friendly_fire: bool = False, statistics: bool = False, ranked: int = 3, stipulated: int = 0, stipulated_rate: float = 0.05, interval: int = 80, search_word: str = '', group_length: int = 0)
291@dataclass 292class CommandAttrs(ParameterData): 293 """サブコマンド設定パラメータ""" 294 295 commandword: list[str] = field(default_factory=list) 296 """呼び出しキーワード""" 297 command_suffix: list[str] = field(default_factory=list) 298 """コマンド接尾辞(登録キーワード+接尾辞を呼び出しキーワードとして扱う)""" 299 300 aggregation_range: str = field(default="当日") 301 """検索範囲未指定時に使用される範囲""" 302 always_argument: list[str] = field(default_factory=list) 303 """オプションとして常に付与される文字列""" 304 dropitems: list[str] = field(default_factory=list) 305 """非表示にする項目""" 306 307 def stipulated_calculation(self, game_count: int) -> int: 308 """ 309 規定打数をゲーム数から計算 310 311 Args: 312 game_count (int): 指定ゲーム数 313 314 Returns: 315 int: 規定ゲーム数 316 317 """ 318 return int(ceil(game_count * self.stipulated_rate) + 1)
サブコマンド設定パラメータ
CommandAttrs( individual: bool = True, guest_skip: bool = True, guest_skip2: bool = True, unregistered_replace: bool = True, friendly_fire: bool = False, statistics: bool = False, ranked: int = 3, stipulated: int = 0, stipulated_rate: float = 0.05, interval: int = 80, search_word: str = '', group_length: int = 0, commandword: list[str] = <factory>, command_suffix: list[str] = <factory>, aggregation_range: str = '当日', always_argument: list[str] = <factory>, dropitems: list[str] = <factory>)
def
stipulated_calculation(self, game_count: int) -> int:
307 def stipulated_calculation(self, game_count: int) -> int: 308 """ 309 規定打数をゲーム数から計算 310 311 Args: 312 game_count (int): 指定ゲーム数 313 314 Returns: 315 int: 規定ゲーム数 316 317 """ 318 return int(ceil(game_count * self.stipulated_rate) + 1)
規定打数をゲーム数から計算
Arguments:
- game_count (int): 指定ゲーム数
Returns:
int: 規定ゲーム数