Coverage for /builds/ase/ase/ase/db/web.py: 72.90%
107 statements
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
« prev ^ index » next coverage.py v7.5.3, created at 2025-08-02 00:12 +0000
1# fmt: off
3"""Helper functions for Flask WSGI-app."""
4from typing import Dict, List, Optional, Tuple
6from ase.db.core import Database
7from ase.db.table import Table, all_columns
10class Session:
11 """Seesion object.
13 Stores stuff that the jinja2 templetes (like templates/table.html)
14 need to show. Example from table.html::
16 Displaying rows {{ s.row1 }}-{{ s.row2 }} out of {{ s.nrows }}
18 where *s* is the session object.
19 """
20 next_id = 1
21 sessions: Dict[int, 'Session'] = {}
23 def __init__(self, project_name: str):
24 self.id = Session.next_id
25 Session.next_id += 1
27 Session.sessions[self.id] = self
28 if len(Session.sessions) > 2000:
29 # Forget old sessions:
30 for id in sorted(Session.sessions)[:400]:
31 del Session.sessions[id]
33 self.columns: Optional[List[str]] = None
34 self.nrows: Optional[int] = None
35 self.nrows_total: Optional[int] = None
36 self.page = 0
37 self.limit = 25
38 self.sort = ''
39 self.query = ''
40 self.project_name = project_name
42 def __str__(self) -> str:
43 return str(self.__dict__)
45 @staticmethod
46 def get(id: int) -> 'Session':
47 return Session.sessions[id]
49 def update(self,
50 what: str,
51 x: str,
52 args: Dict[str, str],
53 project) -> None:
55 if self.columns is None:
56 self.columns = list(project.default_columns)
58 if what == 'query':
59 self.query = project.handle_query(args)
60 self.nrows = None
61 self.page = 0
63 elif what == 'sort':
64 if x == self.sort:
65 self.sort = '-' + x
66 elif '-' + x == self.sort:
67 self.sort = 'id'
68 else:
69 self.sort = x
70 self.page = 0
72 elif what == 'limit':
73 self.limit = int(x)
74 self.page = 0
76 elif what == 'page':
77 self.page = int(x)
79 elif what == 'toggle':
80 column = x
81 if column == 'reset':
82 self.columns = list(project.default_columns)
83 else:
84 if column in self.columns:
85 self.columns.remove(column)
86 if column == self.sort.lstrip('-'):
87 self.sort = 'id'
88 self.page = 0
89 else:
90 self.columns.append(column)
92 @property
93 def row1(self) -> int:
94 return self.page * self.limit + 1
96 @property
97 def row2(self) -> int:
98 assert self.nrows is not None
99 return min((self.page + 1) * self.limit, self.nrows)
101 def paginate(self) -> List[Tuple[int, str]]:
102 """Helper function for pagination stuff."""
103 assert self.nrows is not None
104 npages = (self.nrows + self.limit - 1) // self.limit
105 p1 = min(5, npages)
106 p2 = max(self.page - 4, p1)
107 p3 = min(self.page + 5, npages)
108 p4 = max(npages - 4, p3)
109 pgs = list(range(p1))
110 if p1 < p2:
111 pgs.append(-1)
112 pgs += list(range(p2, p3))
113 if p3 < p4:
114 pgs.append(-1)
115 pgs += list(range(p4, npages))
116 pages = [(self.page - 1, 'previous')]
117 for p in pgs:
118 if p == -1:
119 pages.append((-1, '...'))
120 elif p == self.page:
121 pages.append((-1, str(p + 1)))
122 else:
123 pages.append((p, str(p + 1)))
124 nxt = min(self.page + 1, npages - 1)
125 if nxt == self.page:
126 nxt = -1
127 pages.append((nxt, 'next'))
128 return pages
130 def create_table(self,
131 db: Database,
132 uid_key: str,
133 keys: List[str]) -> Table:
134 query = self.query
136 if self.nrows_total is None:
137 self.nrows_total = db.count()
139 if self.nrows is None:
140 try:
141 self.nrows = db.count(query)
142 except (ValueError, KeyError) as e:
143 error = ', '.join(['Bad query'] + list(e.args))
144 from flask import flash
145 flash(error)
146 query = 'id=0' # this will return no rows
147 self.nrows = 0
149 table = Table(db, uid_key)
150 table.select(query, self.columns, self.sort,
151 self.limit, offset=self.page * self.limit,
152 show_empty_columns=True)
153 table.format()
154 assert self.columns is not None
155 table.addcolumns = sorted(column for column in
156 [*all_columns, *keys]
157 if column not in self.columns)
158 return table