Skip to content

Commit

Permalink
Merge pull request #328 from RTIInternational/dev_fix_double_loading
Browse files Browse the repository at this point in the history
Handle bug where the same card was appearing multiple times
  • Loading branch information
AstridKery authored Jun 10, 2024
2 parents 0824189 + e584824 commit 7ef4cbe
Show file tree
Hide file tree
Showing 5 changed files with 44 additions and 18 deletions.
10 changes: 7 additions & 3 deletions backend/django/core/utils/utils_annotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,14 +169,18 @@ def label_data(label, datum, profile, time):
irr_data = datum.irr_ind

with transaction.atomic():
DataLabel.objects.create(
dl, created = DataLabel.objects.get_or_create(
data=datum,
label=label,
profile=profile,
training_set=current_training_set,
time_to_label=time,
timestamp=timezone.now(),
)
# was not created because it already existed
if not created:
return
dl.time_to_label = time
dl.timestamp = timezone.now()
dl.save()
# There's a unique constraint on data/profile, so this is
# guaranteed to return one object
assignment = AssignedData.objects.filter(data=datum, profile=profile).get()
Expand Down
21 changes: 16 additions & 5 deletions backend/django/core/views/api_annotate.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from django.db import transaction
from django.db.models import Q
from django.utils import timezone
from psycopg2.errors import UniqueViolation
from rest_framework.decorators import api_view, permission_classes
from rest_framework.generics import ListAPIView
from rest_framework.permissions import AllowAny
Expand Down Expand Up @@ -316,7 +317,9 @@ def annotate_data(request, data_pk):
labeling_time = request.data["labeling_time"]

# if the coder has been un-assigned from the data
if not AssignedData.objects.filter(data=data, profile=profile).exists():
if (
not AssignedData.objects.filter(data=data, profile=profile).exists()
) or DataLabel.objects.filter(data=data, profile=profile).exists():
response["error"] = (
"ERROR: this card was no longer assigned. Either "
"your cards were un-assigned by an administrator or the label was clicked twice. "
Expand All @@ -339,10 +342,18 @@ def annotate_data(request, data_pk):
assignment = AssignedData.objects.get(data=data, profile=profile)
assignment.delete()
else:
label_data(label, data, profile, labeling_time)
if data.irr_ind:
# if it is reliability data, run processing step
process_irr_label(data, label)
try:
label_data(label, data, profile, labeling_time)
if data.irr_ind:
# if it is reliability data, run processing step
process_irr_label(data, label)
except UniqueViolation:
response["error"] = (
"ERROR: this card was no longer assigned. Either "
"your cards were un-assigned by an administrator or the label was clicked twice. "
"Please refresh the page to get new assigned items to annotate."
)
return Response(response)

# for all data, check if we need to refill queue
check_and_trigger_model(data, profile)
Expand Down
9 changes: 3 additions & 6 deletions frontend/src/actions/card.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,13 +70,12 @@ export const annotateCard = (dataID, labelID, num_cards_left, start_time, projec
dispatch(clearDeck());
return dispatch(setMessage(response.error));
} else {
dispatch(popCard());
dispatch(popCard(dataID));
if (is_admin) {
dispatch(getAdmin(projectID));
queryClient.invalidateQueries(["adminCounts", projectID]);
dispatch(getLabelCounts(projectID));
}
if (num_cards_left <= 1) dispatch(fetchCards(projectID));
}
});
};
Expand All @@ -101,8 +100,7 @@ export const unassignCard = (dataID, num_cards_left, is_admin, projectID) => {
dispatch(clearDeck());
return dispatch(setMessage(response.error));
} else {
dispatch(popCard());
if (num_cards_left <= 1) dispatch(fetchCards(projectID));
dispatch(popCard(dataID));
}
});
};
Expand All @@ -127,12 +125,11 @@ export const passCard = (dataID, num_cards_left, is_admin, projectID, message) =
dispatch(clearDeck());
return dispatch(setMessage(response.error));
} else {
dispatch(popCard());
dispatch(popCard(dataID));
if (is_admin) {
dispatch(getAdmin(projectID));
queryClient.invalidateQueries(["adminCounts", projectID]);
}
if (num_cards_left <= 1) dispatch(fetchCards(projectID));
}
});
};
Expand Down
8 changes: 5 additions & 3 deletions frontend/src/containers/card_container.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,11 @@ import DataCard, { PAGES } from "../components/DataCard/DataCard";
const PROJECT_ID = window.PROJECT_ID;

const CardContainer = (props) => {
if (props.cards.length == 0) {
props.fetchCards();
}
React.useEffect(() => {
if (props.cards.length === 0) {
props.fetchCards();
}
}, [props.cards.length]);
return (
props.cards !== undefined && props.cards.length > 0 ?
<DataCard
Expand Down
14 changes: 13 additions & 1 deletion frontend/src/reducers/card.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,26 @@ const initialState = {
};

const card = handleActions({
[POP_CARD]: (state) => {
[POP_CARD]: (state, action) => {
// if the card isn't the first item in the deck don't pop it off
// This handles double-clicking of Skip
if (state.cards[0].text.pk != action.payload) {
return state;
}

// Set the start time of the new top card to the current time
if (state.cards.length > 1) {
state.cards[1]['start_time'] = moment();
}
return update(state, { cards: { $splice: [[0, 1]] } } );
},
[PUSH_CARD]: (state, action) => {
// only push the card if it's not already in the stack - failsafe
for (let i = 0; i < state.cards.length; i++) {
if (state.cards[i].id == action.payload.id) {
return state;
}
}
// Set the start time of the new top card to the current time
if (state.cards.length > 0) {
state.cards[0]['start_time'] = moment();
Expand Down

0 comments on commit 7ef4cbe

Please sign in to comment.