Skip to content
This repository was archived by the owner on Sep 29, 2023. It is now read-only.

Commit 332f106

Browse files
Merge pull request #148 from viur-framework/feature_recordBone
Proposal for an implementation of a recordBone
2 parents 3db3875 + 8f98cc2 commit 332f106

4 files changed

Lines changed: 257 additions & 3 deletions

File tree

bones/__init__.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,4 @@
2222
from server.bones.emailBone import emailBone
2323
from server.bones.randomSliceBone import randomSliceBone
2424
from server.bones.spatialBone import spatialBone
25+
from server.bones.recordBone import recordBone

bones/recordBone.py

Lines changed: 231 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,231 @@
1+
# -*- coding: utf-8 -*-
2+
from server.bones.bone import baseBone, getSystemInitialized
3+
from server.errors import ReadFromClientError
4+
import extjson
5+
6+
7+
class recordBone(baseBone):
8+
type = "record"
9+
10+
def __init__(self, using, format=None, indexed=False, multiple=True, *args, **kwargs):
11+
super(recordBone, self).__init__(indexed=indexed, multiple=multiple, *args, **kwargs)
12+
13+
self.using = using
14+
self.format = format
15+
if not format or indexed or not multiple:
16+
NotImplemented("A recordBone must not be indexed, must be multiple and must have a format set")
17+
18+
if getSystemInitialized():
19+
self._usingSkelCache = using()
20+
else:
21+
self._usingSkelCache = None
22+
23+
def setSystemInitialized(self):
24+
super(recordBone, self).setSystemInitialized()
25+
self._usingSkelCache = self.using()
26+
27+
28+
def _restoreValueFromDatastore(self, val):
29+
"""
30+
Restores one of our values from the serialized data read from the datastore
31+
32+
:param value: Json-Encoded datastore property
33+
34+
:return: Our Value (with restored usingSkel data)
35+
"""
36+
value = extjson.loads(val)
37+
assert isinstance(value, dict), "Read something from the datastore thats not a dict: %s" % str(type(value))
38+
39+
usingSkel = self._usingSkelCache
40+
usingSkel.setValuesCache({})
41+
usingSkel.unserialize(value)
42+
43+
return usingSkel.getValuesCache()
44+
45+
def unserialize(self, valuesCache, name, expando):
46+
if name not in expando:
47+
valuesCache[name] = None
48+
return True
49+
50+
val = expando[name]
51+
52+
if self.multiple:
53+
valuesCache[name] = []
54+
55+
if not val:
56+
return True
57+
58+
if isinstance(val, list):
59+
for res in val:
60+
try:
61+
valuesCache[name].append(self._restoreValueFromDatastore(res))
62+
except:
63+
raise
64+
else:
65+
try:
66+
valuesCache[name].append(self._restoreValueFromDatastore(val))
67+
except:
68+
raise
69+
70+
71+
return True
72+
73+
def serialize(self, valuesCache, name, entity):
74+
if not valuesCache[name]:
75+
entity.set(name, None, False)
76+
77+
else:
78+
usingSkel = self._usingSkelCache
79+
res = []
80+
81+
for val in valuesCache[name]:
82+
usingSkel.setValuesCache(val)
83+
res.append(extjson.dumps(usingSkel.serialize()))
84+
85+
entity.set(name, res, False)
86+
87+
return entity
88+
89+
def fromClient(self, valuesCache, name, data):
90+
valuesCache[name] = []
91+
tmpRes = {}
92+
93+
clientPrefix = "%s." % name
94+
95+
for k, v in data.items():
96+
#print(k, v)
97+
98+
if k.startswith(clientPrefix) or k == name:
99+
if k == name:
100+
k = k.replace(name, "", 1)
101+
102+
else:
103+
k = k.replace(clientPrefix, "", 1)
104+
105+
if "." in k:
106+
try:
107+
idx, bname = k.split(".", 1)
108+
idx = int(idx)
109+
except ValueError:
110+
idx = 0
111+
112+
try:
113+
bname = k.split(".", 1)
114+
except ValueError:
115+
# We got some garbage as input; don't try to parse it
116+
continue
117+
118+
else:
119+
idx = 0
120+
bname = k
121+
122+
if not bname:
123+
continue
124+
125+
if not idx in tmpRes:
126+
tmpRes[idx] = {}
127+
128+
if bname in tmpRes[idx]:
129+
if isinstance(tmpRes[idx][bname], list):
130+
tmpRes[idx][bname].append(v)
131+
else:
132+
tmpRes[idx][bname] = [tmpRes[idx][bname], v]
133+
else:
134+
tmpRes[idx][bname] = v
135+
136+
tmpList = [tmpRes[k] for k in sorted(tmpRes.keys())]
137+
138+
errorDict = {}
139+
forceFail = False
140+
141+
for i, r in enumerate(tmpList[:]):
142+
usingSkel = self._usingSkelCache
143+
usingSkel.setValuesCache({})
144+
145+
if not usingSkel.fromClient(r):
146+
for k, v in usingSkel.errors.items():
147+
errorDict["%s.%d.%s" % (name, i, k)] = v
148+
forceFail = True
149+
150+
tmpList[i] = usingSkel.getValuesCache()
151+
152+
cleanList = []
153+
154+
for item in tmpList:
155+
err = self.isInvalid(item)
156+
if err:
157+
errorDict["%s.%s" % (name, tmpList.index(item))] = err
158+
else:
159+
cleanList.append(item)
160+
161+
valuesCache[name] = tmpList
162+
163+
if not cleanList:
164+
if not (self.required or errorDict):
165+
# Returning a error will only cause a warning if we are not required
166+
return "No value selected!"
167+
errorDict[name] = "No value selected"
168+
169+
if len(errorDict.keys()):
170+
return ReadFromClientError(errorDict, forceFail)
171+
172+
return None
173+
174+
def getSearchTags(self, values, key):
175+
def getValues(res, skel, valuesCache):
176+
for k, bone in skel.items():
177+
if bone.searchable:
178+
for tag in bone.getSearchTags(valuesCache, k):
179+
if tag not in res:
180+
res.append(tag)
181+
return res
182+
183+
value = values.get(key)
184+
res = []
185+
186+
if not value:
187+
return res
188+
189+
for val in value:
190+
res = getValues(res, self._usingSkelCache, val)
191+
192+
return res
193+
194+
def getSearchDocumentFields(self, valuesCache, name, prefix=""):
195+
def getValues(res, skel, valuesCache, searchPrefix):
196+
for key, bone in skel.items():
197+
if bone.searchable:
198+
res.extend(bone.getSearchDocumentFields(valuesCache, key, prefix=searchPrefix))
199+
200+
value = valuesCache.get(name)
201+
res = []
202+
203+
if not value:
204+
return res
205+
206+
for idx, val in enumerate(value):
207+
getValues(res, self._usingSkelCache, val, "%s%s_%s" % (prefix, name, str(idx)))
208+
209+
return res
210+
211+
def getReferencedBlobs(self, valuesCache, name):
212+
def blobsFromSkel(skel, valuesCache):
213+
blobList = set()
214+
for key, _bone in skel.items():
215+
blobList.update(_bone.getReferencedBlobs(valuesCache, key))
216+
return blobList
217+
218+
res = set()
219+
value = valuesCache.get(name)
220+
221+
if not value:
222+
return res
223+
224+
if isinstance(value, list):
225+
for val in value:
226+
res.update(blobsFromSkel(self._usingSkelCache, val))
227+
228+
elif isinstance(value, dict):
229+
res.update(blobsFromSkel(self._usingSkelCache, value))
230+
231+
return res

