libs.commands.report.stats_list

libs/commands/report/results_list.py

  1"""
  2libs/commands/report/results_list.py
  3"""
  4
  5from typing import TYPE_CHECKING
  6
  7import matplotlib.pyplot as plt
  8
  9import libs.global_value as g
 10from libs.domain.datamodels import GameInfo
 11from libs.functions import message
 12from libs.functions.compose import text_item
 13from libs.types import StyleOptions
 14from libs.utils import formatter, graphutil, textutil
 15
 16if TYPE_CHECKING:
 17    from pathlib import Path  # noqa: F401
 18
 19    import pandas as pd
 20
 21    from integrations.protocols import MessageParserProtocol
 22    from libs.types import MessageType
 23
 24
 25def main(m: "MessageParserProtocol") -> None:
 26    """
 27    成績一覧表を生成する
 28
 29    Args:
 30        m (MessageParserProtocol): メッセージデータ
 31
 32    """
 33    # 検索動作を合わせる
 34    g.params.guest_skip = g.params.guest_skip2
 35
 36    # --- データ取得
 37    game_info = GameInfo()
 38    df = g.params.read_data("REPORT_RESULTS_LIST").reset_index(drop=True)
 39    df.index = df.index + 1
 40    if df.empty:
 41        m.set_headline(message.random_reply(m, "no_hits"), StyleOptions(title="成績一覧"))
 42        m.status.result = False
 43        return
 44
 45    if g.params.anonymous:
 46        mapping_dict = formatter.anonymous_mapping(df["name"].unique().tolist())
 47        df["name"] = df["name"].replace(mapping_dict)
 48
 49    # 見出し設定
 50    if g.params.individual:
 51        title = "個人成績一覧"
 52        df = df.rename(columns={"name": "player"})
 53    else:  # チーム集計
 54        title = "チーム成績一覧"
 55        df = df.rename(columns={"name": "team"})
 56
 57    if g.params.mode == 3:
 58        df.drop(columns=["4th_count", "rank4_rate", "4th_mix"], inplace=True)
 59
 60    # 非表示項目
 61    if g.params.ignore_flying or g.cfg.rule.dropitems(g.params.rule_version) & g.cfg.dropitems.flying:
 62        df = df.drop(columns=["flying_mix", "flying_count", "flying_rate"])
 63    if g.cfg.rule.dropitems(g.params.rule_version) & g.cfg.dropitems.yakuman:
 64        df = df.drop(columns=["yakuman_mix", "yakuman_count", "yakuman_rate"])
 65
 66    file_path: "MessageType"
 67    match g.params.format.lower():
 68        case "text" | "txt":
 69            file_path = text_generation(df)
 70        case "csv":
 71            file_path = csv_generation(df)
 72        case _:
 73            file_path = graph_generation(game_info, df, title)
 74
 75    m.set_headline(message.header(game_info, m), StyleOptions(title=title))
 76    match g.adapter.interface_type:
 77        case "slack" | "discord":
 78            m.set_message(file_path, StyleOptions(title=title, use_comment=True, header_hidden=True))
 79        case "web":
 80            m.set_message(df_generation(df), StyleOptions())
 81        case _:
 82            df = df.filter(
 83                items=["player", "team", "game", "total_mix", "avg_mix", "1st_mix", "2nd_mix", "2nd_mix", "3rd_mix", "4th_mix", "flying_mix", "yakuman_mix"]
 84            )
 85            m.set_message(df, StyleOptions())
 86
 87
 88def graph_generation(game_info: GameInfo, df: "pd.DataFrame", title: str) -> "MessageType":
 89    """
 90    グラフ生成処理
 91
 92    Args:
 93        game_info (GameInfo): ゲーム情報
 94        df (pd.DataFrame): 描写データ
 95        title (str): グラフタイトル
 96
 97    Returns:
 98        MessageType: 生成ファイルパス
 99
100    """
101    if g.adapter.conf.plotting_backend == "plotly":
102        return None
103
104    df = formatter.df_rename(
105        df.filter(
106            items=[
107                "player",
108                "team",
109                "game",
110                "total_mix",
111                "avg_mix",
112                "rank_avg",
113                "1st_mix",
114                "2nd_mix",
115                "3rd_mix",
116                "4th_mix",
117                "rank_dist",
118                "flying_mix",
119                "yakuman_mix",
120            ]
121        ),
122        StyleOptions(),
123    )
124
125    # フォント/色彩設定
126    graphutil.setup()
127    report_file_path = textutil.save_file_path("report.png")
128    plt.rcParams["font.size"] = 6
129
130    match (plt.rcParams["text.color"], plt.rcParams["figure.facecolor"]):
131        case text_color, bg_color if text_color == "black" and bg_color == "white":
132            line_color1 = "#dddddd"
133            line_color2 = "#ffffff"
134        case text_color, bg_color if text_color == "white" and bg_color == "black":
135            line_color1 = "#111111"
136            line_color2 = "#000000"
137        case _:
138            line_color1 = plt.rcParams["figure.facecolor"]
139            line_color2 = plt.rcParams["figure.facecolor"]
140
141    column_color = ["#000080"] * len(df.columns)
142    cell_color = []
143    for x in range(len(df)):
144        if int(x % 2):
145            cell_color.append([line_color1] * len(df.columns))
146        else:
147            cell_color.append([line_color2] * len(df.columns))
148
149    fig = plt.figure(figsize=(8, (len(df) * 0.2) + 0.8), dpi=200, tight_layout=True)
150    ax_dummy = fig.add_subplot(111)
151    ax_dummy.axis("off")
152
153    plt.title(title, fontsize=12)
154    tb = plt.table(
155        cellText=df.values,
156        colLabels=df.columns,
157        colColours=column_color,
158        cellColours=cell_color,
159        loc="center",
160    )
161
162    tb.auto_set_font_size(False)
163    tb.auto_set_column_width(range(len(df)))
164    for i in range(len(df.columns)):
165        tb[0, i].set_text_props(color="#FFFFFF", weight="bold")
166        for j in range(len(df) + 1):
167            tb[j, i].set_text_props(ha="center")
168
169    # 追加テキスト
170    remark_text = "".join(text_item.remarks(True)) + text_item.search_word(True)
171    add_text = "[検索範囲:{}] [総ゲーム数:{}] {}".format(
172        text_item.search_range(time_pattern="time"),
173        game_info.count,
174        f"[{remark_text}]" if remark_text else "",
175    )
176
177    fig.text(
178        0.01,
179        0.01,  # 表示位置(左下0,0 右下0,1)
180        add_text,
181        transform=fig.transFigure,
182        fontsize=6,
183    )
184
185    fig.savefig(report_file_path)
186    return report_file_path
187
188
189def text_generation(df: "pd.DataFrame") -> "MessageType":
190    """
191    テキストテーブル生成
192
193    Args:
194        df (pd.DataFrame): 描写データ
195
196    Returns:
197        MessageType: 生成ファイルパス
198
199    """
200    report_file_path = g.cfg.setting.work_dir / (f"{g.params.filename}.txt" if g.params.filename else "report.txt")
201
202    df = df.filter(
203        items=[
204            "player",
205            "team",
206            "game",
207            "point_sum",
208            "point_avg",
209            "1st_count",
210            "rank1_rate",
211            "2nd_count",
212            "rank2_rate",
213            "3rd_count",
214            "rank3_rate",
215            "4th_count",
216            "rank4_rate",
217            "rank_avg",
218            "flying_count",
219            "flying_rate",
220            "yakuman_count",
221            "yakuman_rate",
222        ]
223    )
224    fmt = formatter.floatfmt_adjust(df, index=True)
225    df = formatter.df_rename(df, StyleOptions())
226    df.to_markdown(report_file_path, tablefmt="outline", floatfmt=fmt)
227
228    return report_file_path
229
230
231def csv_generation(df: "pd.DataFrame") -> "MessageType":
232    """
233    CSV生成
234
235    Args:
236        df (pd.DataFrame): 描写データ
237
238    Returns:
239        MessageType: 生成ファイルパス
240
241    """
242    report_file_path = g.cfg.setting.work_dir / (f"{g.params.filename}.csv" if g.params.filename else "report.csv")
243
244    df = df.filter(
245        items=[
246            "player",
247            "team",
248            "game",
249            "point_sum",
250            "point_avg",
251            "1st_count",
252            "rank1_rate",
253            "2nd_count",
254            "rank2_rate",
255            "3rd_count",
256            "rank3_rate",
257            "4th_count",
258            "rank4_rate",
259            "rank_avg",
260            "flying_count",
261            "flying_rate",
262            "yakuman_count",
263            "yakuman_rate",
264        ]
265    )
266
267    for x in df.columns:
268        match x:
269            case "point_sum" | "point_avg":
270                df[x] = df[x].round(1)
271            case "rank1_rate" | "rank2_rate" | "rank3_rate" | "rank4_rate" | "flying_rate" | "yakuman_rate":
272                df[x] = df[x].round(2)
273            case "rank_avg":
274                df[x] = df[x].astype(float).round(2)
275
276    df.to_csv(report_file_path)
277
278    return report_file_path
279
280
281def df_generation(df: "pd.DataFrame") -> "MessageType":
282    """
283    テキストテーブル生成
284
285    Args:
286        df (pd.DataFrame): 描写データ
287
288    Returns:
289        MessageType: 整形データ
290
291    """
292    df = df.filter(
293        items=[
294            "player",
295            "team",
296            "game",
297            "point_sum",
298            "point_avg",
299            "1st_count",
300            "rank1_rate",
301            "2nd_count",
302            "rank2_rate",
303            "3rd_count",
304            "rank3_rate",
305            "4th_count",
306            "rank4_rate",
307            "rank_avg",
308            "flying_count",
309            "flying_rate",
310            "yakuman_count",
311            "yakuman_rate",
312        ]
313    )
314
315    return formatter.df_rename(df, StyleOptions())
def main(m: integrations.protocols.MessageParserProtocol) -> None:
26def main(m: "MessageParserProtocol") -> None:
27    """
28    成績一覧表を生成する
29
30    Args:
31        m (MessageParserProtocol): メッセージデータ
32
33    """
34    # 検索動作を合わせる
35    g.params.guest_skip = g.params.guest_skip2
36
37    # --- データ取得
38    game_info = GameInfo()
39    df = g.params.read_data("REPORT_RESULTS_LIST").reset_index(drop=True)
40    df.index = df.index + 1
41    if df.empty:
42        m.set_headline(message.random_reply(m, "no_hits"), StyleOptions(title="成績一覧"))
43        m.status.result = False
44        return
45
46    if g.params.anonymous:
47        mapping_dict = formatter.anonymous_mapping(df["name"].unique().tolist())
48        df["name"] = df["name"].replace(mapping_dict)
49
50    # 見出し設定
51    if g.params.individual:
52        title = "個人成績一覧"
53        df = df.rename(columns={"name": "player"})
54    else:  # チーム集計
55        title = "チーム成績一覧"
56        df = df.rename(columns={"name": "team"})
57
58    if g.params.mode == 3:
59        df.drop(columns=["4th_count", "rank4_rate", "4th_mix"], inplace=True)
60
61    # 非表示項目
62    if g.params.ignore_flying or g.cfg.rule.dropitems(g.params.rule_version) & g.cfg.dropitems.flying:
63        df = df.drop(columns=["flying_mix", "flying_count", "flying_rate"])
64    if g.cfg.rule.dropitems(g.params.rule_version) & g.cfg.dropitems.yakuman:
65        df = df.drop(columns=["yakuman_mix", "yakuman_count", "yakuman_rate"])
66
67    file_path: "MessageType"
68    match g.params.format.lower():
69        case "text" | "txt":
70            file_path = text_generation(df)
71        case "csv":
72            file_path = csv_generation(df)
73        case _:
74            file_path = graph_generation(game_info, df, title)
75
76    m.set_headline(message.header(game_info, m), StyleOptions(title=title))
77    match g.adapter.interface_type:
78        case "slack" | "discord":
79            m.set_message(file_path, StyleOptions(title=title, use_comment=True, header_hidden=True))
80        case "web":
81            m.set_message(df_generation(df), StyleOptions())
82        case _:
83            df = df.filter(
84                items=["player", "team", "game", "total_mix", "avg_mix", "1st_mix", "2nd_mix", "2nd_mix", "3rd_mix", "4th_mix", "flying_mix", "yakuman_mix"]
85            )
86            m.set_message(df, StyleOptions())

