X-Git-Url: https://plomlompom.com/repos/?a=blobdiff_plain;f=plomtask%2Fhttp.py;fp=plomtask%2Fhttp.py;h=d602f07b9baf3c18df19fa88435928d2a33845d6;hb=db62e6559fdd577dae38d4b6f5cbd5ef6a14cc57;hp=26c8b719fec481e4106d66073a85844a1d16491a;hpb=a658d31f985ec9a08181005614c3d9876e046274;p=plomtask diff --git a/plomtask/http.py b/plomtask/http.py index 26c8b71..d602f07 100644 --- a/plomtask/http.py +++ b/plomtask/http.py @@ -1,11 +1,12 @@ """Web server stuff.""" from __future__ import annotations from dataclasses import dataclass -from typing import Any, Callable, Mapping +from typing import Any, Callable from base64 import b64encode, b64decode from http.server import BaseHTTPRequestHandler from http.server import HTTPServer from urllib.parse import urlparse, parse_qs +from json import dumps as json_dumps from os.path import split as path_split from jinja2 import Environment as JinjaEnv, FileSystemLoader as JinjaFSLoader from plomtask.dating import date_in_n_days @@ -16,6 +17,7 @@ from plomtask.db import DatabaseConnection, DatabaseFile from plomtask.processes import Process, ProcessStep, ProcessStepsNode from plomtask.conditions import Condition from plomtask.todos import Todo +from plomtask.db import BaseModel TEMPLATES_DIR = 'templates' @@ -27,7 +29,37 @@ class TaskServer(HTTPServer): *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.db = db_file - self.jinja = JinjaEnv(loader=JinjaFSLoader(TEMPLATES_DIR)) + self.headers: list[tuple[str, str]] = [] + self._render_mode = 'html' + self._jinja = JinjaEnv(loader=JinjaFSLoader(TEMPLATES_DIR)) + + def set_json_mode(self) -> None: + """Make server send JSON instead of HTML responses.""" + self._render_mode = 'json' + self.headers += [('Content-Type', 'application/json')] + + @staticmethod + def ctx_to_json(ctx: dict[str, object]) -> str: + """Render ctx into JSON string.""" + def walk_ctx(node: object) -> Any: + if isinstance(node, BaseModel): + return node.as_dict + if isinstance(node, (list, tuple)): + return [walk_ctx(x) for x in node] + if isinstance(node, HandledException): + return str(node) + return node + for k, v in ctx.items(): + ctx[k] = walk_ctx(v) + return json_dumps(ctx) + + def render(self, ctx: dict[str, object], tmpl_name: str = '') -> str: + """Render ctx according to self._render_mode..""" + tmpl_name = f'{tmpl_name}.{self._render_mode}' + if 'html' == self._render_mode: + template = self._jinja.get_template(tmpl_name) + return template.render(ctx) + return self.__class__.ctx_to_json(ctx) class InputsParser: @@ -106,16 +138,18 @@ class TaskHandler(BaseHTTPRequestHandler): _form_data: InputsParser _params: InputsParser - def _send_html(self, + def _send_page(self, + ctx: dict[str, Any], tmpl_name: str, - ctx: Mapping[str, object], - code: int = 200) -> None: + code: int = 200 + ) -> None: """Send HTML as proper HTTP response.""" - tmpl = self.server.jinja.get_template(tmpl_name) - html = tmpl.render(ctx) + body = self.server.render(ctx, tmpl_name) self.send_response(code) + for header_tuple in self.server.headers: + self.send_header(*header_tuple) self.end_headers() - self.wfile.write(bytes(html, 'utf-8')) + self.wfile.write(bytes(body, 'utf-8')) @staticmethod def _request_wrapper(http_method: str, not_found_msg: str @@ -148,7 +182,7 @@ class TaskHandler(BaseHTTPRequestHandler): assert hasattr(cls, 'empty_cache') cls.empty_cache() ctx = {'msg': error} - self._send_html('msg.html', ctx, error.http_code) + self._send_page(ctx, 'msg', error.http_code) finally: self.conn.close() return wrapper @@ -158,11 +192,11 @@ class TaskHandler(BaseHTTPRequestHandler): def do_GET(self, handler: Callable[[], str | dict[str, object]] ) -> str | None: """Render page with result of handler, or redirect if result is str.""" - tmpl_name = f'{self._site}.html' - ctx_or_redir = handler() - if isinstance(ctx_or_redir, str): - return ctx_or_redir - self._send_html(tmpl_name, ctx_or_redir) + tmpl_name = f'{self._site}' + ctx_or_redir_target = handler() + if isinstance(ctx_or_redir_target, str): + return ctx_or_redir_target + self._send_page(ctx_or_redir_target, tmpl_name) return None @_request_wrapper('POST', 'Unknown POST target')