Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""Helper functions for Flask WSGI-app."""
2import re
3from typing import Any, Dict, List, Optional, Tuple
5from ase.db.core import Database, default_key_descriptions
6from ase.db.table import Table, all_columns
9class Session:
10 next_id = 1
11 sessions: Dict[int, 'Session'] = {}
13 def __init__(self, project_name: str):
14 self.id = Session.next_id
15 Session.next_id += 1
17 Session.sessions[self.id] = self
18 if len(Session.sessions) > 2000:
19 # Forget old sessions:
20 for id in sorted(Session.sessions)[:400]:
21 del Session.sessions[id]
23 self.columns: Optional[List[str]] = None
24 self.nrows: Optional[int] = None
25 self.page = 0
26 self.limit = 25
27 self.sort = ''
28 self.query = ''
29 self.project_name = project_name
31 def __str__(self) -> str:
32 return str(self.__dict__)
34 @staticmethod
35 def get(id: int) -> 'Session':
36 return Session.sessions[id]
38 def update(self,
39 what: str,
40 x: str,
41 args: Dict[str, str],
42 project: Dict[str, Any]) -> None:
44 if self.columns is None:
45 self.columns = project['default_columns'][:]
47 if what == 'query':
48 self.query = project['handle_query_function'](args)
49 self.nrows = None
50 self.page = 0
52 elif what == 'sort':
53 if x == self.sort:
54 self.sort = '-' + x
55 elif '-' + x == self.sort:
56 self.sort = 'id'
57 else:
58 self.sort = x
59 self.page = 0
61 elif what == 'limit':
62 self.limit = int(x)
63 self.page = 0
65 elif what == 'page':
66 self.page = int(x)
68 elif what == 'toggle':
69 column = x
70 if column == 'reset':
71 self.columns = project['default_columns'][:]
72 else:
73 if column in self.columns:
74 self.columns.remove(column)
75 if column == self.sort.lstrip('-'):
76 self.sort = 'id'
77 self.page = 0
78 else:
79 self.columns.append(column)
81 @property
82 def row1(self) -> int:
83 return self.page * self.limit + 1
85 @property
86 def row2(self) -> int:
87 assert self.nrows is not None
88 return min((self.page + 1) * self.limit, self.nrows)
90 def paginate(self) -> List[Tuple[int, str]]:
91 """Helper function for pagination stuff."""
92 assert self.nrows is not None
93 npages = (self.nrows + self.limit - 1) // self.limit
94 p1 = min(5, npages)
95 p2 = max(self.page - 4, p1)
96 p3 = min(self.page + 5, npages)
97 p4 = max(npages - 4, p3)
98 pgs = list(range(p1))
99 if p1 < p2:
100 pgs.append(-1)
101 pgs += list(range(p2, p3))
102 if p3 < p4:
103 pgs.append(-1)
104 pgs += list(range(p4, npages))
105 pages = [(self.page - 1, 'previous')]
106 for p in pgs:
107 if p == -1:
108 pages.append((-1, '...'))
109 elif p == self.page:
110 pages.append((-1, str(p + 1)))
111 else:
112 pages.append((p, str(p + 1)))
113 nxt = min(self.page + 1, npages - 1)
114 if nxt == self.page:
115 nxt = -1
116 pages.append((nxt, 'next'))
117 return pages
119 def create_table(self,
120 db: Database,
121 uid_key: str,
122 keys: List[str]) -> Table:
123 query = self.query
124 if self.nrows is None:
125 try:
126 self.nrows = db.count(query)
127 except (ValueError, KeyError) as e:
128 error = ', '.join(['Bad query'] + list(e.args))
129 from flask import flash
130 flash(error)
131 query = 'id=0' # this will return no rows
132 self.nrows = 0
134 table = Table(db, uid_key)
135 table.select(query, self.columns, self.sort,
136 self.limit, offset=self.page * self.limit,
137 show_empty_columns=True)
138 table.format()
139 assert self.columns is not None
140 table.addcolumns = sorted(column for column in
141 all_columns + keys
142 if column not in self.columns)
143 return table
146KeyDescriptions = Dict[str, Tuple[str, str, str]] # type-hint shortcut
149def create_key_descriptions(kd: KeyDescriptions) -> KeyDescriptions:
150 kd = kd.copy()
151 kd.update(default_key_descriptions)
153 # Fill in missing descriptions:
154 for key, (short, long, unit) in kd.items():
155 if not short:
156 kd[key] = (key, key, unit)
157 elif not long:
158 kd[key] = (short, short, unit)
160 sub = re.compile(r'`(.)_(.)`')
161 sup = re.compile(r'`(.*)\^\{?(.*?)\}?`')
163 # Convert LaTeX to HTML:
164 for key, value in kd.items():
165 short, long, unit = value
166 unit = sub.sub(r'\1<sub>\2</sub>', unit)
167 unit = sup.sub(r'\1<sup>\2</sup>', unit)
168 unit = unit.replace(r'\text{', '').replace('}', '')
169 kd[key] = (short, long, unit)
171 return kd