integrations.web.events.create_bp

ルート設定

 1"""
 2ルート設定
 3"""
 4
 5from integrations.web.events.create_bp.detail import detail_bp
 6from integrations.web.events.create_bp.graph import graph_bp
 7from integrations.web.events.create_bp.index import index_bp
 8from integrations.web.events.create_bp.member import member_bp
 9from integrations.web.events.create_bp.ranking import ranking_bp
10from integrations.web.events.create_bp.report import report_bp
11from integrations.web.events.create_bp.score import score_bp
12from integrations.web.events.create_bp.summary import summary_bp
13from integrations.web.events.create_bp.user_assets import user_assets_bp
14
15__all__ = [
16    "detail_bp",
17    "graph_bp",
18    "index_bp",
19    "member_bp",
20    "ranking_bp",
21    "report_bp",
22    "score_bp",
23    "summary_bp",
24    "user_assets_bp",
25]
def detail_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
21def detail_bp(adapter: "ServiceAdapter") -> Blueprint:
22    """
23    個人成績詳細ページ用Blueprint
24
25    Args:
26        adapter (ServiceAdapter): web用アダプタ
27
28    Returns:
29        Blueprint: Blueprint
30
31    """
32    bp = Blueprint("detail", __name__, url_prefix="/detail")
33
34    @bp.route("/", methods=["GET", "POST"])
35    def detail() -> "Response":
36        if not adapter.conf.view_summary:
37            abort(403)
38
39        padding = current_app.config["padding"]
40        players = g.cfg.member.lists
41
42        m = adapter.parser()
43        cookie_data = cast(dict[str, Any], adapter.functions.get_cookie(request))
44        text = " ".join(cookie_data.values())
45        m.data.text = f"{g.cfg.results.commandword[0]} {text}"
46        libs.dispatcher.by_keyword(m)
47
48        _, message = adapter.functions.header_message(m)
49
50        for data, options in m.post.message:
51            if options.title:
52                message += f"<h2>{options.title}</h2>\n"
53
54            if isinstance(data, pd.DataFrame):
55                show_index = options.show_index
56                if options.title == "戦績" and g.params.verbose:
57                    padding = "0.25em 0.75em"
58                    data = _conv_verbose(data)
59                message += adapter.functions.to_styled_html(data, padding, show_index)
60                message = message.replace(f">{g.params.player_name}<", f"><div class='player_name'>{g.params.player_name}</div><")
61
62            if isinstance(data, str):
63                message += adapter.functions.to_text_html(data)
64
65        cookie_data.update({"body": message, "players": players, **asdict(adapter.conf)})
66        page = adapter.functions.set_cookie("detail.html", request, cookie_data)
67
68        return page
69
70    return bp

個人成績詳細ページ用Blueprint

Arguments:
  • adapter (ServiceAdapter): web用アダプタ
Returns:

Blueprint: Blueprint

def graph_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
22def graph_bp(adapter: "ServiceAdapter") -> Blueprint:
23    """
24    グラフ表示ページ用Blueprint
25
26    Args:
27        adapter (ServiceAdapter): web用アダプタ
28
29    Returns:
30        Blueprint: Blueprint
31
32    """
33    bp = Blueprint("graph", __name__, url_prefix="/graph")
34
35    @bp.route("/", methods=["GET", "POST"])
36    def graph() -> "Response":
37        if not adapter.conf.view_graph:
38            abort(403)
39
40        padding = current_app.config["padding"]
41
42        m = adapter.parser()
43        cookie_data = adapter.functions.get_cookie(request)
44        text = " ".join(cookie_data.values())
45        m.data.text = f"{g.cfg.graph.commandword[0]} {text}"
46        libs.dispatcher.by_keyword(m)
47
48        _, message = adapter.functions.header_message(m)
49
50        for data, options in m.post.message:
51            if isinstance(data, PosixPath) and data.exists():
52                message += f"<p>\n{data.read_text(encoding='utf-8')}\n</p>\n"
53
54            if isinstance(data, pd.DataFrame) and options.title == "素点情報":
55                show_index = options.show_index
56                data["ゲーム数"] = data["ゲーム数"].astype("float")
57                data.rename(columns={"平均値(x)": "平均値", "中央値(|)": "中央値"}, inplace=True)
58                message += f"<h2>{options.title}</h2>\n"
59                message += adapter.functions.to_styled_html(data, padding, show_index)
60
61            if isinstance(data, pd.DataFrame) and options.title == "順位/ポイント情報":
62                show_index = options.show_index
63                data["ゲーム数"] = data["ゲーム数"].astype("float")
64                multi = [
65                    ("", "ゲーム数"),
66                    ("1位", "獲得数"),
67                    ("1位", "獲得率"),
68                    ("2位", "獲得数"),
69                    ("2位", "獲得率"),
70                    ("3位", "獲得数"),
71                    ("3位", "獲得率"),
72                    ("4位", "獲得数"),
73                    ("4位", "獲得率"),
74                    ("", "平均順位"),
75                    ("区間成績", "区間ポイント"),
76                    ("区間成績", "区間平均"),
77                    ("", "通算ポイント"),
78                ]
79                data.columns = pd.MultiIndex.from_tuples(multi)
80                message += f"<h2>{options.title}</h2>\n"
81                message += adapter.functions.to_styled_html(data, padding, show_index)
82
83        cookie_data.update(body=message, **asdict(adapter.conf))
84        page = adapter.functions.set_cookie("graph.html", request, cookie_data)
85
86        return page
87
88    return bp