成績一覧表を生成する

Arguments:
  • m (MessageParserProtocol): メッセージデータ
def graph_generation( game_info: libs.domain.datamodels.GameInfo, df: pandas.DataFrame, title: str) -> None | str | pathlib.Path | pandas.DataFrame:
 89def graph_generation(game_info: GameInfo, df: "pd.DataFrame", title: str) -> "MessageType":
 90    """
 91    グラフ生成処理
 92
 93    Args:
 94        game_info (GameInfo): ゲーム情報
 95        df (pd.DataFrame): 描写データ
 96        title (str): グラフタイトル
 97
 98    Returns:
 99        MessageType: 生成ファイルパス
100
101    """
102    if g.adapter.conf.plotting_backend == "plotly":
103        return None
104
105    df = formatter.df_rename(
106        df.filter(
107            items=[
108                "player",
109                "team",
110                "game",
111                "total_mix",
112                "avg_mix",
113                "rank_avg",
114                "1st_mix",
115                "2nd_mix",
116                "3rd_mix",
117                "4th_mix",
118                "rank_dist",
119                "flying_mix",
120                "yakuman_mix",
121            ]
122        ),
123        StyleOptions(),
124    )
125
126    # フォント/色彩設定
127    graphutil.setup()
128    report_file_path = textutil.save_file_path("report.png")
129    plt.rcParams["font.size"] = 6
130
131    match (plt.rcParams["text.color"], plt.rcParams["figure.facecolor"]):
132        case text_color, bg_color if text_color == "black" and bg_color == "white":
133            line_color1 = "#dddddd"
134            line_color2 = "#ffffff"
135        case text_color, bg_color if text_color == "white" and bg_color == "black":
136            line_color1 = "#111111"
137            line_color2 = "#000000"
138        case _:
139            line_color1 = plt.rcParams["figure.facecolor"]
140            line_color2 = plt.rcParams["figure.facecolor"]
141
142    column_color = ["#000080"] * len(df.columns)
143    cell_color = []
144    for x in range(len(df)):
145        if int(x % 2):
146            cell_color.append([line_color1] * len(df.columns))
147        else:
148            cell_color.append([line_color2] * len(df.columns))
149
150    fig = plt.figure(figsize=(8, (len(df) * 0.2) + 0.8), dpi=200, tight_layout=True)
151    ax_dummy = fig.add_subplot(111)
152    ax_dummy.axis("off")
153
154    plt.title(title, fontsize=12)
155    tb = plt.table(
156        cellText=df.values,
157        colLabels=df.columns,
158        colColours=column_color,
159        cellColours=cell_color,
160        loc="center",
161    )
162
163    tb.auto_set_font_size(False)
164    tb.auto_set_column_width(range(len(df)))
165    for i in range(len(df.columns)):
166        tb[0, i].set_text_props(color="#FFFFFF", weight="bold")
167        for j in range(len(df) + 1):
168            tb[j, i].set_text_props(ha="center")
169
170    # 追加テキスト
171    remark_text = "".join(text_item.remarks(True)) + text_item.search_word(True)
172    add_text = "[検索範囲:{}] [総ゲーム数:{}] {}".format(
173        text_item.search_range(time_pattern="time"),
174        game_info.count,
175        f"[{remark_text}]" if remark_text else "",
176    )
177
178    fig.text(
179        0.01,
180        0.01,  # 表示位置(左下0,0 右下0,1)
181        add_text,
182        transform=fig.transFigure,
183        fontsize=6,
184    )
185
186    fig.savefig(report_file_path)
187    return report_file_path

