Skip to content

Commit 00dd7f0

Browse files
author
Nora Schneider
committed
Add import manual validation csv
1 parent 383dc70 commit 00dd7f0

File tree

6 files changed

+461
-2
lines changed

6 files changed

+461
-2
lines changed

signbank/dictionary/csv_import.py

+155-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626

2727
from .forms import CSVFileOnlyUpload, CSVUploadForm
2828
from .models import (Dataset, FieldChoice, Gloss, GlossTranslations, Language,
29-
ShareValidationAggregation, ValidationRecord)
29+
ManualValidationAggregation, ShareValidationAggregation, ValidationRecord)
3030
from .tasks import retrieve_videos_for_glosses
3131

3232
User = get_user_model()
@@ -721,3 +721,157 @@ def confirm_import_qualtrics_csv(request):
721721
"missing_gloss_question_pairs": missing_gloss_pk_question_pairs
722722
}
723723
)
724+
725+
726+
@login_required
727+
@permission_required("dictionary.import_csv")
728+
def import_manual_validation(request):
729+
"""
730+
Import ManualValidationAggregations from a CSV file
731+
"""
732+
# Make sure that the session variables are flushed before using this view.
733+
request.session.pop("group_row_map", None)
734+
request.session.pop("glosses", None)
735+
736+
if not request.method == "POST":
737+
# If request type is not POST, return to the original form.
738+
csv_form = CSVFileOnlyUpload()
739+
return render(request, "dictionary/import_manual_validation_csv.html",
740+
{"import_csv_form": csv_form}, )
741+
742+
form = CSVFileOnlyUpload(request.POST, request.FILES)
743+
744+
if not form.is_valid():
745+
# If form is not valid, set a error message and return to the original form.
746+
messages.add_message(request, messages.ERROR,
747+
_("The provided CSV-file does not meet the requirements "
748+
"or there is some other problem."))
749+
return render(request, "dictionary/import_manual_validation_csv.html",
750+
{"import_csv_form": form}, )
751+
752+
group_row_map = {}
753+
group_gloss_count = {}
754+
glosses = []
755+
required_headers = [
756+
"group",
757+
"idgloss",
758+
"yes",
759+
"no",
760+
"abstain or not sure",
761+
"comments"
762+
]
763+
try:
764+
validation_record_reader = csv.DictReader(
765+
codecs.iterdecode(form.cleaned_data["file"], "utf-8"),
766+
delimiter=",",
767+
quotechar='"'
768+
)
769+
for header in required_headers:
770+
if not header in validation_record_reader.fieldnames:
771+
request.session.pop("group_row_map", None)
772+
request.session.pop("glosses", None)
773+
# Set a message to be shown so that the user knows what is going on.
774+
messages.add_message(request, messages.ERROR,
775+
_(f"CSV is missing required column: {header}"))
776+
return render(request,
777+
"dictionary/import_manual_validation_csv.html",
778+
{"import_csv_form": CSVFileOnlyUpload()}, )
779+
780+
for row in validation_record_reader:
781+
if validation_record_reader.line_num == 1:
782+
continue
783+
try:
784+
group_row_map[row["group"]].append(row)
785+
group_gloss_count[row["group"]] += 1
786+
except KeyError:
787+
group_row_map[row["group"]] = [row]
788+
group_gloss_count[row["group"]] = 1
789+
glosses.append(row["idgloss"].split(":")[1])
790+
791+
except csv.Error as e:
792+
# Can't open file, remove session variables
793+
request.session.pop("group_row_map", None)
794+
request.session.pop("glosses", None)
795+
# Set a message to be shown so that the user knows what is going on.
796+
messages.add_message(request, messages.ERROR, _("Cannot open the file:" + str(e)))
797+
return render(request, "dictionary/import_manual_validation_csv.html",
798+
{"import_csv_form": CSVFileOnlyUpload()}, )
799+
except UnicodeDecodeError as e:
800+
# File is not UTF-8 encoded.
801+
messages.add_message(request, messages.ERROR, _("File must be UTF-8 encoded!"))
802+
return render(request, "dictionary/import_manual_validation_csv.html",
803+
{"import_csv_form": CSVFileOnlyUpload()}, )
804+
805+
# Store dataset's id and the list of glosses to be added in session.
806+
request.session["group_row_map"] = group_row_map
807+
request.session["glosses"] = list(set(glosses))
808+
809+
return render(request, "dictionary/import_manual_validation_csv_confirmation.html",
810+
{"group_row_map": group_row_map, "group_gloss_count": group_gloss_count})
811+
812+
813+
@login_required
814+
@permission_required("dictionary.import_csv")
815+
@transaction.atomic()
816+
def confirm_import_manual_validation(request):
817+
"""This view adds the data to database if the user confirms the action"""
818+
if not request.method == "POST":
819+
# If request method is not POST, redirect to the import form
820+
return HttpResponseRedirect(reverse("dictionary:import_manual_validation_csv"))
821+
822+
if "cancel" in request.POST:
823+
# If user cancels adding data, flush session variables
824+
request.session.pop("group_row_map", None)
825+
request.session.pop("glosses", None)
826+
# Set a message to be shown so that the user knows what is going on.
827+
messages.add_message(request, messages.WARNING, _("Cancelled adding CSV data."))
828+
return HttpResponseRedirect(reverse("dictionary:import_manual_validation_csv"))
829+
830+
if not "confirm" in request.POST:
831+
return HttpResponseRedirect(reverse("dictionary:import_manual_validation_csv"))
832+
833+
manual_validation_aggregations = []
834+
missing_glosses = []
835+
836+
if "group_row_map" and "glosses" in request.session:
837+
gloss_pk_list = request.session["glosses"]
838+
gloss_dict = Gloss.objects.in_bulk(gloss_pk_list)
839+
840+
gloss_row_map = request.session["group_row_map"]
841+
842+
# Go through csv data
843+
for group, rows in gloss_row_map.items():
844+
for row in rows:
845+
gloss = gloss_dict.get(int(row["idgloss"].split(":")[1]))
846+
if not gloss:
847+
missing_glosses.append((group, row["idgloss"]))
848+
continue
849+
sign_seen_yes = row["yes"]
850+
sign_seen_no = row["no"]
851+
sign_seen_not_sure = row["abstain or not sure"]
852+
comments = row["comments"]
853+
manual_validation_aggregations.append(ManualValidationAggregation(
854+
gloss=gloss,
855+
group=group,
856+
sign_seen_yes=int(sign_seen_yes) if sign_seen_yes else 0,
857+
sign_seen_no=int(sign_seen_no) if sign_seen_no else 0,
858+
sign_seen_not_sure=int(sign_seen_not_sure) if sign_seen_not_sure else 0,
859+
comments=comments
860+
))
861+
862+
ManualValidationAggregation.objects.bulk_create(manual_validation_aggregations)
863+
864+
del request.session["group_row_map"]
865+
del request.session["glosses"]
866+
867+
# Set a message to be shown so that the user knows what is going on.
868+
messages.add_message(request, messages.SUCCESS,
869+
_("ValidationRecords were added succesfully."))
870+
return render(
871+
request, "dictionary/import_manual_validation_csv_confirmation.html",
872+
{
873+
"manual_validation_aggregations": manual_validation_aggregations,
874+
"manual_validation_aggregations_count": len(manual_validation_aggregations),
875+
"missing_glosses": missing_glosses
876+
}
877+
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{% extends 'baselayout.html' %}
2+
{% load stylesheet %}
3+
{% load bootstrap3 %}
4+
{% load i18n %}
5+
{% block bootstrap3_title %}{% blocktrans %}Import Manual Validation CSV{% endblocktrans %} | {% endblock %}
6+
7+
{% block content %}
8+
{% if perms.dictionary.import_csv %}
9+
<div id="import-csv">
10+
{# Translators: #}
11+
<h3>{% blocktrans %}Import Manual Validation CSV{% endblocktrans %}:</h3>
12+
13+
<ul>
14+
<li>{% blocktrans %}CSV-file should contain the following columns:{% endblocktrans %}</li>
15+
<ul>
16+
<li>{% blocktrans %}group{% endblocktrans %}</li>
17+
<li>{% blocktrans %}idgloss{% endblocktrans %}</li>
18+
<li>{% blocktrans %}yes{% endblocktrans %}</li>
19+
<li>{% blocktrans %}no{% endblocktrans %}</li>
20+
<li>{% blocktrans %}abstain or not sure{% endblocktrans %}</li>
21+
<li>{% blocktrans %}comments{% endblocktrans %}</li>
22+
</ul>
23+
<li>{% blocktrans %}Column headers should be all lower case{% endblocktrans %}</li>
24+
<li>{% blocktrans %}Any further columns will be ignored during import{% endblocktrans %}</li>
25+
<li>{% blocktrans %}The CSV-file's character set needs to be UTF-8. When exporting the CSV-file,
26+
make sure that you select UTF-8 charset.{% endblocktrans %}</li>
27+
</ul>
28+
29+
<form enctype="multipart/form-data" action='{% url "dictionary:import_manual_validation_csv" %}' method='post'>
30+
{% csrf_token %}
31+
{% bootstrap_field import_csv_form.file %}
32+
<br>
33+
<input class='btn btn-primary' type='submit' value='{% blocktrans %}Import manual validation aggregations from CSV{% endblocktrans %}'>
34+
</form>
35+
</div>
36+
{% else %}
37+
{# Translators: Message that appears if user doesn't have proper user rights to view this page. #}
38+
<p>{% blocktrans %}You do not have sufficient user rights to view this page.{% endblocktrans %}</p>
39+
{% endif %}
40+
{% endblock %}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
{% extends 'baselayout.html' %}
2+
{% load stylesheet %}
3+
{% load bootstrap3 %}
4+
{% load i18n %}
5+
{% block bootstrap3_title %}{% blocktrans %}Confirm Import Manual Validation CSV{% endblocktrans %} | {% endblock %}
6+
7+
{% block content %}
8+
{% if perms.dictionary.import_csv %}
9+
<div>
10+
{% if group_row_map %}
11+
<h3>{% blocktrans %}ManualValidationAggregations to be added{% endblocktrans %}</h3>
12+
{% for group, gloss_count in group_gloss_count.items %}
13+
<p>{% blocktrans %}Group: {{ group }} - {{ gloss_count }} glosses{% endblocktrans %}</p>
14+
{% endfor %}
15+
<form action='{% url "dictionary:confirm_import_manual_validation_csv" %}' method='post'>
16+
{% csrf_token %}
17+
<input class='btn btn-primary' name='confirm' type='submit' value='{% blocktrans %}Confirm{% endblocktrans %}'>
18+
<input class='btn btn-primary' name='cancel' type='submit' value='{% blocktrans %}Cancel{% endblocktrans %}'>
19+
</form>
20+
{% endif %}
21+
</div>
22+
<div>
23+
{% if manual_validation_aggregations %}
24+
<div>
25+
<h3>{% blocktrans %}Manual validation aggregations were successfully added{% endblocktrans %}</h3>
26+
<p>{% blocktrans %}Added total of {{ manual_validation_aggregations_count }} validation aggregations.{% endblocktrans %}</p>
27+
</div>
28+
{% if missing_glosses %}
29+
<div>
30+
<h4>{% blocktrans %}Could not find some glosses{% endblocktrans %}</h4>
31+
{% for group, idgloss in missing_glosses %}
32+
<p>{% blocktrans %}Group: {{ group }} - Gloss: {{ idgloss }}{% endblocktrans %}</p>
33+
{% endfor %}
34+
</div>
35+
{% endif %}
36+
<p><a href="{% url "dictionary:import_manual_validation_csv" %}">{% blocktrans %}Return to the form{% endblocktrans %}</a></p>
37+
{% endif %}
38+
</div>
39+
{% endif %}
40+
{% endblock %}

0 commit comments

Comments
 (0)