diff --git a/backend/django/core/forms.py b/backend/django/core/forms.py index b87148c3..5361c102 100644 --- a/backend/django/core/forms.py +++ b/backend/django/core/forms.py @@ -142,10 +142,13 @@ class Meta: fields = ["allow_coders_view_labels"] def __init__(self, *args, **kwargs): - percentage_irr = kwargs.pop('percentage_irr') + percentage_irr = kwargs.pop("percentage_irr") super(ProjectUpdateAdvancedForm, self).__init__(*args, **kwargs) if percentage_irr > 0: - self.fields['allow_coders_view_labels'].widget.attrs['disabled'] = 'disabled' + self.fields["allow_coders_view_labels"].widget.attrs[ + "disabled" + ] = "disabled" + class LabelForm(forms.ModelForm): class Meta: @@ -276,7 +279,6 @@ class Meta: allow_coders_view_labels = forms.BooleanField(initial=False, required=False) - def clean(self): use_active_learning = self.cleaned_data.get("use_active_learning") use_default_batch_size = self.cleaned_data.get("use_default_batch_size") diff --git a/backend/django/core/urls/api.py b/backend/django/core/urls/api.py index 516be63f..04b4ab77 100644 --- a/backend/django/core/urls/api.py +++ b/backend/django/core/urls/api.py @@ -40,7 +40,9 @@ ), re_path(r"^unassign_data/(?P\d+)/$", api_annotate.unassign_data), re_path(r"^skip_data/(?P\d+)/$", api_annotate.skip_data), - re_path(r"^verify_label/(?P\d+)/$", api_annotate.verify_label), + re_path( + r"^toggle_verify_label/(?P\d+)/$", api_annotate.toggle_verify_label + ), re_path( r"^enter_coding_page/(?P\d+)/$", api_annotate.enter_coding_page ), diff --git a/backend/django/core/views/api_annotate.py b/backend/django/core/views/api_annotate.py index 897075af..295c47b9 100644 --- a/backend/django/core/views/api_annotate.py +++ b/backend/django/core/views/api_annotate.py @@ -208,8 +208,8 @@ def unassign_data(request, data_pk): @api_view(["POST"]) @permission_classes((IsCoder,)) -def verify_label(request, data_pk): - """Take a data label that was not verified, and verify it. +def toggle_verify_label(request, data_pk): + """Either verify or unverify a data item. Args: request: The POST request @@ -227,15 +227,22 @@ def verify_label(request, data_pk): return Response(response) elif DataLabel.objects.filter(data=data).count() > 1: response["error"] = ( - "ERROR: This data has multiple labels. This shouldn't " - "be possible with unverified data as it is pre-labeled." + "ERROR: This data has multiple labels which only occurs for incomplete IRR data which cannot be verified." ) else: - VerifiedDataLabel.objects.create( - data_label=DataLabel.objects.get(data=data), - verified_timestamp=timezone.now(), - verified_by=request.user.profile, - ) + datalabel = DataLabel.objects.get(data=data) + # if it's verified, un-verify it. + if VerifiedDataLabel.objects.filter(data_label=datalabel).exists(): + VerifiedDataLabel.objects.filter( + data_label=datalabel, + ).delete() + else: + # the data is not verified so we verify it + VerifiedDataLabel.objects.create( + data_label=DataLabel.objects.get(data=data), + verified_timestamp=timezone.now(), + verified_by=request.user.profile, + ) return Response(response) @@ -1095,13 +1102,15 @@ def get_label_history(request, project_pk): labeled_data_df["pre_loaded"] = labeled_data_df["pre_loaded"].apply( lambda x: "Yes" if x else "No" ) - labeled_data_df["edit"] = "yes" + labeled_data_df["edit"] = labeled_data_df["verified"].apply( + lambda verified: "No" if verified == "Yes" else "Yes" + ) labeled_data_df["label"] = labeled_data_df["labelID"].apply( lambda x: label_dict[x] ) if len(irr_data_df) > 0: - irr_data_df["edit"] = "no" + irr_data_df["edit"] = "No" irr_data_df["label"] = irr_data_df["labelID"].apply(lambda x: label_dict[x]) irr_data_df["verified"] = ( "N/A (IRR)" # Technically resolved IRR is verified but perhaps not this user's specific label so just NA @@ -1116,9 +1125,9 @@ def get_label_history(request, project_pk): # merge the data info with the label info if len(all_labeled_stuff) > 0: data_df = data_df.merge(all_labeled_stuff, on=["id"], how="left") - data_df["edit"] = data_df["edit"].fillna("yes") + data_df["edit"] = data_df["edit"].fillna("Yes") else: - data_df["edit"] = "yes" + data_df["edit"] = "Yes" data_df["label"] = "" data_df["profile"] = "" data_df["timestamp"] = "" diff --git a/backend/django/core/views/frontend.py b/backend/django/core/views/frontend.py index c79e26ed..2bc756d9 100644 --- a/backend/django/core/views/frontend.py +++ b/backend/django/core/views/frontend.py @@ -325,7 +325,9 @@ def done(self, form_list, form_dict, **kwargs): proj_obj.percentage_irr = advanced_data["percentage_irr"] proj_obj.num_users_irr = advanced_data["num_users_irr"] proj_obj.classifier = advanced_data["classifier"] - proj_obj.allow_coders_view_labels = advanced_data["allow_coders_view_labels"] + proj_obj.allow_coders_view_labels = advanced_data[ + "allow_coders_view_labels" + ] # use the data dedup choice to set dedup property of metadata fields proj_obj.dedup_on = data.cleaned_data["dedup_on"] @@ -444,13 +446,12 @@ def form_valid(self, form): else: return self.render_to_response(context) + class ProjectUpdateAdvanced(LoginRequiredMixin, UserPassesTestMixin, UpdateView): model = Project form_class = ProjectUpdateAdvancedForm template_name = "projects/update/advanced.html" - permission_denied_message = ( - "You must be an Admin or Project Creator to access the Advanced Project Settings Update page." - ) + permission_denied_message = "You must be an Admin or Project Creator to access the Advanced Project Settings Update page." raise_exception = True def test_func(self): @@ -465,7 +466,7 @@ def get_form_kwargs(self): # pass the percentage_irr to the form kwargs = super().get_form_kwargs() project = self.get_object() - kwargs['percentage_irr'] = project.percentage_irr + kwargs["percentage_irr"] = project.percentage_irr return kwargs def form_valid(self, form): @@ -478,7 +479,6 @@ def form_valid(self, form): return self.render_to_response(context) - class ProjectUpdateData(LoginRequiredMixin, UserPassesTestMixin, UpdateView): model = Project form_class = DataUpdateWizardForm diff --git a/frontend/src/components/History/HistoryTable.jsx b/frontend/src/components/History/HistoryTable.jsx index 9803bd2b..4e62b77d 100644 --- a/frontend/src/components/History/HistoryTable.jsx +++ b/frontend/src/components/History/HistoryTable.jsx @@ -92,7 +92,7 @@ const HistoryTable = () => { const [shouldRefetch, setShouldRefetch] = useState(false); const { data: historyData, refetch: refetchHistory } = useHistory(page + 1, unlabeled, filters); - const { mutate: verifyLabel } = useVerifyLabel(); + const { mutate: toggleVerifyLabel } = useVerifyLabel(); const metadataColumnsAccessorKeys = []; if (historyData) { @@ -171,17 +171,30 @@ const HistoryTable = () => { accessorKey: "verified", cell: (info) => { if (info.getValue() == "Yes") { - return (