render/html/default.py

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,8 @@ def renderBoneValue(self, bone, skel, key):
313313
elif skelValue in bone.values:
314314
return KeyValueWrapper(skelValue, bone.values[skelValue])
315315
return skelValue
316-
elif bone.type=="relational" or bone.type.startswith("relational."):
316+
317+
elif bone.type == "relational" or bone.type.startswith("relational."):
317318
if isinstance(skel[key], list):
318319
tmpList = []
319320
for k in skel[key]:
@@ -350,8 +351,23 @@ def renderBoneValue(self, bone, skel, key):
350351
"dest": self.collectSkelData(refSkel),
351352
"rel": usingData
352353
}
353-
else:
354-
return None
354+
355+
elif bone.type == "record" or bone.type.startswith("record."):
356+
usingSkel = bone._usingSkelCache
357+
value = skel[key]
358+
359+
if isinstance(value, list):
360+
ret = []
361+
for entry in value:
362+
usingSkel.setValuesCache(entry)
363+
ret.append(self.collectSkelData(usingSkel))
364+
365+
return ret
366+
367+
elif isinstance(value, dict):
368+
usingSkel.setValuesCache(value)
369+
return self.collectSkelData(usingSkel)
370+
355371
else:
356372
return skel[key]
357373

render/json/default.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,12 @@ def renderBoneStructure(self, bone):
5454
"relskel": self.renderSkelStructure(RefSkel.fromSkel(skeletonByKind(bone.kind), *bone.refKeys))
5555
})
5656

57+
elif bone.type == "record" or bone.type.startswith("record."):
58+
ret.update({
59+
"multiple": bone.multiple,
60+
"format": bone.format,
61+
"using": self.renderSkelStructure(bone.using())
62+
})
5763

5864
elif bone.type == "select" or bone.type.startswith("select."):
5965
ret.update({

0 commit comments

Comments
 (0)