integrations.web.functions
integrations/web/functions.py
1""" 2integrations/web/functions.py 3""" 4 5import re 6from typing import TYPE_CHECKING, Any 7 8from flask import make_response, render_template 9 10from integrations.base.interface import FunctionsInterface 11from libs.types import StyleOptions 12from libs.utils import formatter 13 14if TYPE_CHECKING: 15 import pandas as pd 16 from flask import Request, Response 17 18 from integrations.protocols import MessageParserProtocol 19 20 21class SvcFunctions(FunctionsInterface): 22 """WebUI専用関数""" 23 24 def to_styled_html(self, df: "pd.DataFrame", padding: str, index: bool = False) -> str: 25 """ 26 データフレームをHTML表に変換 27 28 Args: 29 df (pd.DataFrame): 変換元データ 30 padding (str): パディング 31 index (bool): インデックスの表示 32 33 Returns: 34 str: HTML表 35 36 """ 37 df = formatter.df_rename(df, StyleOptions(rename_type=StyleOptions.RenameType.NORMAL)) 38 df = df.rename(columns={"name": "プレイヤー名", "point": "ポイント", "rank": "順位"}) 39 styled = ( 40 df.style.format( 41 { 42 "通算": "{:+.1f}pt", 43 "ポイント": "{:+.1f}pt", 44 "獲得ポイント": "{:+.1f}pt", 45 ("東家", "ポイント"): "{:+.1f}pt", 46 ("南家", "ポイント"): "{:+.1f}pt", 47 ("西家", "ポイント"): "{:+.1f}pt", 48 ("北家", "ポイント"): "{:+.1f}pt", 49 "順位": "{:.0f}位", 50 "平均": "{:+.1f}pt", 51 "順位差": "{:.1f}pt", 52 "トップ差": "{:.1f}pt", 53 "ポイント合計": "{:.1f}pt", 54 "ゲーム参加率": "{}", 55 "通算ポイント": "{}", 56 "平均ポイント": "{}", 57 "最大獲得ポイント": "{}", 58 "平均収支": "{}", 59 "平均素点": "{}", 60 "平均順位": "{}", 61 "1位率": "{}", 62 "連対率": "{}", 63 "ラス回避率": "{}", 64 "トビ率": "{}", 65 "役満和了率": "{}", 66 "レート": "{:.1f}", 67 "順位偏差": "{:.0f}", 68 "得点偏差": "{:.0f}", 69 "経過日数": "{:.0f} 日", 70 "プレイ回数": "{:.0f} ゲーム", 71 # レポート 72 ("ポイント", "通算"): "{:+.1f}pt", 73 ("ポイント", "平均"): "{:+.1f}pt", 74 ("1位", "獲得率"): "{:.2%}", 75 ("2位", "獲得率"): "{:.2%}", 76 ("3位", "獲得率"): "{:.2%}", 77 ("4位", "獲得率"): "{:.2%}", 78 ("トビ", "率"): "{:.2%}", 79 ("役満", "和了率"): "{:.2%}", 80 ("1位", "獲得ポイント"): "{:+.1f}pt", 81 ("2位", "獲得ポイント"): "{:+.1f}pt", 82 ("3位", "獲得ポイント"): "{:+.1f}pt", 83 ("4位", "獲得ポイント"): "{:+.1f}pt", 84 ("5位", "獲得ポイント"): "{:+.1f}pt", 85 # 成績統計 86 "ゲーム数": "{:.0f}", 87 ("", "ゲーム数"): "{:.0f}", 88 ("1位", "獲得数"): "{:.0f}", 89 ("2位", "獲得数"): "{:.0f}", 90 ("3位", "獲得数"): "{:.0f}", 91 ("4位", "獲得数"): "{:.0f}", 92 ("", "平均順位"): "{:.2f}", 93 ("区間成績", "区間ポイント"): "{:+.1f}pt", 94 ("区間成績", "区間平均"): "{:+.1f}pt", 95 ("", "通算ポイント"): "{:+.1f}pt", 96 }, 97 na_rep="-----", 98 ) 99 .set_table_attributes('class="data_table"') 100 .set_table_styles( 101 [ 102 { 103 "selector": "th", 104 "props": [ 105 ("color", "#ffffff"), 106 ("background-color", "#000000"), 107 ("text-align", "center"), 108 ("padding", padding), 109 ], 110 }, 111 {"selector": "td", "props": [("text-align", "center"), ("padding", padding)]}, 112 {"selector": "tr:nth-child(odd)", "props": [("background-color", "#f0f0f0f0")]}, 113 {"selector": "tr:nth-child(even)", "props": [("background-color", "#dfdfdfdf")]}, 114 ] 115 ) 116 ) 117 if not index: 118 styled = styled.hide(axis="index") 119 120 ret = styled.to_html() 121 ret = re.sub(r" >-(\d+)</td>", r" >▲\1</td>", ret) # 素点 122 ret = re.sub(r" >-(\d+\.\d)(点?)</td>", r" >▲\1\2</td>", ret) # 素点(小数点付き) 123 ret = re.sub(r" >-(\d+\.\d)pt</td>", r" >▲\1pt</td>", ret) # ポイント 124 ret = re.sub(r" >(\d\.\d\d)0000</td>", r" >\1</td>", ret) # 縦持ち平均順位 125 126 return ret 127 128 def to_text_html(self, text: str) -> str: 129 """ 130 テキストをHTMLに変換 131 132 Args: 133 text (str): 変換元 134 135 Returns: 136 str: 返還後 137 138 """ 139 ret: str = "<p>\n" 140 for line in text.splitlines(): 141 ret += f"{line.strip()}<br>\n" 142 ret += "</p>\n" 143 144 return ret 145 146 def header_message(self, m: "MessageParserProtocol") -> tuple[str, str]: 147 """ 148 ヘッダ情報取得 149 150 Args: 151 m (MessageParserProtocol): メッセージデータ 152 153 Returns: 154 tuple[str, str]: 取得文字列 155 156 """ 157 message = "" 158 title = "" 159 160 if m.post.headline: 161 header_data, header_option = m.post.headline 162 if title := header_option.title: 163 message = f"<h1>{title}</h1>\n" 164 if isinstance(header_data, str): 165 message += f"<p>\n{header_data.replace('\n', '<br>\n')}</p>\n" 166 167 return title, message 168 169 def set_cookie(self, html: str, req: "Request", data: dict[str, Any]) -> "Response": 170 """ 171 cookie保存 172 173 Args: 174 html (str): テンプレートHTML 175 req (Request): Request 176 data (dict[str, Any]): データ 177 178 Returns: 179 Response: Response 180 181 """ 182 page = make_response(render_template(html, **data)) 183 if req.method == "POST": 184 if req.form.get("action") == "reset": # cookie削除 185 for k in req.cookies.keys(): 186 page.delete_cookie(k, path=req.path) 187 else: 188 for k, v in req.form.to_dict().items(): 189 if k == "action": 190 continue 191 page.set_cookie(k, v, path=req.path) 192 193 return page 194 195 def get_cookie(self, req: "Request") -> dict[str, str]: 196 """ 197 cookie取得 198 199 Args: 200 req (Request): Request 201 202 Returns: 203 dict[str, str]: cookieデータ 204 205 """ 206 initial_value: dict[str, str] = { 207 "range": "", 208 "guest": "ゲストなし", 209 "display": "", 210 "result": "", 211 "collect": "", 212 } 213 214 target_keys: list[str] = [ 215 "collect", 216 "display", 217 "guest", 218 "player", 219 "range", 220 "result", 221 "text", 222 ] 223 224 if req.method == "POST": 225 cookie_data = req.form.to_dict() 226 if req.form.get("action") == "reset": 227 cookie_data = initial_value 228 else: 229 cookie_data.pop("action") 230 else: 231 cookie_data = initial_value 232 cookie_data.update(req.cookies) 233 234 return {k: v for k, v in cookie_data.items() if k in target_keys} 235 236 def get_conversations(self, m: "MessageParserProtocol") -> dict[str, Any]: 237 """Abstractmethod dummy""" 238 _ = m 239 return {} 240 241 def post_processing(self, m: "MessageParserProtocol") -> None: 242 """Abstractmethod dummy""" 243 _ = m
22class SvcFunctions(FunctionsInterface): 23 """WebUI専用関数""" 24 25 def to_styled_html(self, df: "pd.DataFrame", padding: str, index: bool = False) -> str: 26 """ 27 データフレームをHTML表に変換 28 29 Args: 30 df (pd.DataFrame): 変換元データ 31 padding (str): パディング 32 index (bool): インデックスの表示 33 34 Returns: 35 str: HTML表 36 37 """ 38 df = formatter.df_rename(df, StyleOptions(rename_type=StyleOptions.RenameType.NORMAL)) 39 df = df.rename(columns={"name": "プレイヤー名", "point": "ポイント", "rank": "順位"}) 40 styled = ( 41 df.style.format( 42 { 43 "通算": "{:+.1f}pt", 44 "ポイント": "{:+.1f}pt", 45 "獲得ポイント": "{:+.1f}pt", 46 ("東家", "ポイント"): "{:+.1f}pt", 47 ("南家", "ポイント"): "{:+.1f}pt", 48 ("西家", "ポイント"): "{:+.1f}pt", 49 ("北家", "ポイント"): "{:+.1f}pt", 50 "順位": "{:.0f}位", 51 "平均": "{:+.1f}pt", 52 "順位差": "{:.1f}pt", 53 "トップ差": "{:.1f}pt", 54 "ポイント合計": "{:.1f}pt", 55 "ゲーム参加率": "{}", 56 "通算ポイント": "{}", 57 "平均ポイント": "{}", 58 "最大獲得ポイント": "{}", 59 "平均収支": "{}", 60 "平均素点": "{}", 61 "平均順位": "{}", 62 "1位率": "{}", 63 "連対率": "{}", 64 "ラス回避率": "{}", 65 "トビ率": "{}", 66 "役満和了率": "{}", 67 "レート": "{:.1f}", 68 "順位偏差": "{:.0f}", 69 "得点偏差": "{:.0f}", 70 "経過日数": "{:.0f} 日", 71 "プレイ回数": "{:.0f} ゲーム", 72 # レポート 73 ("ポイント", "通算"): "{:+.1f}pt", 74 ("ポイント", "平均"): "{:+.1f}pt", 75 ("1位", "獲得率"): "{:.2%}", 76 ("2位", "獲得率"): "{:.2%}", 77 ("3位", "獲得率"): "{:.2%}", 78 ("4位", "獲得率"): "{:.2%}", 79 ("トビ", "率"): "{:.2%}", 80 ("役満", "和了率"): "{:.2%}", 81 ("1位", "獲得ポイント"): "{:+.1f}pt", 82 ("2位", "獲得ポイント"): "{:+.1f}pt", 83 ("3位", "獲得ポイント"): "{:+.1f}pt", 84 ("4位", "獲得ポイント"): "{:+.1f}pt", 85 ("5位", "獲得ポイント"): "{:+.1f}pt", 86 # 成績統計 87 "ゲーム数": "{:.0f}", 88 ("", "ゲーム数"): "{:.0f}", 89 ("1位", "獲得数"): "{:.0f}", 90 ("2位", "獲得数"): "{:.0f}", 91 ("3位", "獲得数"): "{:.0f}", 92 ("4位", "獲得数"): "{:.0f}", 93 ("", "平均順位"): "{:.2f}", 94 ("区間成績", "区間ポイント"): "{:+.1f}pt", 95 ("区間成績", "区間平均"): "{:+.1f}pt", 96 ("", "通算ポイント"): "{:+.1f}pt", 97 }, 98 na_rep="-----", 99 ) 100 .set_table_attributes('class="data_table"') 101 .set_table_styles( 102 [ 103 { 104 "selector": "th", 105 "props": [ 106 ("color", "#ffffff"), 107 ("background-color", "#000000"), 108 ("text-align", "center"), 109 ("padding", padding), 110 ], 111 }, 112 {"selector": "td", "props": [("text-align", "center"), ("padding", padding)]}, 113 {"selector": "tr:nth-child(odd)", "props": [("background-color", "#f0f0f0f0")]}, 114 {"selector": "tr:nth-child(even)", "props": [("background-color", "#dfdfdfdf")]}, 115 ] 116 ) 117 ) 118 if not index: 119 styled = styled.hide(axis="index") 120 121 ret = styled.to_html() 122 ret = re.sub(r" >-(\d+)</td>", r" >▲\1</td>", ret) # 素点 123 ret = re.sub(r" >-(\d+\.\d)(点?)</td>", r" >▲\1\2</td>", ret) # 素点(小数点付き) 124 ret = re.sub(r" >-(\d+\.\d)pt</td>", r" >▲\1pt</td>", ret) # ポイント 125 ret = re.sub(r" >(\d\.\d\d)0000</td>", r" >\1</td>", ret) # 縦持ち平均順位 126 127 return ret 128 129 def to_text_html(self, text: str) -> str: 130 """ 131 テキストをHTMLに変換 132 133 Args: 134 text (str): 変換元 135 136 Returns: 137 str: 返還後 138 139 """ 140 ret: str = "<p>\n" 141 for line in text.splitlines(): 142 ret += f"{line.strip()}<br>\n" 143 ret += "</p>\n" 144 145 return ret 146 147 def header_message(self, m: "MessageParserProtocol") -> tuple[str, str]: 148 """ 149 ヘッダ情報取得 150 151 Args: 152 m (MessageParserProtocol): メッセージデータ 153 154 Returns: 155 tuple[str, str]: 取得文字列 156 157 """ 158 message = "" 159 title = "" 160 161 if m.post.headline: 162 header_data, header_option = m.post.headline 163 if title := header_option.title: 164 message = f"<h1>{title}</h1>\n" 165 if isinstance(header_data, str): 166 message += f"<p>\n{header_data.replace('\n', '<br>\n')}</p>\n" 167 168 return title, message 169 170 def set_cookie(self, html: str, req: "Request", data: dict[str, Any]) -> "Response": 171 """ 172 cookie保存 173 174 Args: 175 html (str): テンプレートHTML 176 req (Request): Request 177 data (dict[str, Any]): データ 178 179 Returns: 180 Response: Response 181 182 """ 183 page = make_response(render_template(html, **data)) 184 if req.method == "POST": 185 if req.form.get("action") == "reset": # cookie削除 186 for k in req.cookies.keys(): 187 page.delete_cookie(k, path=req.path) 188 else: 189 for k, v in req.form.to_dict().items(): 190 if k == "action": 191 continue 192 page.set_cookie(k, v, path=req.path) 193 194 return page 195 196 def get_cookie(self, req: "Request") -> dict[str, str]: 197 """ 198 cookie取得 199 200 Args: 201 req (Request): Request 202 203 Returns: 204 dict[str, str]: cookieデータ 205 206 """ 207 initial_value: dict[str, str] = { 208 "range": "", 209 "guest": "ゲストなし", 210 "display": "", 211 "result": "", 212 "collect": "", 213 } 214 215 target_keys: list[str] = [ 216 "collect", 217 "display", 218 "guest", 219 "player", 220 "range", 221 "result", 222 "text", 223 ] 224 225 if req.method == "POST": 226 cookie_data = req.form.to_dict() 227 if req.form.get("action") == "reset": 228 cookie_data = initial_value 229 else: 230 cookie_data.pop("action") 231 else: 232 cookie_data = initial_value 233 cookie_data.update(req.cookies) 234 235 return {k: v for k, v in cookie_data.items() if k in target_keys} 236 237 def get_conversations(self, m: "MessageParserProtocol") -> dict[str, Any]: 238 """Abstractmethod dummy""" 239 _ = m 240 return {} 241 242 def post_processing(self, m: "MessageParserProtocol") -> None: 243 """Abstractmethod dummy""" 244 _ = m
WebUI専用関数
def
to_styled_html(self, df: pandas.DataFrame, padding: str, index: bool = False) -> str:
25 def to_styled_html(self, df: "pd.DataFrame", padding: str, index: bool = False) -> str: 26 """ 27 データフレームをHTML表に変換 28 29 Args: 30 df (pd.DataFrame): 変換元データ 31 padding (str): パディング 32 index (bool): インデックスの表示 33 34 Returns: 35 str: HTML表 36 37 """ 38 df = formatter.df_rename(df, StyleOptions(rename_type=StyleOptions.RenameType.NORMAL)) 39 df = df.rename(columns={"name": "プレイヤー名", "point": "ポイント", "rank": "順位"}) 40 styled = ( 41 df.style.format( 42 { 43 "通算": "{:+.1f}pt", 44 "ポイント": "{:+.1f}pt", 45 "獲得ポイント": "{:+.1f}pt", 46 ("東家", "ポイント"): "{:+.1f}pt", 47 ("南家", "ポイント"): "{:+.1f}pt", 48 ("西家", "ポイント"): "{:+.1f}pt", 49 ("北家", "ポイント"): "{:+.1f}pt", 50 "順位": "{:.0f}位", 51 "平均": "{:+.1f}pt", 52 "順位差": "{:.1f}pt", 53 "トップ差": "{:.1f}pt", 54 "ポイント合計": "{:.1f}pt", 55 "ゲーム参加率": "{}", 56 "通算ポイント": "{}", 57 "平均ポイント": "{}", 58 "最大獲得ポイント": "{}", 59 "平均収支": "{}", 60 "平均素点": "{}", 61 "平均順位": "{}", 62 "1位率": "{}", 63 "連対率": "{}", 64 "ラス回避率": "{}", 65 "トビ率": "{}", 66 "役満和了率": "{}", 67 "レート": "{:.1f}", 68 "順位偏差": "{:.0f}", 69 "得点偏差": "{:.0f}", 70 "経過日数": "{:.0f} 日", 71 "プレイ回数": "{:.0f} ゲーム", 72 # レポート 73 ("ポイント", "通算"): "{:+.1f}pt", 74 ("ポイント", "平均"): "{:+.1f}pt", 75 ("1位", "獲得率"): "{:.2%}", 76 ("2位", "獲得率"): "{:.2%}", 77 ("3位", "獲得率"): "{:.2%}", 78 ("4位", "獲得率"): "{:.2%}", 79 ("トビ", "率"): "{:.2%}", 80 ("役満", "和了率"): "{:.2%}", 81 ("1位", "獲得ポイント"): "{:+.1f}pt", 82 ("2位", "獲得ポイント"): "{:+.1f}pt", 83 ("3位", "獲得ポイント"): "{:+.1f}pt", 84 ("4位", "獲得ポイント"): "{:+.1f}pt", 85 ("5位", "獲得ポイント"): "{:+.1f}pt", 86 # 成績統計 87 "ゲーム数": "{:.0f}", 88 ("", "ゲーム数"): "{:.0f}", 89 ("1位", "獲得数"): "{:.0f}", 90 ("2位", "獲得数"): "{:.0f}", 91 ("3位", "獲得数"): "{:.0f}", 92 ("4位", "獲得数"): "{:.0f}", 93 ("", "平均順位"): "{:.2f}", 94 ("区間成績", "区間ポイント"): "{:+.1f}pt", 95 ("区間成績", "区間平均"): "{:+.1f}pt", 96 ("", "通算ポイント"): "{:+.1f}pt", 97 }, 98 na_rep="-----", 99 ) 100 .set_table_attributes('class="data_table"') 101 .set_table_styles( 102 [ 103 { 104 "selector": "th", 105 "props": [ 106 ("color", "#ffffff"), 107 ("background-color", "#000000"), 108 ("text-align", "center"), 109 ("padding", padding), 110 ], 111 }, 112 {"selector": "td", "props": [("text-align", "center"), ("padding", padding)]}, 113 {"selector": "tr:nth-child(odd)", "props": [("background-color", "#f0f0f0f0")]}, 114 {"selector": "tr:nth-child(even)", "props": [("background-color", "#dfdfdfdf")]}, 115 ] 116 ) 117 ) 118 if not index: 119 styled = styled.hide(axis="index") 120 121 ret = styled.to_html() 122 ret = re.sub(r" >-(\d+)</td>", r" >▲\1</td>", ret) # 素点 123 ret = re.sub(r" >-(\d+\.\d)(点?)</td>", r" >▲\1\2</td>", ret) # 素点(小数点付き) 124 ret = re.sub(r" >-(\d+\.\d)pt</td>", r" >▲\1pt</td>", ret) # ポイント 125 ret = re.sub(r" >(\d\.\d\d)0000</td>", r" >\1</td>", ret) # 縦持ち平均順位 126 127 return ret
データフレームをHTML表に変換
Arguments:
- df (pd.DataFrame): 変換元データ
- padding (str): パディング
- index (bool): インデックスの表示
Returns:
str: HTML表
def
to_text_html(self, text: str) -> str:
129 def to_text_html(self, text: str) -> str: 130 """ 131 テキストをHTMLに変換 132 133 Args: 134 text (str): 変換元 135 136 Returns: 137 str: 返還後 138 139 """ 140 ret: str = "<p>\n" 141 for line in text.splitlines(): 142 ret += f"{line.strip()}<br>\n" 143 ret += "</p>\n" 144 145 return ret
テキストをHTMLに変換
Arguments:
- text (str): 変換元
Returns:
str: 返還後
147 def header_message(self, m: "MessageParserProtocol") -> tuple[str, str]: 148 """ 149 ヘッダ情報取得 150 151 Args: 152 m (MessageParserProtocol): メッセージデータ 153 154 Returns: 155 tuple[str, str]: 取得文字列 156 157 """ 158 message = "" 159 title = "" 160 161 if m.post.headline: 162 header_data, header_option = m.post.headline 163 if title := header_option.title: 164 message = f"<h1>{title}</h1>\n" 165 if isinstance(header_data, str): 166 message += f"<p>\n{header_data.replace('\n', '<br>\n')}</p>\n" 167 168 return title, message
ヘッダ情報取得
Arguments:
- m (MessageParserProtocol): メッセージデータ
Returns:
tuple[str, str]: 取得文字列
def
get_conversations( self, m: integrations.protocols.MessageParserProtocol) -> dict[str, typing.Any]:
237 def get_conversations(self, m: "MessageParserProtocol") -> dict[str, Any]: 238 """Abstractmethod dummy""" 239 _ = m 240 return {}
Abstractmethod dummy