diff --git a/ums/management/db.py b/ums/management/db.py index 99d1813..c205787 100644 --- a/ums/management/db.py +++ b/ums/management/db.py @@ -125,8 +125,8 @@ class DB(): id:str|None=None, sender:str|None=None, recipient:str|None=None, processed:bool|None=None, time_after:int|None=None, time_before:int|None=None, - limit:int=20, offset:int=0 - ) -> Generator[RowObject, None, None]: + limit:int=20, offset:int=0, _count_only:bool=False + ) -> Generator[RowObject|int, None, None]: where = [] params = { @@ -153,11 +153,29 @@ class DB(): where_clause = "" with self.db: - for row in self.db.execute( - "SELECT * FROM Messages {} ORDER BY time DESC LIMIT :lim OFFSET :off".format(where_clause), - params - ): - yield self._create_row_object(row) + if _count_only: + count = self.db.execute( + "SELECT COUNT(*) as count FROM Messages {}".format(where_clause), + params + ).fetchone() + + yield count['count'] + else: + for row in self.db.execute( + "SELECT * FROM Messages {} ORDER BY time DESC LIMIT :lim OFFSET :off".format(where_clause), + params + ): + yield self._create_row_object(row) + + def __len__(self) -> int: + return self.len() + + def len(self, **kwargs) -> int: + """ + See `DB.iterate` for possible values of `kwargs`. + """ + kwargs['_count_only'] = True + return next(self.iterate(**kwargs)) def _create_row_object(self, row:sqlite3.Row) -> RowObject: return RowObject( diff --git a/ums/management/interface.py b/ums/management/interface.py index 506db19..d0c4b40 100644 --- a/ums/management/interface.py +++ b/ums/management/interface.py @@ -8,12 +8,15 @@ # source code released under the terms of GNU Public License Version 3 # https://www.gnu.org/licenses/gpl-3.0.txt +import re + from urllib.parse import urlencode from fastapi import APIRouter, Request from fastapi.responses import HTMLResponse, RedirectResponse from fastapi.templating import Jinja2Templates + from ums.management.db import DB class Interface(): @@ -37,24 +40,57 @@ class Interface(): return RedirectResponse(self._PREFIX + "/table") @self.router.get("/table", response_class=HTMLResponse, summary="Table of messages") - def table(request: Request, limit:int=10, offset:int=0): + def table(request: Request, + id:str|None=None, sender:str|None=None, recipient:str|None=None, + processed:bool|None=None, + time_after:int|str|None=None, time_before:int|str|None=None, + limit:int=10, offset:int=0, _count_only:bool=False + ): + db_args = { "limit" : limit, "offset" : offset } - def pagination_link(**kwargs): - link_args = db_args.copy() - link_args.update(kwargs) - return urlencode(link_args) + convert_time = lambda t: self.template.env.globals["date2timestamp"](t) \ + if not re.match(r'^\d+$', t) else int(t) + for v,n,f in ( + (id,'id',str), (sender,'sender',str), (recipient,'recipient',str), + (processed,'processed', bool), + (time_after, 'time_after', convert_time), (time_before, 'time_before', convert_time) + ): + if not v is None: + db_args[n] = f(v) - return self.template.TemplateResponse( - 'table.html', - {"request" : request, - "db" : self.db, "db_args" : db_args, - "pagination_link" : pagination_link - } - ) + if _count_only: + return self.db.len(**db_args) + else: + def pagination_link(**kwargs): + link_args = db_args.copy() + link_args.update(kwargs) + return urlencode(link_args) + + return self.template.TemplateResponse( + 'table.html', + {"request" : request, + "db" : self.db, "db_args" : db_args, + "pagination_link" : pagination_link + } + ) + + @self.router.get("/table/total", summary="Total number of messages in table") + def table_total(request: Request, + id:str|None=None, sender:str|None=None, recipient:str|None=None, + processed:bool|None=None, + time_after:int|str|None=None, time_before:int|str|None=None, + limit:int=10, offset:int=0 + ) -> int: + + kwargs = locals().copy() + del kwargs['table'] + kwargs['_count_only'] = True + + return table(**kwargs) @self.router.get("/new", response_class=HTMLResponse, summary="Add new riddle") def new(request: Request): diff --git a/ums/management/main.py b/ums/management/main.py index 3bc6029..b04cd41 100644 --- a/ums/management/main.py +++ b/ums/management/main.py @@ -16,6 +16,8 @@ from fastapi import FastAPI, Request from fastapi.responses import HTMLResponse from fastapi.templating import Jinja2Templates +from jinja2.runtime import Undefined as JinjaUndefined + from ums.management.interface import Interface from ums.management.db import DB @@ -23,6 +25,8 @@ from ums.utils import AgentMessage, RiddleData, RiddleDataType, RiddleSolution, class WebMain(): + _TIME_FORMAT = "%H:%M:%S %d.%m.%Y" + def __init__(self): self._init_app() self._init_templates() @@ -47,8 +51,17 @@ class WebMain(): directory=TEMPLATE_PATH, auto_reload=True ) - self.template.env.globals["timestamp2date"] = lambda t: \ - datetime.fromtimestamp(t).strftime("%H:%M:%S %d.%m.%Y") + + def timestamp2date(t:int|JinjaUndefined) -> str: + return "" if isinstance(t, JinjaUndefined) \ + else datetime.fromtimestamp(t).strftime(self._TIME_FORMAT) + self.template.env.globals["timestamp2date"] = timestamp2date + + def date2timestamp(d:str|JinjaUndefined) -> int|str: + return "" if isinstance(d, JinjaUndefined) \ + else int(datetime.strptime(d, self._TIME_FORMAT).timestamp()) + self.template.env.globals["date2timestamp"] = date2timestamp + def _add_routers(self): interface_router = Interface(self.template, self.db) diff --git a/web/public/static/main.css b/web/public/static/main.css index e69de29..e12d985 100644 --- a/web/public/static/main.css +++ b/web/public/static/main.css @@ -0,0 +1,4 @@ + +.value_filter { + width: 150px; +} \ No newline at end of file diff --git a/web/public/static/main.js b/web/public/static/main.js index e69de29..23f63fb 100644 --- a/web/public/static/main.js +++ b/web/public/static/main.js @@ -0,0 +1,37 @@ + + +function pagination_link(name, value){ + let link_args = db_args; + if (name && value){ + link_args[name] = value; + } + else if(name){ + delete link_args[name]; + } + return '?' + $.param( link_args ); +} + +$(".value_filter").change((e)=>{ + window.location = pagination_link($(e.target).attr('name').substr('filter_'.length), $(e.target).val()) +}); + +function enable_auto_refresh(){ + setInterval( () => { + if($('#autoRefresh').prop('checked')){ + $.get( + '/app/table/total' + pagination_link(), + (v) => { + if( v != db_total ){ + window.location = pagination_link() + } + } + ); + } + }, 1000); + sessionStorage.setItem('auto_refresh', $('#autoRefresh').prop('checked')); +} +$("#autoRefresh").click(enable_auto_refresh); +if(sessionStorage.hasOwnProperty('auto_refresh') && sessionStorage.getItem('auto_refresh') === 'true'){ + $("#autoRefresh").prop('checked', true); + enable_auto_refresh() +} \ No newline at end of file diff --git a/web/templates/base.html b/web/templates/base.html index 1e711a8..fb3764e 100644 --- a/web/templates/base.html +++ b/web/templates/base.html @@ -18,7 +18,6 @@ -