グラフ表示ページ用Blueprint

Arguments:
  • adapter (ServiceAdapter): web用アダプタ
Returns:

Blueprint: Blueprint

def index_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
15def index_bp(adapter: "ServiceAdapter") -> Blueprint:
16    """
17    index用Blueprint
18
19    Args:
20        adapter (ServiceAdapter): web用アダプタ
21
22    Returns:
23        Blueprint: Blueprint
24
25    """
26    bp = Blueprint("index", __name__, url_prefix="/")
27
28    @bp.route("/", methods=["GET", "POST"])
29    def index() -> str:
30        return render_template("index.html", **asdict(adapter.conf))
31
32    return bp

index用Blueprint

Arguments:
  • adapter (ServiceAdapter): web用アダプタ
Returns:

Blueprint: Blueprint

def member_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
20def member_bp(adapter: "ServiceAdapter") -> Blueprint:
21    """
22    メンバー管理ページ用Blueprint
23
24    Args:
25        adapter (ServiceAdapter): web用アダプタ
26
27    Returns:
28        Blueprint: Blueprint
29
30    """
31    bp = Blueprint("member", __name__, url_prefix="/member")
32
33    @bp.route("/", methods=["GET", "POST"])
34    def mgt_member() -> str:
35        if not adapter.conf.management_member:
36            abort(403)
37
38        m = adapter.parser()
39        g.params = dictutil.placeholder(g.cfg.help, m)
40
41        padding = current_app.config["padding"]
42        data: dict[str, Any] = asdict(adapter.conf)
43
44        if request.method == "POST":
45            match request.form.get("action"):
46                case "add_member":
47                    if name := request.form.get("member", "").strip():
48                        ret = member.append(name.split()[0:2])
49                        data.update(result_msg=ret)
50                case "del_member":
51                    if name := request.form.get("member", "").strip():
52                        ret = member.remove(name.split()[0:2])
53                        data.update(result_msg=ret)
54                case "add_team":
55                    if team_name := request.form.get("team", "").strip():
56                        ret = team.append(team_name.split()[0:2])
57                        data.update(result_msg=ret)
58                case "del_team":
59                    if team_name := request.form.get("team", "").strip():
60                        ret = team.remove(team_name.split()[0:2])
61                        data.update(result_msg=ret)
62                case "delete_all_team":
63                    ret = team.clear()
64                    data.update(result_msg=ret)
65
66            lookup.read_memberslist()
67
68        member_df = g.params.read_data("MEMBER_INFO")
69        if member_df.empty:
70            data.update(member_table="<p>登録済みメンバーはいません。</p>")
71        else:
72            data.update(member_table=adapter.functions.to_styled_html(member_df.drop(columns=["id"]), padding))
73
74        team_df = g.params.read_data("TEAM_INFO")
75        if team_df.empty:
76            data.update(team_table="<p>登録済みチームはありません。</p>")
77        else:
78            data.update(team_table=adapter.functions.to_styled_html(team_df.drop(columns=["id"]), padding))
79
80        return render_template("registry.html", **data)
81
82    return bp

メンバー管理ページ用Blueprint

Arguments:
  • adapter (ServiceAdapter): web用アダプタ
