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]
19def detail_bp(adapter: "ServiceAdapter") -> Blueprint: 20 """個人成績詳細ページ用Blueprint 21 22 Args: 23 adapter (ServiceAdapter): web用アダプタ 24 25 Returns: 26 Blueprint: Blueprint 27 """ 28 29 bp = Blueprint("detail", __name__, url_prefix="/detail") 30 31 @bp.route("/", methods=["GET", "POST"]) 32 def detail(): 33 if not adapter.conf.view_summary: 34 abort(403) 35 36 padding = current_app.config["padding"] 37 players = current_app.config["players"] 38 39 m = adapter.parser() 40 cookie_data = adapter.functions.get_cookie(request) 41 text = " ".join(cookie_data.values()) 42 m.data.text = f"{g.cfg.results.commandword[0]} {text}" 43 libs.dispatcher.by_keyword(m) 44 45 message = adapter.functions.header_message(m) 46 47 for data in m.post.message: 48 for k, v in data.items(): 49 msg = v.get("data") 50 51 if not k.isnumeric() and k: 52 message += f"<h2>{k}</h2>\n" 53 54 if isinstance(msg, pd.DataFrame): 55 show_index = v["options"].show_index 56 if k == "戦績" and g.params.get("verbose"): 57 padding = "0.25em 0.75em" 58 msg = _conv_verbose(msg) 59 message += adapter.functions.to_styled_html(msg, 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(msg, str): 63 message += adapter.functions.to_text_html(msg) 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
20def graph_bp(adapter: "ServiceAdapter") -> Blueprint: 21 """グラフ表示ページ用Blueprint 22 23 Args: 24 adapter (ServiceAdapter): web用アダプタ 25 26 Returns: 27 Blueprint: Blueprint 28 """ 29 30 bp = Blueprint("graph", __name__, url_prefix="/graph") 31 32 @bp.route("/", methods=["GET", "POST"]) 33 def graph(): 34 if not adapter.conf.view_graph: 35 abort(403) 36 37 padding = current_app.config["padding"] 38 39 m = adapter.parser() 40 cookie_data = adapter.functions.get_cookie(request) 41 text = " ".join(cookie_data.values()) 42 m.data.text = f"{g.cfg.graph.commandword[0]} {text}" 43 libs.dispatcher.by_keyword(m) 44 45 message = adapter.functions.header_message(m) 46 47 for data in m.post.message: 48 for k, v in data.items(): 49 msg = v.get("data") 50 51 if isinstance(msg, PosixPath) and msg.exists(): 52 message += f"<p>\n{msg.read_text(encoding="utf-8")}\n</p>\n" 53 54 if isinstance(msg, pd.DataFrame) and k == "素点情報": 55 show_index = v["options"].show_index 56 msg["ゲーム数"] = msg["ゲーム数"].astype("float") 57 msg.rename(columns={"平均値(x)": "平均値", "中央値(|)": "中央値"}, inplace=True) 58 message += f"<h2>{k}</h2>\n" 59 message += adapter.functions.to_styled_html(msg, padding, show_index) 60 61 if isinstance(msg, pd.DataFrame) and k == "順位/ポイント情報": 62 show_index = v["options"].show_index 63 msg["ゲーム数"] = msg["ゲーム数"].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 msg.columns = pd.MultiIndex.from_tuples(multi) 80 message += f"<h2>{k}</h2>\n" 81 message += adapter.functions.to_styled_html(msg, 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
15def index_bp(adapter: "ServiceAdapter") -> Blueprint : 16 """index用Blueprint 17 18 Args: 19 adapter (ServiceAdapter): web用アダプタ 20 21 Returns: 22 Blueprint: Blueprint 23 """ 24 25 bp = Blueprint("index", __name__, url_prefix="/") 26 27 @bp.route("/", methods=["GET", "POST"]) 28 def index(): 29 return render_template("index.html", **asdict(adapter.conf)) 30 31 return bp
index用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
18def member_bp(adapter: "ServiceAdapter") -> Blueprint: 19 """メンバー管理ページ用Blueprint 20 21 Args: 22 adapter (ServiceAdapter): web用アダプタ 23 24 Returns: 25 Blueprint: Blueprint 26 """ 27 28 bp = Blueprint("member", __name__, url_prefix="/member") 29 30 @bp.route("/", methods=["GET", "POST"]) 31 def mgt_member(): 32 if not adapter.conf.management_member: 33 abort(403) 34 35 padding = current_app.config["padding"] 36 data: dict = asdict(adapter.conf) 37 38 if request.method == "POST": 39 match request.form.get("action"): 40 case "add_member": 41 if (name := request.form.get("member", "").strip()): 42 ret = member.append(name.split()[0:2]) 43 data.update(result_msg=ret) 44 case "del_member": 45 if (name := request.form.get("member", "").strip()): 46 ret = member.remove(name.split()[0:2]) 47 data.update(result_msg=ret) 48 case "add_team": 49 if (team_name := request.form.get("team", "").strip()): 50 ret = team.append(team_name.split()[0:2]) 51 data.update(result_msg=ret) 52 case "del_team": 53 if (team_name := request.form.get("team", "").strip()): 54 ret = team.remove(team_name.split()[0:2]) 55 data.update(result_msg=ret) 56 case "delete_all_team": 57 ret = team.clear() 58 data.update(result_msg=ret) 59 60 member_df = loader.read_data("MEMBER_INFO") 61 if member_df.empty: 62 data.update(member_table="<p>登録済みメンバーはいません。</p>") 63 else: 64 data.update(member_table=adapter.functions.to_styled_html(member_df, padding)) 65 66 team_df = loader.read_data("TEAM_INFO") 67 if team_df.empty: 68 data.update(team_table="<p>登録済みチームはありません。</p>") 69 else: 70 data.update(team_table=adapter.functions.to_styled_html(team_df, padding)) 71 72 return render_template("registry.html", **data) 73 74 return bp
メンバー管理ページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
19def ranking_bp(adapter: "ServiceAdapter") -> Blueprint: 20 """ランキングページ用Blueprint 21 22 Args: 23 adapter (ServiceAdapter): web用アダプタ 24 25 Returns: 26 Blueprint: Blueprint 27 """ 28 29 bp = Blueprint("ranking", __name__, url_prefix="/ranking") 30 31 @bp.route("/", methods=["GET", "POST"]) 32 def ranking(): 33 if not adapter.conf.view_ranking: 34 abort(403) 35 36 padding = current_app.config["padding"] 37 38 m = adapter.parser() 39 cookie_data = adapter.functions.get_cookie(request) 40 text = " ".join(cookie_data.values()) 41 m.data.text = f"{g.cfg.ranking.commandword[0]} {text}" 42 libs.dispatcher.by_keyword(m) 43 44 message = adapter.functions.header_message(m) 45 46 for data in m.post.message: 47 for k, v in data.items(): 48 msg = v.get("data") 49 50 if not k.isnumeric() and k: 51 message += f"<h2>{k}</h2>\n" 52 53 if isinstance(msg, pd.DataFrame): 54 show_index = v["options"].show_index 55 message += adapter.functions.to_styled_html(msg, padding, show_index) 56 57 if isinstance(msg, str): 58 message += adapter.functions.to_text_html(msg) 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
19def report_bp(adapter: "ServiceAdapter") -> Blueprint: 20 """レポートページ用Blueprint 21 22 Args: 23 adapter (ServiceAdapter): web用アダプタ 24 25 Returns: 26 Blueprint: Blueprint 27 """ 28 29 bp = Blueprint("report", __name__, url_prefix="/report") 30 31 @bp.route("/", methods=["GET", "POST"]) 32 def report(): 33 if not adapter.conf.view_report: 34 abort(403) 35 36 padding = current_app.config["padding"] 37 38 m = adapter.parser() 39 cookie_data = adapter.functions.get_cookie(request) 40 text = " ".join(cookie_data.values()) 41 m.data.text = f"{g.cfg.report.commandword[0]} {text}" 42 libs.dispatcher.by_keyword(m) 43 44 message = adapter.functions.header_message(m) 45 46 for data in m.post.message: 47 for k, v in data.items(): 48 msg = v.get("data") 49 50 if not k.isnumeric() and k: 51 message += f"<h2>{k}</h2>\n" 52 53 if isinstance(msg, pd.DataFrame): 54 show_index = v["options"].show_index 55 if {"個人成績一覧", "チーム成績一覧"} & set(m.post.headline): 56 check_column = msg.columns.to_list() 57 multi = [ 58 ("", "名前" if g.params.get("individual", True) 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 msg.columns = pd.MultiIndex.from_tuples([x for x in multi if x is not None]) 77 elif "成績上位者" in m.post.headline.keys(): 78 name = "名前" if g.params.get("individual", True) else "チーム" 79 check_column = msg.columns.to_list() 80 multi = [ 81 ("", "集計月"), 82 ("1位", name), ("1位", "獲得ポイント"), 83 ("2位", name), ("2位", "獲得ポイント"), 84 ("3位", name), ("3位", "獲得ポイント"), 85 ("4位", name), ("4位", "獲得ポイント"), 86 ("5位", name), ("5位", "獲得ポイント"), 87 ] 88 msg.columns = pd.MultiIndex.from_tuples([x for x in multi if x is not None]) 89 message += adapter.functions.to_styled_html(msg, padding, show_index) 90 91 if isinstance(msg, str): 92 message += adapter.functions.to_text_html(msg) 93 94 cookie_data.update(body=message, **asdict(adapter.conf)) 95 page = adapter.functions.set_cookie("report.html", request, cookie_data) 96 97 return page 98 99 return bp
レポートページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
22def score_bp(adapter: "ServiceAdapter") -> Blueprint: 23 """スコア管理ページ用Blueprint 24 25 Args: 26 adapter (ServiceAdapter): web用アダプタ 27 28 Returns: 29 Blueprint: Blueprint 30 """ 31 32 bp = Blueprint("score", __name__, url_prefix="/score") 33 34 @bp.route("/", methods=["GET", "POST"]) 35 def mgt_score(): 36 if not adapter.conf.management_score: 37 abort(403) 38 39 padding = current_app.config["padding"] 40 players = current_app.config["players"] 41 m = adapter.parser() 42 43 def score_table() -> str: 44 df = formatter.df_rename(pd.read_sql( 45 sql=""" 46 select 47 '<input type="radio" name="ts" value="' || ts || '">' as '#', 48 playtime, 49 p1_name, p1_str, 50 p2_name, p2_str, 51 p3_name, p3_str, 52 p4_name, p4_str, 53 comment 54 from 55 result 56 order by 57 ts desc 58 limit 0, 10 59 ; 60 """, 61 con=dbutil.connection(g.cfg.setting.database_file) 62 )) 63 64 if not isinstance(df.columns, pd.MultiIndex): 65 new_columns = [tuple(col.split(" ")) if " " in col else ("", col) for col in df.columns] 66 df.columns = pd.MultiIndex.from_tuples(new_columns, names=["座席", "項目"]) 67 68 return adapter.functions.to_styled_html(df, padding) 69 70 data: dict = asdict(adapter.conf) 71 data.update(players=players) 72 73 if request.method == "POST": 74 data.update(request.form.to_dict()) 75 data.update(mode="update") 76 data.update(g.cfg.mahjong.to_dict()) 77 78 if "ts" in data: 79 match request.form.get("action"): 80 case "modify": 81 sql = "select * from result where ts = :ts;" 82 df = pd.read_sql(sql=sql, con=dbutil.connection(g.cfg.setting.database_file), params=data) 83 data.update(next(iter(df.T.to_dict().values()))) 84 return render_template("score_input.html", **data) 85 case "delete": 86 m.data.event_ts = request.form.get("ts", "") 87 modify.db_delete(m) 88 data.update(table=score_table()) 89 return render_template("score_list.html", **data) 90 case "update": 91 g.params.update(unregistered_replace=False) 92 data.update(request.form.to_dict(), players=players) 93 if (p1_name := request.form.get("p1_other")): 94 data.update(p1_name=formatter.name_replace(p1_name)) 95 if (p2_name := request.form.get("p2_other")): 96 data.update(p2_name=formatter.name_replace(p2_name)) 97 if (p3_name := request.form.get("p3_other")): 98 data.update(p3_name=formatter.name_replace(p3_name)) 99 if (p4_name := request.form.get("p4_other")): 100 data.update(p4_name=formatter.name_replace(p4_name)) 101 if not request.form.get("comment"): 102 data.update(comment=None) 103 104 detection = GameResult(**data) 105 m.status.source = "web" 106 if data.get("mode") == "insert": 107 modify.db_insert(detection, m) 108 else: 109 modify.db_update(detection, m) 110 111 data.update(table=score_table()) 112 return render_template("score_list.html", **data) 113 elif request.form.get("action") == "modify": # 新規登録 114 playtime = ExtDT() 115 data.update(mode="insert", playtime=playtime.format(fmt="sql"), ts=playtime.format(fmt="ts")) 116 return render_template("score_input.html", **data) 117 118 data.update(table=score_table()) 119 return render_template("score_list.html", **data) 120 121 return bp
スコア管理ページ用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint
19def summary_bp(adapter: "ServiceAdapter") -> Blueprint: 20 """成績サマリページ用Blueprint 21 22 Args: 23 adapter (ServiceAdapter): web用アダプタ 24 25 Returns: 26 Blueprint: Blueprint 27 """ 28 29 bp = Blueprint("summary", __name__, url_prefix="/summary") 30 31 @bp.route("/", methods=["GET", "POST"]) 32 def summary(): 33 if not adapter.conf.view_summary: 34 abort(403) 35 36 padding = current_app.config["padding"] 37 38 m = adapter.parser() 39 cookie_data = adapter.functions.get_cookie(request) 40 text = " ".join(cookie_data.values()) 41 m.data.text = f"{g.cfg.results.commandword[0]} {text}" 42 libs.dispatcher.by_keyword(m) 43 44 message = adapter.functions.header_message(m) 45 46 for data in m.post.message: 47 for k, v in data.items(): 48 msg = v.get("data") 49 50 if not k.isnumeric() and k: 51 message += f"<h2>{k}</h2>\n" 52 53 if isinstance(msg, pd.DataFrame): 54 show_index = v["options"].show_index 55 if k == "戦績" and g.params.get("verbose"): 56 padding = "0.25em 0.75em" 57 msg = _conv_verbose(msg) 58 59 message += adapter.functions.to_styled_html(msg, padding, show_index) 60 61 if isinstance(msg, str): 62 message += adapter.functions.to_text_html(msg) 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 """ユーザー指定CSS用Blueprint 19 20 Args: 21 adapter (ServiceAdapter): web用アダプタ 22 23 Returns: 24 Blueprint: Blueprint 25 """ 26 27 bp = Blueprint( 28 "user_assets", 29 __name__, 30 static_folder=os.path.dirname(os.path.join(g.cfg.config_dir, adapter.conf.custom_css)), 31 static_url_path="/user_static" 32 ) 33 34 @bp.before_request 35 def restrict_static(): 36 if not os.path.basename(request.path) == adapter.conf.custom_css: 37 abort(403) 38 39 return bp
ユーザー指定CSS用Blueprint
Arguments:
- adapter (ServiceAdapter): web用アダプタ
Returns:
Blueprint: Blueprint