Skip to content

Commit 704ad48

Browse files
committed
add dev command to import answers from all_answers_data.csv
This is mostly so you can take the answers from a live system and import them into a dev system for testing purposes. It's not very robust and shouldn't be used for live import.
1 parent 4101007 commit 704ad48

File tree

1 file changed

+248
-0
lines changed

1 file changed

+248
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
import re
2+
3+
from django.contrib.auth.models import User
4+
from django.core.management.base import BaseCommand
5+
6+
import pandas as pd
7+
8+
from crowdsourcer.models import (
9+
MarkingSession,
10+
Option,
11+
PublicAuthority,
12+
Question,
13+
Response,
14+
ResponseType,
15+
)
16+
17+
YELLOW = "\033[33m"
18+
RED = "\033[31m"
19+
GREEN = "\033[32m"
20+
NOBOLD = "\033[0m"
21+
22+
23+
class Command(BaseCommand):
24+
help = "Add automatic points"
25+
26+
def add_arguments(self, parser):
27+
parser.add_argument(
28+
"-q", "--quiet", action="store_true", help="Silence debug text."
29+
)
30+
31+
parser.add_argument(
32+
"--session",
33+
action="store",
34+
required=True,
35+
help="Marking session to use questions with",
36+
)
37+
38+
parser.add_argument(
39+
"--file",
40+
action="store",
41+
required=True,
42+
help="CSV file containing the answers",
43+
)
44+
45+
parser.add_argument(
46+
"--commit", action="store_true", help="Commits changes to DB"
47+
)
48+
49+
def print_info(self, message, level=2, colour=None):
50+
if self.quiet and level > 1:
51+
return
52+
53+
if colour is not None:
54+
message = f"{colour}{message}{NOBOLD}"
55+
56+
self.stdout.write(message)
57+
58+
def get_answers(self, file):
59+
df = pd.read_csv(file)
60+
61+
return df
62+
63+
def handle(
64+
self,
65+
quiet: bool = False,
66+
commit: bool = False,
67+
file: str = "",
68+
session: str = "",
69+
*args,
70+
**kwargs,
71+
):
72+
self.quiet = quiet
73+
74+
self.print_info("Please do not use this for a live server", 1, colour=RED)
75+
76+
u, _ = User.objects.get_or_create(
77+
username="Auto_answer_script",
78+
)
79+
80+
try:
81+
rt = ResponseType.objects.get(type="Audit")
82+
except ResponseType.DoesNotExist:
83+
self.print_info("No such ResponseType Audit", 1, colour=RED)
84+
85+
try:
86+
ms = MarkingSession.objects.get(label=session)
87+
except MarkingSession.DoesNotExist:
88+
self.stderr.write(f"No such Marking Session {session}", 1, colour=RED)
89+
90+
answers = self.get_answers(file)
91+
92+
responses_added = 0
93+
responses_skipped = 0
94+
existing_responses = 0
95+
96+
for _, answer in answers.iterrows():
97+
if pd.isna(answer["question-number"]):
98+
self.print_info(
99+
f"Bad value for question number {answer['question-number']} in row {_}",
100+
colour=YELLOW,
101+
)
102+
responses_skipped += 1
103+
continue
104+
105+
add_response = False
106+
107+
if answer["section"] == "":
108+
self.print_info(
109+
f"Bad section ({answer['section']}) for question number {answer['question-number']} in row {_}",
110+
colour=YELLOW,
111+
)
112+
responses_skipped += 1
113+
continue
114+
115+
council_name = answer["council name"]
116+
council = PublicAuthority.objects.get(marking_session=ms, name=council_name)
117+
118+
q_parts = re.match(r"(\d+)([a-z]?)", answer["question-number"])
119+
q_args = {"number": q_parts.groups()[0]}
120+
if len(q_parts.groups()) == 2 and q_parts.groups()[1] != "":
121+
q_args["number_part"] = q_parts.groups()[1]
122+
123+
try:
124+
question = Question.objects.get(
125+
section__marking_session=ms,
126+
section__title=answer["section"],
127+
**q_args,
128+
)
129+
except Question.DoesNotExist:
130+
self.print_info(
131+
f"no matching question for {answer['section']}, {q_args}",
132+
1,
133+
colour=YELLOW,
134+
)
135+
responses_skipped += 1
136+
continue
137+
138+
desc = answer["answer"].strip()
139+
try:
140+
if question.question_type == "multiple_choice":
141+
opts = desc.split(",")
142+
options = []
143+
for o in opts:
144+
new_opt = Option.objects.get(question=question, description=o)
145+
options.append(new_opt)
146+
else:
147+
option = Option.objects.get(question=question, description=desc)
148+
except Option.DoesNotExist:
149+
self.print_info(
150+
f"no matching option for {question.number_and_part}, {answer['section']} - '{desc}'",
151+
colour=YELLOW,
152+
)
153+
responses_skipped += 1
154+
continue
155+
except Option.MultipleObjectsReturned:
156+
self.print_info(
157+
f"multiple matching option for {question.number_and_part}, {answer['section']} - '{desc}'",
158+
colour=YELLOW,
159+
)
160+
responses_skipped += 1
161+
continue
162+
163+
try:
164+
response = Response.objects.get(
165+
question=question, authority=council, response_type=rt
166+
)
167+
if question.question_type == "multiple_choice":
168+
if response.multi_option is not None:
169+
if option.id not in options:
170+
self.print_info(
171+
f"existing response does not contain expected response for {question.number_and_part}, {answer['section']}, {council.name}",
172+
colour=YELLOW,
173+
)
174+
else:
175+
if response.option != option:
176+
self.print_info(
177+
f"different existing response for {question.number_and_part}, {answer['section']}, {council.name}",
178+
1,
179+
colour=YELLOW,
180+
)
181+
self.print_info(
182+
f"response exists for {council.name} for {question.number_and_part}, {question.section.title}",
183+
colour=YELLOW,
184+
)
185+
existing_responses += 1
186+
except Response.DoesNotExist:
187+
add_response = True
188+
except Response.MultipleObjectsReturned:
189+
self.print_info(
190+
f"multiple responses for {council.name} for {question.number_and_part}, {question.section.title}",
191+
colour=YELLOW,
192+
)
193+
responses_skipped += 1
194+
except ValueError:
195+
self.print_info(
196+
f"problem with response for {council.name} for {question.number_and_part}, {question.section.title}",
197+
colour=YELLOW,
198+
)
199+
responses_skipped += 1
200+
201+
if add_response:
202+
responses_added += 1
203+
self.print_info(
204+
f"creating response for {council.name} for {question.number_and_part}, {question.section.title}",
205+
colour=GREEN,
206+
)
207+
if commit:
208+
if question.question_type == "multiple_choice":
209+
r = Response.objects.create(
210+
user=u,
211+
question=question,
212+
authority=council,
213+
response_type=rt,
214+
private_notes="Automatically assigned mark",
215+
)
216+
for o in options:
217+
r.multi_option.add(o.id)
218+
else:
219+
response_opts = {
220+
"user": u,
221+
"question": question,
222+
"authority": council,
223+
"response_type": rt,
224+
"option": option,
225+
"private_notes": "Automatically imported mark",
226+
}
227+
if pd.isna(answer["page_number"]) is not False:
228+
response_opts["page_number"] = answer["page_number"]
229+
if pd.isna(answer["evidence"]) is not False:
230+
response_opts["evidence"] = answer["evidence"]
231+
if pd.isna(answer["public_notes"]) is not False:
232+
response_opts["public_notes"] = answer["public_notes"]
233+
234+
r = Response.objects.create(**response_opts)
235+
if not commit:
236+
self.print_info(
237+
"call with --commit to commit changed to database", 1, colour=YELLOW
238+
)
239+
240+
if responses_skipped:
241+
colour = YELLOW
242+
else:
243+
colour = GREEN
244+
self.print_info(
245+
f"{responses_added} reponses added, {existing_responses} existing responses, {responses_skipped} responses with error",
246+
1,
247+
colour=colour,
248+
)

0 commit comments

Comments
 (0)