Coverage for control/typ/value.py : 92%

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"""Types of values in value tables."""
3from control.utils import pick as G, E, NBSP
4from control.html import HtmlElements as H
5from config import Config as C, Names as N
6from control.typ.related import Related, castObjectId
8CW = C.web
10MESSAGES = CW.messages
12FILTER_THRESHOLD = CW.filterThreshold
14QQ = H.icon(CW.unknown[N.generic])
15Qq = H.icon(CW.unknown[N.generic], asChar=True)
18class ConversionError(Exception):
19 pass
22class Value(Related):
23 """Type class for types with values in value tables."""
25 widgetType = N.related
27 def __init__(self, context):
28 """## Initialization
30 Store a handle to the Context singleton.
32 Parameters
33 ----------
34 context: object
35 See below.
36 """
38 self.context = context
39 """*object* A `control.context.Context` singleton.
40 """
42 def fromStr(self, editVal, constrain=None, uid=None, eppn=None, extensible=False):
43 """Convert a value to an object id by looking it up in a value table.
45 If the value is a singleton list, and if the value list is extensible,
46 we insert the new value into the value table, and return the new id.
48 Otherwise, the value must be either:
50 * the string representation of id of a value
51 * or a value in the relevant value table.
53 In all cases the MongoDb object id of that value is returned.
55 If a `constrain` is given only values in a subset defined by a constraint
56 qualify. This happens when we look for score records that are bound to a
57 criterion.
59 Parameters
60 ----------
61 editVal: string
62 The value as it has been sent from the client
63 constrain:
64 An additional constraint for the value.
65 See `control.db.Db.getValueRecords`.
66 """
68 if not editVal:
69 return None
71 context = self.context
72 db = context.db
73 table = self.name
75 valType = type(editVal)
76 valuesInv = (
77 getattr(db, f"""{table}Inv""", {})
78 if constrain is None
79 else db.getValueInv(table, constrain)
80 )
81 if valType is list or valType is tuple:
82 if extensible and editVal:
83 editVal = editVal[0]
84 if editVal in valuesInv: 84 ↛ 85line 84 didn't jump to line 85, because the condition on line 84 was never true
85 return valuesInv[editVal]
86 fieldName = N.rep if extensible is True else extensible
87 field = {fieldName: editVal}
88 return db.insertItem(table, uid, eppn, True, **field)
89 raise ConversionError
91 values = (
92 getattr(db, table, {})
93 if constrain is None
94 else db.getValueIds(table, constrain)
95 )
96 valId = castObjectId(editVal)
97 if valId is None:
98 valId = G(valuesInv, editVal)
99 if valId not in values:
100 raise ConversionError
101 return valId
103 def toEdit(self, val):
104 return val
106 def toOrig(self, val):
107 return val if val is None else str(val)
109 def widget(self, val, multiple, extensible, constrain):
110 context = self.context
111 db = context.db
112 table = self.name
114 if table == N.permissionGroup: 114 ↛ 115line 114 didn't jump to line 115, because the condition on line 114 was never true
115 auth = context.auth
116 user = auth.user
117 group = G(user, N.group)
118 groupRep = G(G(db.permissionGroup, group), N.rep)
119 else:
120 groupRep = None
122 valueRecords = db.getValueRecords(table, constrain=constrain, upper=groupRep)
124 filterControl = (
125 [
126 H.input(
127 E,
128 type=N.text,
129 placeholder=G(MESSAGES, N.filter, default=E),
130 cls="wfilter",
131 ),
132 H.iconx(N.add, cls="small wfilter add", title="add value")
133 if extensible
134 else E,
135 H.iconx(N.clear, cls="small wfilter clear", title="clear filter"),
136 ]
137 if len(valueRecords) > FILTER_THRESHOLD
138 else []
139 )
140 atts = dict(
141 markup=True,
142 clickable=True,
143 multiple=multiple,
144 active=val,
145 hideInActual=True,
146 hideBlockedUsers=True,
147 )
148 results = (self.title(record=record, **atts) for record in valueRecords)
149 return H.div(
150 filterControl
151 + [
152 formatted
153 for (text, formatted) in (
154 ([] if multiple else [self.title(record={}, **atts)])
155 + (
156 sorted(
157 results,
158 key=lambda x: x[0].lower(),
159 )
160 if type(valueRecords) is tuple
161 else list(results)
162 )
163 )
164 ],
165 cls="wvalue",
166 )
168 def title(
169 self,
170 eid=None,
171 record=None,
172 markup=False,
173 clickable=False,
174 multiple=False,
175 active=None,
176 hideInActual=False,
177 hideBlockedUsers=False,
178 **kwargs,
179 ):
180 if record is None and eid is None:
181 return None if markup is None else (QQ, QQ) if markup else Qq
183 table = self.name
185 if record is None:
186 context = self.context
187 record = context.getItem(table, eid)
189 titleStr = self.titleStr(record, markup=markup, **kwargs)
190 titleHint = self.titleHint(record)
192 if markup:
193 if eid is None:
194 eid = G(record, N._id)
196 isActive = eid in (active or []) if multiple else eid == active
197 baseCls = (
198 ("button " if multiple or not isActive else "label ")
199 if clickable
200 else "tag "
201 )
202 activeCls = "active " if isActive else E
203 inActualCls = self.inActualCls(record=record)
204 hidden = (
205 hideInActual
206 and inActualCls
207 and not isActive
208 or hideBlockedUsers
209 and table == N.user
210 and not G(record, N.mayLogin)
211 )
212 if hidden: 212 ↛ 213line 212 didn't jump to line 213, because the condition on line 212 was never true
213 return (E, E)
214 atts = dict(cls=f"{baseCls}{activeCls}medium {inActualCls}")
215 if clickable and eid is not None:
216 atts[N.eid] = str(eid)
218 if titleHint:
219 atts[N.title] = titleHint
221 titleIcon = (
222 (NBSP + H.icon(N.cross if isActive else N.add, cls="small"))
223 if multiple
224 else E
225 )
227 titleFormatted = H.span(
228 [titleStr, titleIcon],
229 lab=titleStr.lower(),
230 **atts,
231 )
232 return (titleStr, titleFormatted)
233 else:
234 return titleStr