Returns:

Blueprint: Blueprint

def ranking_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
21def ranking_bp(adapter: "ServiceAdapter") -> Blueprint:
22    """
23    ランキングページ用Blueprint
24
25    Args:
26        adapter (ServiceAdapter): web用アダプタ
27
28    Returns:
29        Blueprint: Blueprint
30
31    """
32    bp = Blueprint("ranking", __name__, url_prefix="/ranking")
33
34    @bp.route("/", methods=["GET", "POST"])
35    def ranking() -> "Response":
36        if not adapter.conf.view_ranking:
37            abort(403)
38
39        padding = current_app.config["padding"]
40
41        m = adapter.parser()
42        cookie_data = adapter.functions.get_cookie(request)
43        text = " ".join(cookie_data.values())
44        m.data.text = f"{g.cfg.ranking.commandword[0]} {text}"
45        libs.dispatcher.by_keyword(m)
46
47        _, message = adapter.functions.header_message(m)
48
49        for data, options in m.post.message:
50            if options.title:
51                message += f"<h2>{options.title}</h2>\n"
52
53            if isinstance(data, pd.DataFrame):
54                show_index = options.show_index
55                message += adapter.functions.to_styled_html(data, padding, show_index)
56
57            if isinstance(data, str):
58                message += adapter.functions.to_text_html(data)
59
60        cookie_data.update(body=message, **asdict(adapter.conf))
61        page = adapter.functions.set_cookie("ranking.html", request, cookie_data)
62
63        return page
64
65    return bp

ランキングページ用Blueprint

Arguments:
  • adapter (ServiceAdapter): web用アダプタ
Returns:

Blueprint: Blueprint

def report_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
 21def report_bp(adapter: "ServiceAdapter") -> Blueprint:
 22    """
 23    レポートページ用Blueprint
 24
 25    Args:
 26        adapter (ServiceAdapter): web用アダプタ
 27
 28    Returns:
 29        Blueprint: Blueprint
 30
 31    """
 32    bp = Blueprint("report", __name__, url_prefix="/report")
 33
 34    @bp.route("/", methods=["GET", "POST"])
 35    def report() -> "Response":
 36        if not adapter.conf.view_report:
 37            abort(403)
 38
 39        padding = current_app.config["padding"]
 40
 41        m = adapter.parser()
 42        cookie_data = adapter.functions.get_cookie(request)
 43        text = " ".join(cookie_data.values())
 44        m.data.text = f"{g.cfg.report.commandword[0]} {text}"
 45        libs.dispatcher.by_keyword(m)
 46
 47        headline_title, message = adapter.functions.header_message(m)
 48
 49        for data, options in m.post.message:
 50            if not options.title.isnumeric() and options.title:
 51                message += f"<h2>{options.title}</h2>\n"
 52
 53            if isinstance(data, pd.DataFrame):
 54                show_index = options.show_index
 55                if {"個人成績一覧", "チーム成績一覧"} & set(headline_title):
 56                    check_column = data.columns.to_list()
 57                    multi = [
 58                        ("", "プレイヤー名" if g.params.individual else "チーム名"),
 59                        ("", "ゲーム数"),
 60                        ("ポイント", "通算") if {"通算ポイント", "平均ポイント"}.issubset(check_column) else None,
 61                        ("ポイント", "平均") if {"通算ポイント", "平均ポイント"}.issubset(check_column) else None,
 62                        ("1位", "獲得数") if {"1位数", "1位率"}.issubset(check_column) else None,
 63                        ("1位", "獲得率") if {"1位数", "1位率"}.issubset(check_column) else None,
 64                        ("2位", "獲得数") if {"2位数", "2位率"}.issubset(check_column) else None,
 65                        ("2位", "獲得率") if {"2位数", "2位率"}.issubset(check_column) else None,
 66                        ("3位", "獲得数") if {"3位数", "3位率"}.issubset(check_column) else None,
 67                        ("3位", "獲得率") if {"3位数", "3位率"}.issubset(check_column) else None,
 68                        ("4位", "獲得数") if {"4位数", "4位率"}.issubset(check_column) else None,
 69                        ("4位", "獲得率") if {"4位数", "4位率"}.issubset(check_column) else None,
 70                        ("平均順位", "") if {"平均順位", "平順"} & set(check_column) else None,
 71                        ("トビ", "回数") if {"トビ数", "トビ率"}.issubset(check_column) else None,
 72                        ("トビ", "率") if {"トビ数", "トビ率"}.issubset(check_column) else None,
 73                        ("役満", "和了数") if {"役満和了数", "役満和了率"}.issubset(check_column) else None,
 74                        ("役満", "和了率") if {"役満和了数", "役満和了率"}.issubset(check_column) else None,
 75                    ]
 76                    data.columns = pd.MultiIndex.from_tuples([x for x in multi if x is not None])
 77                elif "成績上位者" in headline_title:
 78                    name = "名前" if g.params.individual else "チーム"
 79                    check_column = data.columns.to_list()
 80                    multi = [
 81                        ("", "集計月"),
 82                        ("1位", name),
 83                        ("1位", "獲得ポイント"),
 84                        ("2位", name),
 85                        ("2位", "獲得ポイント"),
 86                        ("3位", name),
 87                        ("3位", "獲得ポイント"),
 88                        ("4位", name),
 89                        ("4位", "獲得ポイント"),
 90                        ("5位", name),
 91                        ("5位", "獲得ポイント"),
 92                    ]
 93                    data.columns = pd.MultiIndex.from_tuples([x for x in multi if x is not None])
 94                message += adapter.functions.to_styled_html(data, padding, show_index)
 95
 96            if isinstance(data, str):
 97                message += adapter.functions.to_text_html(data)
 98
 99        cookie_data.update(body=message, **asdict(adapter.conf))
