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"""Detail records of master records of tables. 

2 

3* Rendering 

4* Customization 

5""" 

6 

7from config import Config as C, Names as N 

8from control.utils import pick as G, E 

9from control.html import HtmlElements as H 

10from control.perm import checkTable 

11 

12CT = C.tables 

13 

14DETAILS = CT.details 

15 

16 

17class Details: 

18 """Deals with detail records.""" 

19 

20 inheritProps = ( 

21 N.context, 

22 N.uid, 

23 N.eppn, 

24 N.tableObj, 

25 N.mkTable, 

26 N.table, 

27 N.record, 

28 N.eid, 

29 N.fields, 

30 N.prov, 

31 N.perm, 

32 N.wfitem, 

33 N.kind, 

34 ) 

35 

36 def __init__(self, recordObj): 

37 """## Initialization 

38 

39 Store the incoming information. 

40 

41 A number of properties will be inherited from the master record object 

42 that spawns a detail record object. 

43 

44 Parameters 

45 ---------- 

46 recordObj: object 

47 A `control.record.Record` object (or one of a derived class) 

48 """ 

49 

50 for prop in Details.inheritProps: 

51 setattr(self, prop, getattr(recordObj, prop, None)) 

52 

53 self.details = {} 

54 """*dict* Stores the details of this record. 

55 

56 Keyed by the name of the detail table, values are lists of detail 

57 records in that detail table. 

58 """ 

59 

60 def tablePerm(self, table): 

61 context = self.context 

62 auth = context.auth 

63 

64 return checkTable(auth, table) 

65 

66 def fetchDetails(self, dtable, sortKey=None): 

67 """Fetch detail records from the database. 

68 

69 !!! caution 

70 The details will only be fetched if the user has permissions 

71 to list the detail table! 

72 

73 !!! caution "Workflow restrictions" 

74 There might be additional restrictions on individual records 

75 due to workflow. Some records may not be readable. 

76 They will be filtered out. 

77 

78 Parameters 

79 ---------- 

80 dtable: string 

81 The name of the table in which the detail records are stored. 

82 sortKey: function 

83 A function to sort the detail records. 

84 

85 Returns 

86 ------- 

87 void 

88 The detail records are stored in the `details` attribute, which is a 

89 dict keyed by the names of the detail tables. 

90 """ 

91 

92 if not self.tablePerm(dtable): 92 ↛ 93line 92 didn't jump to line 93, because the condition on line 92 was never true

93 return 

94 

95 context = self.context 

96 db = context.db 

97 mkTable = self.mkTable 

98 table = self.table 

99 eid = self.eid 

100 

101 dtableObj = mkTable(context, dtable) 

102 drecords = db.getDetails(dtable, table, eid, sortKey=sortKey) 

103 self.details[dtable] = ( 

104 dtableObj, 

105 tuple(drecord for drecord in drecords if dtableObj.readable(drecord)), 

106 ) 

107 

108 def wrap(self, readonly=False, showTable=None, showEid=None): 

109 """Wrap the details of all tables for this record into HTML. 

110 

111 Parameters 

112 ---------- 

113 readonly: boolean, optional `False` 

114 Whether the records should be presented in readonly form. 

115 showTable: string 

116 Name of the detail table of which record `showEid` should be opened. 

117 showEid: ObjectId 

118 Id of the detail record that should be initially opened. 

119 

120 Returns 

121 ------- 

122 string(html) 

123 """ 

124 

125 table = self.table 

126 

127 for detailTable in G(DETAILS, table, default=[]): 

128 self.fetchDetails(detailTable) 

129 

130 details = self.details 

131 

132 return H.join( 

133 self.wrapDetail( 

134 detailTable, 

135 readonly=readonly, 

136 showEid=showEid if detailTable == showTable else None, 

137 ) 

138 for detailTable in details 

139 ) 

140 

141 def wrapDetail( 

142 self, 

143 dtable, 

144 withDetails=False, 

145 readonly=False, 

146 bodyMethod=None, 

147 inner=True, 

148 wrapMethod=None, 

149 expanded=False, 

150 withProv=True, 

151 extraCls=None, 

152 filterFunc=None, 

153 combineMethod=None, 

154 withN=True, 

155 showEid=None, 

156 ): 

157 """Wrap the details of a specific table for this record into HTML. 

158 

159 Some of the parameters above will be passed to the initializers 

160 of the detail record objects, others to their `wrap` method. 

161 

162 Parameters 

163 ---------- 

164 dtable: string 

165 The name of the detail table 

166 withDetails, readonly, bodyMethod: mixed 

167 See `control.record.Record` 

168 inner, wrapMethod, withProv, extraCls: mixed 

169 See `control.record.Record.wrap`. 

170 expanded: boolean 

171 Whether to expand all details in this detail table. 

172 If True, the expanded details will not get a collapse control. 

173 filterFunc: function, optional `None` 

174 You can optionally filter the detail records. 

175 combineMethod: function, optional `None` 

176 After getting the HTML for individual records, you can 

177 instruct to reorder/restructure those representations. 

178 withN: boolean, optional `True` 

179 Whether to present the number of detail records 

180 showEid: ObjectId 

181 Id of the detail record that should be initially opened. 

182 

183 Returns 

184 ------- 

185 string(html) 

186 """ 

187 

188 details = self.details 

189 

190 (dtableObj, drecordsAll) = G(details, dtable, default=(None, [])) 

191 if not dtableObj: 191 ↛ 192line 191 didn't jump to line 192, because the condition on line 191 was never true

192 return E 

193 

194 drecords = [ 

195 drecord 

196 for drecord in drecordsAll 

197 if filterFunc is None or filterFunc(drecord) 

198 ] 

199 

200 nRecords = len(drecords) 

201 if nRecords == 0: 

202 return E 

203 

204 (itemSingular, itemPlural) = dtableObj.itemLabels 

205 itemLabel = itemSingular if nRecords == 1 else itemPlural 

206 

207 nRep = H.div(f"""{nRecords} {itemLabel}""", cls="stats") 

208 

209 drecordReps = [] 

210 for drecord in drecords: 

211 show = showEid == str(G(drecord, N._id)) 

212 drecordReps.append( 

213 dtableObj.record( 

214 record=drecord, 

215 readonly=readonly, 

216 bodyMethod=bodyMethod, 

217 withDetails=withDetails or show, 

218 ).wrap( 

219 inner=inner, 

220 wrapMethod=wrapMethod, 

221 withProv=withProv, 

222 expanded=0 if expanded else 1 if show else -1, 

223 ) 

224 ) 

225 if combineMethod: 225 ↛ 226line 225 didn't jump to line 226, because the condition on line 225 was never true

226 drecordReps = combineMethod(drecordReps) 

227 

228 innerCls = " inner" if inner else E 

229 return H.div( 

230 [nRep if withN else E] + drecordReps, 

231 cls=f"record-details{innerCls} {extraCls}", 

232 ) 

233 

234 @staticmethod 

235 def mustShow(table, kwargs): 

236 """Determines the record id specified by `showTable` and `showEid` in `kwargs`. 

237 

238 "kwargs" may contain the keys `showTable` and `showEid`. 

239 We want the value of `showEid`, but only if the value of `showTable` matches 

240 `table`. 

241 

242 !!! hint 

243 We need this when we have just inserted a detail record and want to 

244 navigate to the master record with the new detail record in an 

245 expanded state. 

246 """ 

247 

248 return G(kwargs, N.showEid) if G(kwargs, N.showTable) == table else None