forked from Submitty/RainbowGrades
-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathparsexml.py
147 lines (126 loc) · 6.44 KB
/
parsexml.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
#!/usr/bin/env python3
import csv
import xml.etree.ElementTree as ET
import sys
import os.path
class QuestionData:
final_answer = ""
final_answer_time = 0
first_answer = ""
attempts = 0
first_answer_time = 0
def __init__(self,final_answer,final_answer_time,attempts,first_answer,first_answer_time):
self.final_answer = final_answer
self.final_answer_time = final_answer_time
self.first_answer = first_answer
self.first_answer_time = first_answer_time
self.attempts = attempts
def xml_to_csv(xml_filename):
"""
Parses .xml files generated by newer versions of iClicker software in SessionData
A CSV file will be written to the same path as the XML file, so it is important that any path, be it
absolute or relative, is included in the xml_filename argument. The CSV file is not a perfect replica of
older (i.e. iClicker 6) CSV files, but is our best approximation at this time. It should be enough for
Rainbow Grades to function properly.
"""
csv_filename = xml_filename[:-3] + "csv"
try:
with open(xml_filename,"r") as readfile:
tree = ET.parse(xml_filename)
root = tree.getroot()
questions_in_order = []
start_times = {}
stop_times = {}
user_question_data = {}
for child in root:
if child.tag == "p": # This is a polling tag
question = child.attrib["qn"]
start_times[question] = child.attrib["strt"]
stop_times[question] = child.attrib["stp"]
questions_in_order.append(question)
question_votes = {}
for qchild in child:
if qchild.tag == "v": # This is a voting tag
clicker_id = qchild.attrib["id"]
if clicker_id not in user_question_data:
user_question_data[clicker_id] = {}
user_question_data[clicker_id][question] = {}
if "fans" in qchild.attrib:
user_question_data[clicker_id][question] = QuestionData(qchild.attrib["ans"],
qchild.attrib["fanst"],
qchild.attrib["att"],
qchild.attrib["fans"],
qchild.attrib["tm"])
question_votes[clicker_id] = qchild.attrib["ans"]
with open(csv_filename, 'w') as writefile:
csvwriter = csv.writer(writefile) # Need to change dialect to be iclicker compliant
# Write the header
# Right now we don't have min reply/min correct in XML land, instead we have MinPart_S
next_row = ["Scoring"]
if "perf" in root.attrib:
performance = root.attrib["perf"]
else:
performance = -1
if "part" in root.attrib:
participation = root.attrib["part"]
else:
participation = 1
csvwriter.writerow(["Scoring", "Performance = " + performance,
"Participation = " + participation, "Min Reply = 2",
"Min Correct = 0",
" "])
next_row = ["Question", " ", " "]
for i in range(len(questions_in_order)):
next_row = next_row + ["Question " + str(i + 1), "Score", "Final Answer Time", "Number of Attempts",
"First Response", "Time"]
csvwriter.writerow(next_row)
next_row = ["Start Time", " ", " "]
for question in questions_in_order:
next_row = next_row + [" " + start_times[question], " ", " ", " ", " ", " "]
csvwriter.writerow(next_row)
next_row = ["Stop Time", " ", " "]
first_stop = True
for question in questions_in_order:
if not first_stop:
next_row = next_row + [" " + stop_times[question], " ", " ", " ", " ", " "]
else:
next_row = next_row + [stop_times[question], " ", " ", " ", " ", " "]
first_stop = False
csvwriter.writerow(next_row)
next_row = ["Correct Answer", " ", " "]
first_stop = True
for question in questions_in_order:
if not first_stop:
next_row = next_row + [" ", " ", " ", " ", " ", " "]
else:
next_row = next_row + ["", " ", " ", " ", " ", " "]
first_stop = False
csvwriter.writerow(next_row)
for user in sorted(user_question_data.keys()):
next_row = [user, "", "0"]
for question in questions_in_order:
if question in user_question_data[user]:
qd = user_question_data[user][question]
next_row = next_row + [qd.final_answer, 0, qd.final_answer_time, qd.attempts,
qd.first_answer, qd.first_answer_time]
else:
next_row = next_row + ["", "", "", "", "", ""]
csvwriter.writerow(next_row)
except IOError as e:
print("File I/O error: {}".format(e))
exit(-1)
if __name__ == '__main__':
if len(sys.argv) != 2:
print("Correct usage is {} [file with iclicker {\"file\":...} entries]".format(sys.argv[0]))
exit(-1)
files = []
try:
with open(sys.argv[1]) as json_file:
for line in json_file:
# Extract just the filenames of the session data
files += [x.strip()[1:-1] for x in line.split("[")[1].split("]")[0].split(",")]
except IOError as e:
print("Error reading JSON excerpt: {}".format(e))
for filename in files:
if len(filename) >= 4 and filename[-4:] == ".xml":
xml_to_csv(filename)