100        page = adapter.functions.set_cookie("report.html", request, cookie_data)
101
102        return page
103
104    return bp

レポートページ用Blueprint

Arguments:
  • adapter (ServiceAdapter): web用アダプタ
Returns:

Blueprint: Blueprint

def score_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
 23def score_bp(adapter: "ServiceAdapter") -> Blueprint:
 24    """
 25    スコア管理ページ用Blueprint
 26
 27    Args:
 28        adapter (ServiceAdapter): web用アダプタ
 29
 30    Returns:
 31        Blueprint: Blueprint
 32
 33    """
 34    bp = Blueprint("score", __name__, url_prefix="/score")
 35
 36    @bp.route("/", methods=["GET", "POST"])
 37    def mgt_score() -> str:
 38        if not adapter.conf.management_score:
 39            abort(403)
 40
 41        padding = current_app.config["padding"]
 42        players = g.cfg.member.lists
 43        m = adapter.parser()
 44
 45        def score_table() -> str:
 46            df = formatter.df_rename(
 47                pd.read_sql(
 48                    sql="""
 49                select
 50                    '<input type="radio" name="ts" value="' || ts || '">' as '#',
 51                    playtime,
 52                    p1_name, p1_str,
 53                    p2_name, p2_str,
 54                    p3_name, p3_str,
 55                    p4_name, p4_str,
 56                    comment, source
 57                from
 58                    result
 59                order by
 60                    ts desc
 61                limit 0, 10
 62                ;
 63                """,
 64                    con=dbutil.connection(g.cfg.setting.database_file),
 65                ),
 66                options=StyleOptions(),
 67            )
 68
 69            if not isinstance(df.columns, pd.MultiIndex):
 70                new_columns = [tuple(col.split(" ")) if " " in col else ("", col) for col in df.columns]
 71                df.columns = pd.MultiIndex.from_tuples(new_columns, names=["座席", "項目"])
 72
 73            return adapter.functions.to_styled_html(df, padding)
 74
 75        data: dict[str, Any] = asdict(adapter.conf)
 76        data.update(players=players)
 77
 78        if request.method == "POST":
 79            data.update(request.form.to_dict())
 80            data.update(mode="update")
 81            data.update(g.cfg.setting.to_dict())
 82
 83            if "ts" in data:
 84                match request.form.get("action"):
 85                    case "modify":
 86                        sql = "select * from result where ts = :ts;"
 87                        df = pd.read_sql(sql=sql, con=dbutil.connection(g.cfg.setting.database_file), params=data)
 88                        data.update(next(iter(df.T.to_dict().values())))
 89                        return render_template("score_input.html", **data)
 90                    case "delete":
 91                        m.data.event_ts = request.form.get("ts", "")
 92                        modify.db_delete(m)
 93                        data.update(table=score_table())
 94                        return render_template("score_list.html", **data)
 95                    case "update":
 96                        g.params.unregistered_replace = False
 97                        data.update(request.form.to_dict(), players=players)
 98                        if p1_name := request.form.get("p1_other"):
 99                            data.update(p1_name=formatter.name_replace(p1_name))
