|
26 | 26 |
|
27 | 27 | from .forms import CSVFileOnlyUpload, CSVUploadForm
|
28 | 28 | from .models import (Dataset, FieldChoice, Gloss, GlossTranslations, Language,
|
29 |
| - ShareValidationAggregation, ValidationRecord) |
| 29 | + ManualValidationAggregation, ShareValidationAggregation, ValidationRecord) |
30 | 30 | from .tasks import retrieve_videos_for_glosses
|
31 | 31 |
|
32 | 32 | User = get_user_model()
|
@@ -721,3 +721,157 @@ def confirm_import_qualtrics_csv(request):
|
721 | 721 | "missing_gloss_question_pairs": missing_gloss_pk_question_pairs
|
722 | 722 | }
|
723 | 723 | )
|
| 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 | + ) |
0 commit comments