/
userjourney.py
268 lines (215 loc) · 10.2 KB
/
userjourney.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
import functools
import itertools
from xml.dom import minidom
import xml.etree.cElementTree as ET
from step import Step, StepNameException
# from dynamic_data import DynamicDataItem
from dynamic_data import ConstantDDI, DateDDI, DelimitedFileDDI, ListDDI, VariableDDI, RelatedDDI, ResponseDDI, AutoCorrelatedDDI, AutoIncrementDDI
from stepgroup import StepGroup
SCHEME_PREFIX = '{http://www.reflective.com}'
TYPE_TO_CLASS_MAP = {'AUTOCORR': AutoCorrelatedDDI, 'AUTOINCR': AutoIncrementDDI, 'CONSTANT': ConstantDDI, 'DATE ': DateDDI, 'FLATFILE': DelimitedFileDDI,
'LIST ': ListDDI, 'RESPONSE': ResponseDDI, 'SAMEAS ': RelatedDDI, 'VARIABLE': VariableDDI,}
class DDINameException(Exception):
def __init__(self, *args):
self.args = [a for a in args]
StepIDException = DDINameException
StepGroupIDException = DDINameException
class UserJourney():
def __init__(self, uj_name, app_name='', version ='7.7'):
self.name = uj_name
self.app_name = app_name
self.version = version
def import_uj(self, filename):
with open(filename, 'r') as f:
raw = f.readlines()
self.first_import_line = raw[0]
ET.register_namespace('', SCHEME_PREFIX[1:-1])
tree = ET.parse(filename)
root = tree.getroot()
self.name = root.attrib['NAME']
self.app_name = root.attrib['APPNAME']
self.version = root.attrib['VERSION']
created = root.find(SCHEME_PREFIX+'CREATED')
self.created = created.text
self.uj_global_settings = []
for nvp in root.findall(SCHEME_PREFIX+'NVP'):
self.uj_global_settings.append({'NAME': nvp.attrib['NAME'], 'PLUGIN': nvp.attrib['PLUGIN'], 'TYPE': nvp.attrib['TYPE'], 'text': nvp.text})
#### Dynamic Data Items ###
dditems_element = root.find(SCHEME_PREFIX+'DYNAMICDATA')
self.dditems = []
for ddi in dditems_element.findall(SCHEME_PREFIX+'DDITEM'):
ddi_type = ddi.find(SCHEME_PREFIX+'SOURCE').attrib['TYPE']
self.dditems.append(TYPE_TO_CLASS_MAP[ddi_type](ddi))
#### Steps & Step-Groups ####
# stepgroups are not defined in the XML, so to construct a stepgroup, we need list of all the steps
steps_element = root.find(SCHEME_PREFIX+'STEPS')
stepgroups = []
stepgroup_steps = []
lead_step = None
last_step_stepgroup_id = -1
for step in steps_element.findall(SCHEME_PREFIX+'STEP'):
current_step = Step(step)
if last_step_stepgroup_id == -1: # adjust for starting element
lead_step = current_step
last_step_stepgroup_id = current_step.stepgroup_id
if current_step.stepgroup_id != last_step_stepgroup_id:
stepgroups.append(StepGroup(lead_step, stepgroup_steps))
lead_step = current_step
stepgroup_steps = [current_step]
last_step_stepgroup_id = current_step.id
else:
stepgroup_steps.append(current_step)
# finalize after last step
stepgroups.append(StepGroup(lead_step, stepgroup_steps))
self.stepgroups = stepgroups
def list_ddi_names(self):
return [z.name for z in self.dditems]
def find_ddi_by_name(self, name):
for ddi in self.dditems:
if ddi.name == name:
return ddi
return None
def find_ddis_by_attribute(self, attrib, value):
result = []
for ddi in self.dditems:
if getattr(ddi, attrib) == value:
result.append(ddi)
return result
def list_step_names(self):
return functools.reduce(lambda x,y: x+y, [z.list_step_names() for z in self.stepgroups])
def list_step_name_id_pairs(self):
stepgroup_name_id_pairs = [z.list_step_name_id_pairs() for z in self.stepgroups]
all_name_id_pairs = {}
for pairs in stepgroup_name_id_pairs:
all_name_id_pairs.update(pairs)
return all_name_id_pairs
def find_step_by_name(self, name):
for stepgroup in self.stepgroups:
match = stepgroup.find_steps_by_attribute('name', name)
if len(match):
return match[0]
return None
def find_step_by_id(self, id_):
for stepgroup in self.stepgroups:
match = stepgroup.find_steps_by_attribute('id', id_)
if len(match):
return match[0]
return None
def find_steps_by_attribute(self, attrib, value):
result = []
for stepgroup in self.stepgroups:
result.extend(stepgroup.find_steps_by_attribute(attrib, value))
return result
def find_steps_by_ddi_reference(self, ddi_name):
result = []
for stepgroup in self.stepgroups:
result.extend(stepgroup.find_steps_by_ddi_reference(ddi_name))
return result
def replace_ddi_references(self, old_name, new_name):
steps_with_references = self.find_steps_by_ddi_reference(old_name)
for step in steps_with_references:
step.replace_ddi(old_name, new_name)
def rename_ddi(self, old_name, new_name):
if new_name not in self.list_ddi_names():
target_ddi = self.find_ddi_by_name(old_name)
target_ddi.rename(new_name)
self.replace_ddi_references(old_name, new_name)
else:
raise DDINameException('new DDI name - ', new_name, ' already exists')
def rename_step(self, old_name, new_name):
if new_name not in self.list_step_names():
target_step = self.find_step_by_name(old_name)
target_step.rename(new_name)
else:
raise StepNameException('new step name - ', new_name, ' already exists')
def list_stepgroup_names(self):
return [z.name for z in self.stepgroups]
def tree_output(self):
result = ''
for stepgroup in self.stepgroups:
result += stepgroup.tree_output()
if stepgroup.lead_step.flow_control_element:
result = result[:-1]
destination_names = []
for destination in [ z for z in stepgroup.lead_step.flow_items if z['name'] == 'DESTINATIONSTEP' ]:
try:
name = self.find_step_by_id(int(destination['value'])).name
except (ValueError, AttributeError):
name = destination['value']
destination_names.append(name)
result += ' -->> ' + '|'.join(destination_names) +'\n'
return result
def find_stepgroup_by_id(self, id_):
groups_wiht_id = [ z for z in self.stepgroups if z.id == id_ ]
if len(groups_wiht_id) != 1:
return None
return groups_wiht_id[0]
def find_stepgroup_by_step_id(self, id_):
target_step = self.find_step_by_id(id_)
if target_step == None:
return None
return self.find_stepgroup_by_id(target_step.stepgroup_id), target_step
def promote_step_to_lead(self, id_):
# lead_to_be = self.find_step_by_id(id_)
target_stepgroup, lead_to_be = self.find_stepgroup_by_step_id(id_)
target_stepgroup.promote(lead_to_be)
def change_uj_name(self, new_name):
self.name = new_name
# self.root.set('NAME', new_name)
def xml(self):
ET.register_namespace('', SCHEME_PREFIX[1:-1])
root = ET.Element("USERJOURNEY", {'APPNAME': self.app_name, 'NAME': self.name, 'VALID': 'true', 'VERSION': self.version})
root.set('xmlns', "http://www.reflective.com")
root.set('xmlns:xsi', "http://www.w3.org/2001/XMLSchema-instance")
root.set('xsi:schemaLocation', "http://www.reflective.com stschema.xsd")
description = ET.SubElement(root, 'DESCRIPTION')
plugin = ET.SubElement(root, 'PLUGIN', {'ID': '1'})
responseprocessor = ET.SubElement(root, 'RESPONSEPROCESSOR')
created = ET.SubElement(root, 'CREATED')
created.text = self.created
for nvp in self.uj_global_settings:
text = nvp['text']
attributes = {key: value for key, value in nvp.items() if key is not 'text'}
new_nvp = ET.SubElement(root, 'NVP', attributes)
new_nvp.text = text
dynamic_data_element = ET.SubElement(root, 'DYNAMICDATA')
for ddi in self.dditems:
dynamic_data_element.append(ddi.xml())
steps_element = ET.SubElement(root, 'STEPS')
for step in [step for sg in self.stepgroups for step in sg.steps]:
st = step.xml()
steps_element.append(step.xml())
rough_string = ET.tostring(root, 'utf-8')
reparsed = minidom.parseString(rough_string)
return reparsed.toprettyxml(indent='\t').replace('<?xml version="1.0" ?>\n', self.first_import_line)
def export_uj(self, filename):
with open(filename, 'w') as xml_file:
xml_file.write(self.xml())
def push_stepgroup_changes_to_XML(self):
new_step_list = list(itertools.chain(*[z.steps for z in self.stepgroups]))
new_step_list = [z.element for z in new_step_list]
steps_element = self.root.find(SCHEME_PREFIX+'STEPS')
container = self.root.find(SCHEME_PREFIX+'STEPS')
if len(new_step_list):
container[:] = new_step_list
else:
if container != None:
self.root.remove(steps_element)
def write_to_file(self, file_name):
self.push_stepgroup_changes_to_XML()
if not len(self.dditems):
self.root.remove(self.dditems_element)
with open(file_name, 'w') as xml_file:
xml_file.write(self.raw.split('\n')[0] + '\n' + ET.tostring(self.root).decode("utf-8"))
def delete_step_by_id(self, id_):
# target_step = self.find_step_by_id(id_)
target_stepgroup, target_step = self.find_stepgroup_by_step_id(id_)
# print('group len', len(target_stepgroup.steps))
if len(target_stepgroup.steps) == 1:
self.stepgroups.remove(target_stepgroup)
else:
target_stepgroup.delete_step(target_step)
def delete_ddi(self, ddi_name):
target_ddi = self.find_ddi_by_name(ddi_name)
# self.dditems_element.remove(target_ddi.element)
self.dditems.remove(target_ddi)