グラフ生成処理

Arguments:
  • game_info (GameInfo): ゲーム情報
  • df (pd.DataFrame): 描写データ
  • title (str): グラフタイトル
Returns:

MessageType: 生成ファイルパス

def text_generation(df: pandas.DataFrame) -> None | str | pathlib.Path | pandas.DataFrame:
190def text_generation(df: "pd.DataFrame") -> "MessageType":
191    """
192    テキストテーブル生成
193
194    Args:
195        df (pd.DataFrame): 描写データ
196
197    Returns:
198        MessageType: 生成ファイルパス
199
200    """
201    report_file_path = g.cfg.setting.work_dir / (f"{g.params.filename}.txt" if g.params.filename else "report.txt")
202
203    df = df.filter(
204        items=[
205            "player",
206            "team",
207            "game",
208            "point_sum",
209            "point_avg",
210            "1st_count",
211            "rank1_rate",
212            "2nd_count",
213            "rank2_rate",
214            "3rd_count",
215            "rank3_rate",
216            "4th_count",
217            "rank4_rate",
218            "rank_avg",
219            "flying_count",
220            "flying_rate",
221            "yakuman_count",
222            "yakuman_rate",
223        ]
224    )
225    fmt = formatter.floatfmt_adjust(df, index=True)
226    df = formatter.df_rename(df, StyleOptions())
227    df.to_markdown(report_file_path, tablefmt="outline", floatfmt=fmt)
228
229    return report_file_path

