-
Notifications
You must be signed in to change notification settings - Fork 1
/
xml.py
341 lines (284 loc) · 14.5 KB
/
xml.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
import elementtree.ElementTree as ET
from qreature.interfaces import IQuiz, IQuizAsXML, IQreatureIntIds, IQuizResult, IQuizQuestion,\
IQuizAnswer, IAnswerDepends, ISlotQuiz, IScaleQuiz, IQuizContainer, ILeadedQuiz
from zope.interface import implements, alsoProvides, noLongerProvides
from zope.component import adapts, getUtility
from elementtree.SimpleXMLTreeBuilder import TreeBuilder
from qreature import quiz
from zope.annotation.interfaces import IAnnotations
from zope.security.proxy import removeSecurityProxy
from zope.app.container.interfaces import INameChooser
from qreature.browser.result.result_interval import INTERVAL_KEY
from xmllib import Error
from zope.app.container.contained import UserError
from qreature.browser.quiz_constructor.actions import PAGES_KEY
from zope.app.file import File
from zope.schema._field import ConstraintNotSatisfied
import re
import codecs
class XMLAttributeError(Exception):
def __init__(self, tag, attr):
self.tag = tag
self.attr = attr
def __str__(self):
return u''.join((u'missing ',unicode(self.attr, 'utf-8'),u' attribute in <', unicode(self.tag, 'utf-8'), u'> tag'))
class XMLValueError(Exception):
def __init__(self, el, attr, value):
self.tag = el.tag.decode('UTF-8')
self.attr = attr
self.value = value
def __str__(self):
return u''.join((u'bad value:',self.value,u' for ',self.attr,u' in <',self.tag,u'> tag'))
class XMLContentError(Exception):
def __init__(self, el):
self.tag = el.tag.decode('UTF-8')
def __str__(self):
return u''.join((u'missing content for <', self.tag, u'> tag'))
class QuizAsXML(object):
implements(IQuizAsXML)
adapts(IQuiz)
upload = u''
download = u'http://qreature.ru/yourquiz.xml'
def __init__(self, quiz):
self.quiz = quiz
def makeQuizContainer(self,el,parent,factory):
title, err = self.getContent(el, 'title')
if err: return None, err
body, err = self.getContent(el, 'content')
if err: return None, err
chooser = INameChooser(parent)
quiz_container = factory(title, body)
try:
name = chooser.chooseName(None, quiz_container)
parent[name] = quiz_container
#this one is for testing only! its better specify ObjectAddedEvent in test setUP !!!
#notify(ObjectAddedEvent(quiz_container))
except UserError, err:
return None, err
return quiz_container, None
def getContent(self, el, tag):
searched_tag = el.find(tag)
if searched_tag is None:
return None, XMLContentError(el)
return searched_tag.text.decode('utf-8'), None
def generateQuiz(self, file_upload):
try:
# so, nobody knows what the file i will get. I want clean unicode!
file_content = file_upload.read()
#the ET reads the file in "while 1" cycle. it is awaiting for EOF
#this means without EOF, it will call thos method infinite amount of times
#It need to prevent it to call it twice
self.allready_read = False
def read_content(size=-1, ):
if self.allready_read: return False
encodings = ['utf-8','cp1251','cp866']
for e in encodings:
try:
u_content = file_content.decode(e)
if e == 'utf-8' and unicode(codecs.BOM_UTF8, "utf8") in u_content:
u_content = u_content.lstrip(unicode(codecs.BOM_UTF8, "utf8"))
break
except:
continue
self.allready_read = True
return u_content.encode('utf-8')
file_upload.read = read_content
tree = ET.parse(file_upload, parser=TreeBuilder())
root = tree.getroot()
quiz_title, err = self.getContent(root,'title')
if err: raise err
self.quiz.title = quiz_title
quiz_body, err = self.getContent(root, 'content')
if err: raise err
self.quiz.body = quiz_body
quiz_ann = IAnnotations(removeSecurityProxy(self.quiz))
pages, err = self.getContent(root, 'questions_per_page')
if pages is not None:
try:
pages = int(pages)
except ValueError:
raise XMLValueError(root,u'questions_per_page',pages)
quiz_ann[PAGES_KEY] = pages
schema, err = self.getContent(root, 'schema')
if schema == u'scale' or schema is None:
alsoProvides(removeSecurityProxy(self.quiz), IScaleQuiz)
noLongerProvides(removeSecurityProxy(self.quiz), ISlotQuiz)
elif schema == u'slots':
alsoProvides(removeSecurityProxy(self.quiz), ISlotQuiz)
noLongerProvides(removeSecurityProxy(self.quiz), IScaleQuiz)
#delete all from quiz :(
def clean(container):
names = []
containers = []
for k,v in container.items():
if IQuizContainer.providedBy(v):
names.append(k)
containers.append(v)
for v in containers:
clean(v)
for n in names:
container.__delitem__(n)
clean(self.quiz)
results_el = root.findall('result')
int_ids = getUtility(IQreatureIntIds, context=self.quiz)
for result_el in results_el:
result, err = self.makeQuizContainer(result_el,self.quiz,quiz.QuizResult)
if err: raise err
res_ann = IAnnotations(removeSecurityProxy(result))
border, err = self.getContent(result_el, 'border')
if err: pass #possible slot quiz. no intervals
if border is not None:
try:
border = int(border)
except ValueError:
raise XMLValueError(result_el,u'border',border)
res_ann[INTERVAL_KEY] = border
questions_el = root.findall('question')
#leads are added after cycle, cause all questions must be created
leads = {}
for question_el in questions_el:
question, err = self.makeQuizContainer(question_el,self.quiz,quiz.QuizQuestion)
if err: raise err
answers_el = question_el.findall('answer')
for answer_el in answers_el:
answer, err = self.makeQuizContainer(answer_el,question,quiz.QuizAnswer)
if err: raise err
try:
value, err = self.getContent(answer_el, 'value')
if err: raise err
if (removeSecurityProxy(answer)).has_key('value'):
answer['value'].value = value
else:
answer['value'] = quiz.AnswerValue(value)
alsoProvides(removeSecurityProxy(self.quiz), ILeadedQuiz)
except XMLContentError:
#it will be zero valued quiz. see qreature.eventhandlers.addAnswerValue
noLongerProvides(removeSecurityProxy(self.quiz), ILeadedQuiz)
pass
try:
lead, err = self.getContent(answer_el, 'lead')
if err: raise err
#leads are added after cycle, cause all questions must be created
leads.update({answer:lead})
except XMLContentError:
#so, no leads.
pass
#find all depends. some depends may be omited (if it is Scale quiz).
depends_el = answer_el.findall('depend')
for depend_el in depends_el:
result_title, err= self.getContent(depend_el,'result_title')
if err: raise err
results = [r for r in self.quiz.values() if IQuizResult.providedBy(r) and r.title == result_title]
if len(results) == 0:
raise Error(u'Wrong result title in <depend>')
result_id = int_ids.queryId(results[0])
depend_value, err = self.getContent(depend_el,u'value')
if err: raise err
try:
depend_value=int(depend_value)
except ValueError:
raise XMLValueError(depend_el,u'value',depend_value)
depend = quiz.AnswerDepends(result_id, depend_value)
depend_name = INameChooser(answer).chooseName(None, depend)
if (removeSecurityProxy(answer)).has_key(depend_name):
answer[depend_name].result_id = depend.result_id
answer[depend_name].depend_value = depend.depend_value
else:
answer[depend_name] = depend
for answer, lead in leads.items():
questions = [q for q in self.quiz.values() if IQuizQuestion.providedBy(q) and q.title == lead]
if len(questions) == 0:
raise Error(u'Wrong question title in <lead>')
question_id = int_ids.queryId(questions[0])
if (removeSecurityProxy(answer)).has_key('lead'):
answer['lead'].question_id = question_id
else:
answer['lead'] = quiz.AnswerLeads(question_id)
except (Error, XMLAttributeError, XMLValueError, XMLContentError, UserError, ConstraintNotSatisfied), msg:
return msg
def generateXML(self):
raw_xml = self.makeXML()
indented_xml = self.indentXML(raw_xml)
file = File(indented_xml,u'text/xml')
return file
def makeXML(self):
def makeChildEl(parent_el,child_tag,content):
parent_el.tail ="\n"
if content is None: return
parent_el.text = "\n"
child_el = ET.Element(child_tag)
child_el.text = content
child_el.tail = "\n"
parent_el.append(child_el)
int_ids = getUtility(IQreatureIntIds, context=self.quiz)
root = ET.Element(u'quiz')
makeChildEl(root,u'title',self.quiz.title)
makeChildEl(root, u'content', self.quiz.body)
if IScaleQuiz.providedBy(self.quiz):
makeChildEl(root,u'schema',u'scale')
elif ISlotQuiz.providedBy(self.quiz):
makeChildEl(root,u'schema',u'slots')
pages = IAnnotations(removeSecurityProxy(self.quiz)).get(PAGES_KEY)
if pages is not None:
makeChildEl(root,u'questions_per_page',str(pages))
results = [r for r in self.quiz.values() if IQuizResult.providedBy(r)]
for r in results:
r_el = ET.Element(u'result')
makeChildEl(r_el,u'title',r.title)
makeChildEl(r_el,u'content',r.body)
result_interval = IAnnotations(removeSecurityProxy(r)).get(INTERVAL_KEY)
if result_interval is not None:
makeChildEl(r_el,u'border',unicode(result_interval))
root.append(r_el)
questions = [q for q in self.quiz.values() if IQuizQuestion.providedBy(q)]
for q in questions:
q_el = ET.Element(u'question')
makeChildEl(q_el,u'title',q.title)
makeChildEl(q_el,u'content',q.body)
answers = [a for a in q.values() if IQuizAnswer.providedBy(a)]
for a in answers:
a_el = ET.Element(u'answer')
makeChildEl(a_el,u'title',a.title)
makeChildEl(a_el,u'content',a.body)
makeChildEl(a_el,u'value',unicode(a['value'].value))
if (removeSecurityProxy(a)).has_key('lead'):
makeChildEl(a_el, u'lead', int_ids.getObject(int(a['lead'].question_id)).title)
depends = [d for d in a.values() if IAnswerDepends.providedBy(d)]
for d in depends:
d_el = ET.Element(u'depend')
makeChildEl(d_el,u'result_title',int_ids.getObject(int(d.result_id)).title)
makeChildEl(d_el,u'value',unicode(d.depend_value))
a_el.append(d_el)
q_el.append(a_el)
root.append(q_el)
data = ET.tostring(root,'utf-8')
return data
def indentXML(self, raw_xml):
data = raw_xml
#indented_xml = '<?xml version="1.0" encoding="utf-8"?>\n'
indented_xml = ''
CONTAINER_TAGS_INDENT = {'<quiz>':0,'</quiz>':0,
'<result>':1,'</result>':1,
'<question>':1,'</question>':1,
'<answer>':2,'</answer>':2,
'<depend>':3,'</depend>':3}
lines = data.split('\n')
indent = ''
shifted_indent = False
container = False
for line in lines:
for key, value in CONTAINER_TAGS_INDENT.items():
if key in line:
indent = '\t'*value
shifted_indent = False
container = True
break
if not container:
if not shifted_indent:
indent += '\t'
shifted_indent = True
else:
pass
indented_xml += ''.join((indent,line,'\n'))
container = False
return indented_xml