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

1# fmt: off 

2 

3"""Helper functions for Flask WSGI-app.""" 

4from typing import Dict, List, Optional, Tuple 

5 

6from ase.db.core import Database 

7from ase.db.table import Table, all_columns 

8 

9 

10class Session: 

11 """Seesion object. 

12 

13 Stores stuff that the jinja2 templetes (like templates/table.html) 

14 need to show. Example from table.html:: 

15 

16 Displaying rows {{ s.row1 }}-{{ s.row2 }} out of {{ s.nrows }} 

17 

18 where *s* is the session object. 

19 """ 

20 next_id = 1 

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

22 

23 def __init__(self, project_name: str): 

24 self.id = Session.next_id 

25 Session.next_id += 1 

26 

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] 

32 

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 

41 

42 def __str__(self) -> str: 

43 return str(self.__dict__) 

44 

45 @staticmethod 

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

47 return Session.sessions[id] 

48 

49 def update(self, 

50 what: str, 

51 x: str, 

52 args: Dict[str, str], 

53 project) -> None: 

54 

55 if self.columns is None: 

56 self.columns = list(project.default_columns) 

57 

58 if what == 'query': 

59 self.query = project.handle_query(args) 

60 self.nrows = None 

61 self.page = 0 

62 

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 

71 

72 elif what == 'limit': 

73 self.limit = int(x) 

74 self.page = 0 

75 

76 elif what == 'page': 

77 self.page = int(x) 

78 

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) 

91 

92 @property 

93 def row1(self) -> int: 

94 return self.page * self.limit + 1 

95 

96 @property 

97 def row2(self) -> int: 

98 assert self.nrows is not None 

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

100 

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 

129 

130 def create_table(self, 

131 db: Database, 

132 uid_key: str, 

133 keys: List[str]) -> Table: 

134 query = self.query 

135 

136 if self.nrows_total is None: 

137 self.nrows_total = db.count() 

138 

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 

148 

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