From: Christian Heller Date: Tue, 16 Jul 2024 19:45:04 +0000 (+0200) Subject: Re-organize most page/JSON rendering code from TaskServer into TaskHandler. X-Git-Url: https://plomlompom.com/repos/%7B%7Bdb.prefix%7D%7D/%7B%7B%20web_path%20%7D%7D/%7B%7Bprefix%7D%7D/todo?a=commitdiff_plain;h=a9baed662f671d2d3cdf257e2deee692238a7e55;p=plomtask Re-organize most page/JSON rendering code from TaskServer into TaskHandler. --- diff --git a/plomtask/http.py b/plomtask/http.py index 61bea84..8fa6f93 100644 --- a/plomtask/http.py +++ b/plomtask/http.py @@ -28,82 +28,8 @@ class TaskServer(HTTPServer): *args: Any, **kwargs: Any) -> None: super().__init__(*args, **kwargs) self.db = db_file - 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], conn: DatabaseConnection) -> str: - """Render ctx into JSON string.""" - - def walk_ctx(node: object, references: CtxReferences) -> Any: - if hasattr(node, 'into_reference'): - if hasattr(node, 'id_') and node.id_ is not None: - library_growing[0] = True - return node.into_reference(references) - if hasattr(node, 'as_dict'): - d = node.as_dict - if '_references' in d: - own_refs = d['_references'] - if own_refs.update(references): - library_growing[0] = True - del d['_references'] - return d - if isinstance(node, (list, tuple)): - return [walk_ctx(x, references) for x in node] - if isinstance(node, dict): - d = {} - for k, v in node.items(): - d[k] = walk_ctx(v, references) - return d - if isinstance(node, HandledException): - return str(node) - return node - - models = {} - for cls in [Day, Process, ProcessStep, Condition, Todo]: - models[cls.__name__] = cls - library: dict[str, dict[str | int, object]] = {} - references = CtxReferences({}) - library_growing = [True] - while library_growing[0]: - library_growing[0] = False - for k, v in ctx.items(): - ctx[k] = walk_ctx(v, references) - for cls_name, ids in references.d.items(): - if cls_name not in library: - library[cls_name] = {} - for id_ in ids: - cls = models[cls_name] - assert hasattr(cls, 'can_create_by_id') - if cls.can_create_by_id: - assert hasattr(cls, 'by_id_or_create') - d = cls.by_id_or_create(conn, id_).as_dict - else: - assert hasattr(cls, 'by_id') - d = cls.by_id(conn, id_).as_dict - del d['_references'] - library[cls_name][id_] = d - references.d[cls_name] = [] - ctx['_library'] = library - return json_dumps(ctx) - - def render(self, - ctx: dict[str, object], - tmpl_name: str, - conn: DatabaseConnection - ) -> 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, conn) + self.render_mode = 'html' + self.jinja = JinjaEnv(loader=JinjaFSLoader(TEMPLATES_DIR)) class InputsParser: @@ -213,13 +139,75 @@ class TaskHandler(BaseHTTPRequestHandler): code: int = 200 ) -> None: """Send ctx as proper HTTP response.""" - body = self.server.render(ctx, tmpl_name, self.conn) + body: str + headers: list[tuple[str, str]] = [] + if 'html' == self.server.render_mode: + tmpl = self.server.jinja.get_template(f'{tmpl_name}.html') + body = tmpl.render(ctx) + else: + body = self._ctx_to_json(ctx) + headers += [('Content-Type', 'application/json')] self.send_response(code) - for header_tuple in self.server.headers: + for header_tuple in headers: self.send_header(*header_tuple) self.end_headers() self.wfile.write(bytes(body, 'utf-8')) + def _ctx_to_json(self, ctx: dict[str, object]) -> str: + """Render ctx into JSON string.""" + + def walk_ctx(node: object, references: CtxReferences) -> Any: + if hasattr(node, 'into_reference'): + if hasattr(node, 'id_') and node.id_ is not None: + library_growing[0] = True + return node.into_reference(references) + if hasattr(node, 'as_dict'): + d = node.as_dict + if '_references' in d: + own_refs = d['_references'] + if own_refs.update(references): + library_growing[0] = True + del d['_references'] + return d + if isinstance(node, (list, tuple)): + return [walk_ctx(x, references) for x in node] + if isinstance(node, dict): + d = {} + for k, v in node.items(): + d[k] = walk_ctx(v, references) + return d + if isinstance(node, HandledException): + return str(node) + return node + + models = {} + for cls in [Day, Process, ProcessStep, Condition, Todo]: + models[cls.__name__] = cls + library: dict[str, dict[str | int, object]] = {} + references = CtxReferences({}) + library_growing = [True] + while library_growing[0]: + library_growing[0] = False + for k, v in ctx.items(): + ctx[k] = walk_ctx(v, references) + for cls_name, ids in references.d.items(): + if cls_name not in library: + library[cls_name] = {} + for id_ in ids: + cls = models[cls_name] + assert hasattr(cls, 'can_create_by_id') + if cls.can_create_by_id: + assert hasattr(cls, 'by_id_or_create') + d = cls.by_id_or_create(self.conn, id_).as_dict + else: + assert hasattr(cls, 'by_id') + d = cls.by_id(self.conn, id_).as_dict + del d['_references'] + library[cls_name][id_] = d + references.d[cls_name] = [] + ctx['_library'] = library + return json_dumps(ctx) + @staticmethod def _request_wrapper(http_method: str, not_found_msg: str ) -> Callable[..., Callable[[TaskHandler], None]]: diff --git a/tests/utils.py b/tests/utils.py index 10d6591..b987bc3 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -510,7 +510,7 @@ class TestCaseWithServer(TestCaseWithDB): self.server_thread.start() self.conn = HTTPConnection(str(self.httpd.server_address[0]), self.httpd.server_address[1]) - self.httpd.set_json_mode() + self.httpd.render_mode = 'json' def tearDown(self) -> None: self.httpd.shutdown()