100                        if p2_name := request.form.get("p2_other"):
101                            data.update(p2_name=formatter.name_replace(p2_name))
102                        if p3_name := request.form.get("p3_other"):
103                            data.update(p3_name=formatter.name_replace(p3_name))
104                        if p4_name := request.form.get("p4_other"):
105                            data.update(p4_name=formatter.name_replace(p4_name))
106                        if not request.form.get("comment"):
107                            data.update(comment=None)
108
109                        detection = GameResult(**data)
110                        detection.source = "web"
111                        m.status.source = "web"
112
113                        if data.get("mode") == "insert":
114                            modify.db_insert(detection, m)
115                        else:
116                            modify.db_update(detection, m)
117
118                        data.update(table=score_table())
119                        return render_template("score_list.html", **data)
120            elif request.form.get("action") == "modify":  # 新規登録
121                playtime = ExtDt()
122                data.update(
123                    mode="insert",
124                    playtime=playtime.format(ExtDt.FMT.SQL),
125                    ts=playtime.format(ExtDt.FMT.TS),
126                )
127                return render_template("score_input.html", **data)
128
129        data.update(table=score_table())
130        return render_template("score_list.html", **data)
131
132    return bp

スコア管理ページ用Blueprint

Arguments:
  • adapter (ServiceAdapter): web用アダプタ
Returns:

Blueprint: Blueprint

def summary_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
21def summary_bp(adapter: "ServiceAdapter") -> Blueprint:
22    """
23    成績サマリページ用Blueprint
24
25    Args:
26        adapter (ServiceAdapter): web用アダプタ
27
28    Returns:
29        Blueprint: Blueprint
30
31    """
32    bp = Blueprint("summary", __name__, url_prefix="/summary")
33
34    @bp.route("/", methods=["GET", "POST"])
35    def summary() -> "Response":
36        if not adapter.conf.view_summary:
37            abort(403)
38
39        padding = current_app.config["padding"]
40
41        m = adapter.parser()
42        cookie_data = adapter.functions.get_cookie(request)
43        text = " ".join(cookie_data.values())
44        m.data.text = f"{g.cfg.results.commandword[0]} {text}"
45        libs.dispatcher.by_keyword(m)
46
47        _, message = adapter.functions.header_message(m)
48
49        for data, options in m.post.message:
50            if options.title:
51                message += f"<h2>{options.title}</h2>\n"
52
53            if isinstance(data, pd.DataFrame):
54                show_index = options.show_index
55                if options.title == "戦績" and g.params.verbose:
56                    padding = "0.25em 0.75em"
57                    data = _conv_verbose(data)
58
59                message += adapter.functions.to_styled_html(data, padding, show_index)
60
61            if isinstance(data, str):
62                message += adapter.functions.to_text_html(data)
63
64        cookie_data.update(body=message, **asdict(adapter.conf))
65        page = adapter.functions.set_cookie("summary.html", request, cookie_data)
66
67        return page
68
69    return bp

成績サマリページ用Blueprint

Arguments:
  • adapter (ServiceAdapter): web用アダプタ
Returns:

Blueprint: Blueprint

def user_assets_bp( adapter: integrations.web.adapter.ServiceAdapter) -> flask.blueprints.Blueprint:
17def user_assets_bp(adapter: "ServiceAdapter") -> Blueprint:
18    """
19    ユーザー指定CSS用Blueprint
20
21    Args:
22        adapter (ServiceAdapter): web用アダプタ
23
24    Returns:
25        Blueprint: Blueprint
26
27    """
28    bp = Blueprint(
29        "user_assets",
30        __name__,
31        static_folder=os.path.dirname(os.path.join(g.cfg.config_dir, adapter.conf.custom_css)),
32        static_url_path="/user_static",
33    )
34
35    @bp.before_request
36    def restrict_static() -> None:
37        if not os.path.basename(request.path) == adapter.conf.custom_css:
38            abort(403)
39
40    return bp

ユーザー指定CSS用Blueprint

Arguments:
  • adapter (ServiceAdapter): web用アダプタ
Returns:

Blueprint: Blueprint