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
class SvcFunctions(integrations.base.interface.FunctionsInterface):
 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: 返還後

def header_message(self, m: integrations.protocols.MessageParserProtocol) -> tuple[str, 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

def post_processing(self, m: integrations.protocols.MessageParserProtocol) -> None:
242    def post_processing(self, m: "MessageParserProtocol") -> None:
243        """Abstractmethod dummy"""
244        _ = m

Abstractmethod dummy