Hide keyboard shortcuts

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 

4 

5from ase.db.core import Database, default_key_descriptions 

6from ase.db.table import Table, all_columns 

7 

8 

9class Session: 

10 next_id = 1 

11 sessions: Dict[int, 'Session'] = {} 

12 

13 def __init__(self, project_name: str): 

14 self.id = Session.next_id 

15 Session.next_id += 1 

16 

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] 

22 

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 

30 

31 def __str__(self) -> str: 

32 return str(self.__dict__) 

33 

34 @staticmethod 

35 def get(id: int) -> 'Session': 

36 return Session.sessions[id] 

37 

38 def update(self, 

39 what: str, 

40 x: str, 

41 args: Dict[str, str], 

42 project: Dict[str, Any]) -> None: 

43 

44 if self.columns is None: 

45 self.columns = project['default_columns'][:] 

46 

47 if what == 'query': 

48 self.query = project['handle_query_function'](args) 

49 self.nrows = None 

50 self.page = 0 

51 

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 

60 

61 elif what == 'limit': 

62 self.limit = int(x) 

63 self.page = 0 

64 

65 elif what == 'page': 

66 self.page = int(x) 

67 

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) 

80 

81 @property 

82 def row1(self) -> int: 

83 return self.page * self.limit + 1 

84 

85 @property 

86 def row2(self) -> int: 

87 assert self.nrows is not None 

88 return min((self.page + 1) * self.limit, self.nrows) 

89 

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 

118 

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 

133 

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 

144 

145 

146KeyDescriptions = Dict[str, Tuple[str, str, str]] # type-hint shortcut 

147 

148 

149def create_key_descriptions(kd: KeyDescriptions) -> KeyDescriptions: 

150 kd = kd.copy() 

151 kd.update(default_key_descriptions) 

152 

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) 

159 

160 sub = re.compile(r'`(.)_(.)`') 

161 sup = re.compile(r'`(.*)\^\{?(.*?)\}?`') 

162 

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) 

170 

171 return kd