テキストテーブル生成

Arguments:
  • df (pd.DataFrame): 描写データ
Returns:

MessageType: 生成ファイルパス

def csv_generation(df: pandas.DataFrame) -> None | str | pathlib.Path | pandas.DataFrame:
232def csv_generation(df: "pd.DataFrame") -> "MessageType":
233    """
234    CSV生成
235
236    Args:
237        df (pd.DataFrame): 描写データ
238
239    Returns:
240        MessageType: 生成ファイルパス
241
242    """
243    report_file_path = g.cfg.setting.work_dir / (f"{g.params.filename}.csv" if g.params.filename else "report.csv")
244
245    df = df.filter(
246        items=[
247            "player",
248            "team",
249            "game",
250            "point_sum",
251            "point_avg",
252            "1st_count",
253            "rank1_rate",
254            "2nd_count",
255            "rank2_rate",
256            "3rd_count",
257            "rank3_rate",
258            "4th_count",
259            "rank4_rate",
260            "rank_avg",
261            "flying_count",
262            "flying_rate",
263            "yakuman_count",
264            "yakuman_rate",
265        ]
266    )
267
268    for x in df.columns:
269        match x:
270            case "point_sum" | "point_avg":
271                df[x] = df[x].round(1)
272            case "rank1_rate" | "rank2_rate" | "rank3_rate" | "rank4_rate" | "flying_rate" | "yakuman_rate":
273                df[x] = df[x].round(2)
274            case "rank_avg":
275                df[x] = df[x].astype(float).round(2)
276
277    df.to_csv(report_file_path)
278
279    return report_file_path

CSV生成

Arguments:
  • df (pd.DataFrame): 描写データ
Returns:

MessageType: 生成ファイルパス

def df_generation(df: pandas.DataFrame) -> None | str | pathlib.Path | pandas.DataFrame:
282def df_generation(df: "pd.DataFrame") -> "MessageType":
283    """
284    テキストテーブル生成
285
286    Args:
287        df (pd.DataFrame): 描写データ
288
289    Returns:
290        MessageType: 整形データ
291
292    """
293    df = df.filter(
294        items=[
295            "player",
296            "team",
297            "game",
298            "point_sum",
299            "point_avg",
300            "1st_count",
301            "rank1_rate",
302            "2nd_count",
303            "rank2_rate",
304            "3rd_count",
305            "rank3_rate",
306            "4th_count",
307            "rank4_rate",
308            "rank_avg",
309            "flying_count",
310            "flying_rate",
311            "yakuman_count",
312            "yakuman_rate",
313        ]
314    )
315
316    return formatter.df_rename(df, StyleOptions())

テキストテーブル生成

Arguments:
  • df (pd.DataFrame): 描写データ
Returns:

MessageType: 整形データ