Yes

); + return ( +
+

Yes

+ +
+ ); } else if (info.getValue() != "No") { return (

{info.getValue()}

); } else { return ( - +
+

No

+ +
); } }, @@ -195,7 +208,13 @@ const HistoryTable = () => { getExpandedRowModel: getExpandedRowModel(), getFilteredRowModel: getFilteredRowModel(), getPaginationRowModel: getPaginationRowModel(), - getRowCanExpand: () => true, + getRowCanExpand: (data) => { + if (data.original.edit === "Yes") { + return true; + } else { + return false; + } + }, getSortedRowModel: getSortedRowModel(), initialState: { pagination: { diff --git a/frontend/src/hooks/index.js b/frontend/src/hooks/index.js index b388f96b..9343d15c 100644 --- a/frontend/src/hooks/index.js +++ b/frontend/src/hooks/index.js @@ -5,4 +5,4 @@ export { default as useLabels } from "./useLabels"; export { default as useMetadataValue } from "./useMetadataValue"; export { default as useModifyLabel } from "./useModifyLabel"; export { default as useSuggestedLabels } from "./useSuggestedLabels"; -export { default as useVerifyLabel } from "./useVerifyLabel"; +export { default as useVerifyLabel } from "./useToggleVerifyLabel"; diff --git a/frontend/src/hooks/useVerifyLabel.js b/frontend/src/hooks/useToggleVerifyLabel.js similarity index 83% rename from frontend/src/hooks/useVerifyLabel.js rename to frontend/src/hooks/useToggleVerifyLabel.js index 5283b592..3f99a2e3 100644 --- a/frontend/src/hooks/useVerifyLabel.js +++ b/frontend/src/hooks/useToggleVerifyLabel.js @@ -6,7 +6,7 @@ import { postConfig } from "../utils/fetch_configs"; const useVerifyLabel = () => useMutation({ mutationFn: ({ dataID }) => - fetch(`/api/verify_label/${dataID}/`, postConfig({ dataID })), + fetch(`/api/toggle_verify_label/${dataID}/`, postConfig({ dataID })), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["history", PROJECT_ID] }); }