Skip to content

Commit

Permalink
Merge pull request #219 from tnc-ca-geo/adding-reviewed-field
Browse files Browse the repository at this point in the history
Updating Filters to Use New "Reviewed" Field
  • Loading branch information
jue-henry authored Apr 26, 2024
2 parents 3f43b3e + 6205ae1 commit e343a10
Show file tree
Hide file tree
Showing 10 changed files with 68 additions and 49 deletions.
2 changes: 1 addition & 1 deletion src/api/buildQuery.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ const imageFields = `
comments {
${imageCommentFields}
}
reviewed
`;

const pageInfoFields = `
Expand Down Expand Up @@ -123,7 +124,6 @@ const viewFields = `
addedStart
addedEnd
reviewed
notReviewed
custom
}
`;
Expand Down
18 changes: 17 additions & 1 deletion src/app/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,10 @@ export const getRandomColor = () => {
const green = Math.floor(Math.random() * (255 - 0) + 0);
const blue = Math.floor(Math.random() * (255 - 0) + 0);

const integer = ((Math.round(red) & 0xff) << 16) + ((Math.round(green) & 0xff) << 8) + (Math.round(blue) & 0xff);
const integer =
((Math.round(red) & 0xff) << 16) +
((Math.round(green) & 0xff) << 8) +
(Math.round(blue) & 0xff);

const string = integer.toString(16).toUpperCase();
return '000000'.substring(string.length) + string;
Expand All @@ -154,3 +157,16 @@ export const normalizeErrors = (error, code) => {
}
return errs;
};

export const isImageReviewed = (image) => {
// images are considered reviewed if they:
// have objects,
// all objects are locked,
// AND not all labels of all objects have been invalidated
const hasObjs = image.objects.length > 0;
const hasUnlockedObjs = image.objects.some((obj) => obj.locked === false);
const hasAllInvalidatedLabels = !image.objects.some((obj) =>
obj.labels.some((lbl) => !lbl.validation || lbl.validation.validated),
);
return hasObjs && !hasUnlockedObjs && !hasAllInvalidatedLabels;
};
4 changes: 2 additions & 2 deletions src/config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const API_URLS = {
development: 'https://7bs5tmbd8e.execute-api.us-west-2.amazonaws.com/dev/external',
staging: 'https://7bs5tmbd8e.execute-api.us-west-2.amazonaws.com/dev/external',
development: 'https://ko0yratczi.execute-api.us-west-2.amazonaws.com/dev/external',
staging: 'https://ko0yratczi.execute-api.us-west-2.amazonaws.com/dev/external',
production: 'https://4634jgo56f.execute-api.us-west-2.amazonaws.com/prod/external/',
};

Expand Down
39 changes: 25 additions & 14 deletions src/features/filters/ReviewFilter.jsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import { useDispatch, useSelector } from 'react-redux'
import { reviewedFilterToggled, selectReviewed, selectNotReviewed } from './filtersSlice';
import { reviewedFilterToggled, selectReviewed } from './filtersSlice';
import Accordion from '../../components/Accordion';
import Checkbox from '../../components/Checkbox';
import { CheckboxLabel } from '../../components/CheckboxLabel';
Expand All @@ -9,29 +9,40 @@ import { CheckboxWrapper } from '../../components/CheckboxWrapper';

const ReviewFilter = () => {
const dispatch = useDispatch();
const reviewed = useSelector(selectReviewed);
const notReviewed = useSelector(selectNotReviewed);
const includeReviewed = reviewed === null || reviewed;
const includeNotReviewed = notReviewed === null || notReviewed;
let reviewed = useSelector(selectReviewed);

/*
Reviewed can have 3 states: null, true, false.
1. Null: both reviewed and not reviewed images are included.
2. True: Only reviewed images are included.
3. False: only not-reviewed images are included.
Using these 3 states we can then manage both the filter state and the UI state.
*/
const handleCheckboxChange = (e) => {
const objFilter = e.target.dataset.objFilter;
dispatch(reviewedFilterToggled({type: objFilter}));
if (objFilter === 'reviewed') {
reviewed = reviewed === null ? false : reviewed === true ? false : null;
}
else {
reviewed = reviewed === null ? true : reviewed === true ? null : true;
}

dispatch(reviewedFilterToggled({ reviewed }));
};

return (
<Accordion label='Review' expandedDefault={false}>
<CheckboxWrapper>
<label>
<Checkbox
checked={includeReviewed}
active={includeReviewed}
checked={reviewed ?? true}
active={reviewed ?? true}
data-obj-filter={'reviewed'}
onChange={handleCheckboxChange}
/>
<CheckboxLabel
checked={includeReviewed}
active={includeReviewed}
checked={reviewed ?? true}
active={reviewed ?? true}
>
reviewed images
</CheckboxLabel>
Expand All @@ -40,14 +51,14 @@ const ReviewFilter = () => {
<CheckboxWrapper>
<label>
<Checkbox
checked={includeNotReviewed}
active={includeNotReviewed}
checked={!reviewed ?? true}
active={!reviewed ?? true}
data-obj-filter={'notReviewed'}
onChange={handleCheckboxChange}
/>
<CheckboxLabel
checked={includeNotReviewed}
active={includeNotReviewed}
checked={!reviewed ?? true }
active={!reviewed ?? true}
>
not-reviewed images
</CheckboxLabel>
Expand Down
11 changes: 1 addition & 10 deletions src/features/filters/filtersSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ const initialState = {
addedStart: null,
addedEnd: null,
reviewed: null,
notReviewed: null,
custom: null,
},
};
Expand Down Expand Up @@ -60,13 +59,7 @@ export const filtersSlice = createSlice({
},

reviewedFilterToggled: (state, { payload }) => {
const reviewedFilter = state.activeFilters[payload.type];
state.activeFilters[payload.type] = reviewedFilter === null ? false : null;
},

notReviewedFilterToggled: (state, { payload }) => {
const notReviewedFilter = state.activeFilters[payload.type];
state.activeFilters[notReviewedFilter] = notReviewedFilter === null ? false : null;
state.activeFilters['reviewed'] = payload.reviewed;
},

customFilterChanged: (state, { payload }) => {
Expand Down Expand Up @@ -190,7 +183,6 @@ export const {
getModelsSuccess,
checkboxFilterToggled,
reviewedFilterToggled,
notReviewedFilterToggled,
customFilterChanged,
dateFilterChanged,
setActiveFilters,
Expand All @@ -205,7 +197,6 @@ export const selectAvailCameraFilters = (state) => state.filters.availFilters.ca
export const selectAvailDeploymentFilters = (state) => state.filters.availFilters.deployments;
export const selectAvailLabelFilters = (state) => state.filters.availFilters.labels;
export const selectReviewed = (state) => state.filters.activeFilters.reviewed;
export const selectNotReviewed = (state) => state.filters.activeFilters.notReviewed;
export const selectCustomFilter = (state) => state.filters.activeFilters.custom;
export const selectDateAddedFilter = (state) => ({
start: state.filters.activeFilters.addedStart,
Expand Down
16 changes: 3 additions & 13 deletions src/features/images/ImagesTable.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -362,25 +362,15 @@ function makeRows(workingImages, focusIndex, selectedImageIndices) {
const dtAdded = DateTime.fromISO(img.dateAdded).toLocaleString(DateTime.DATE_SHORT);

// reviewed columns
const hasObjs = img.objects.length > 0;
const hasUnlockedObjs = img.objects.some((obj) => obj.locked === false);
const hasAllInvalidatedLabels = !img.objects.some((obj) =>
obj.labels.some((lbl) => !lbl.validation || lbl.validation.validated),
);
const reviewed =
hasObjs && !hasUnlockedObjs && !hasAllInvalidatedLabels ? (
<ReviewedIcon reviewed={true} />
) : (
<ReviewedIcon reviewed={false} />
);
const reviewedIcon = <ReviewedIcon reviewed={img.reviewed} />;

return {
thumbnail,
labelPills,
dtOriginal,
dtAdded,
reviewed,
...img,
reviewedIcon,
...img
};
});
}
Expand Down
8 changes: 4 additions & 4 deletions src/features/images/config.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export const columnConfig = [
},
{
Header: 'Reviewed',
accessor: 'reviewed',
accessor: 'reviewedIcon',
disableSortBy: true,
width: '50'
},
Expand All @@ -53,9 +53,9 @@ export const tableBreakpoints = [
];

export const columnsToHideMap = {
'loupeOpen': ['dtOriginal', 'dtAdded', 'reviewed', 'cameraId', 'deploymentName'],
'xxs': ['dtAdded', 'deploymentName', 'cameraId', 'reviewed', 'dtOriginal'],
'xs': ['dtAdded', 'deploymentName', 'cameraId', 'reviewed'],
'loupeOpen': ['dtOriginal', 'dtAdded', 'reviewedIcon', 'cameraId', 'deploymentName'],
'xxs': ['dtAdded', 'deploymentName', 'cameraId', 'reviewedIcon', 'dtOriginal'],
'xs': ['dtAdded', 'deploymentName', 'cameraId', 'reviewedIcon'],
'sm': ['dtAdded', 'deploymentName', 'cameraId'],
'md': ['dtAdded', 'deploymentName'],
'lg': ['dtAdded']
Expand Down
1 change: 0 additions & 1 deletion src/features/images/imagesSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -300,7 +300,6 @@ export const fetchImageContext = (imgId) => {
deployments: [res.image.deploymentId],
labels: null,
reviewed: null,
notReviewed: null,
custom: null,
};
dispatch(setActiveFilters(filters));
Expand Down
2 changes: 0 additions & 2 deletions src/features/projects/projectsMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import {
checkboxFilterToggled,
dateFilterChanged,
reviewedFilterToggled,
notReviewedFilterToggled,
selectActiveFilters,
customFilterChanged,
checkboxOnlyButtonClicked,
Expand Down Expand Up @@ -72,7 +71,6 @@ export const diffFilters = (store) => (next) => (action) => {
checkboxOnlyButtonClicked.match(action) ||
dateFilterChanged.match(action) ||
reviewedFilterToggled.match(action) ||
notReviewedFilterToggled.match(action) ||
customFilterChanged.match(action) ||
setSelectedProjAndView.match(action) ||
editDeploymentsSuccess.match(action) ||
Expand Down
16 changes: 15 additions & 1 deletion src/features/review/reviewSlice.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { createSlice, createAction, createSelector } from '@reduxjs/toolkit';
import { Auth } from 'aws-amplify';
import { call } from '../../api';
import { findImage, findObject, findLabel } from '../../app/utils';
import { findImage, findObject, findLabel, isImageReviewed } from '../../app/utils';
import { toggleOpenLoupe } from '../loupe/loupeSlice';
import { getImagesSuccess, clearImages, deleteImagesSuccess } from '../images/imagesSlice';

Expand Down Expand Up @@ -50,13 +50,16 @@ export const reviewSlice = createSlice({
const { imgId, objId } = payload;
const object = findObject(state.workingImages, imgId, objId);
object.bbox = payload.bbox;
const image = findImage(state.workingImages, imgId);
image.reviewed = isImageReviewed(image);
},

objectsRemoved: (state, { payload }) => {
for (const obj of payload.objects) {
const image = findImage(state.workingImages, obj.imgId);
const objectIndex = image.objects.findIndex((o) => o._id === obj.objId);
image.objects.splice(objectIndex, 1);
image.reviewed = isImageReviewed(image);
}
},

Expand All @@ -70,6 +73,7 @@ export const reviewSlice = createSlice({
const object = image.objects.find((obj) => obj._id === objId);
object.labels.unshift(newLabel);
}
image.reviewed = isImageReviewed(image);
}
state.lastAction = 'labels-added';
state.lastCategoryApplied = payload.labels[0].labelId;
Expand All @@ -88,14 +92,19 @@ export const reviewSlice = createSlice({
const objectIndex = image.objects.findIndex((obj) => obj._id === objId);
image.objects.splice(objectIndex, 1);
}

image.reviewed = isImageReviewed(image);
}
},

labelsValidated: (state, { payload }) => {
payload.labels.forEach(({ userId, imgId, objId, lblId, validated }) => {
const label = findLabel(state.workingImages, imgId, objId, lblId);
label.validation = { validated, userId };
const image = findImage(state.workingImages, imgId);
image.reviewed = isImageReviewed(image);
});

state.lastAction = payload.labels[0].validated ? 'labels-validated' : 'labels-invalidated';
},

Expand All @@ -106,13 +115,17 @@ export const reviewSlice = createSlice({
object.locked = oldLocked;
const label = findLabel(state.workingImages, imgId, objId, lblId);
label.validation = oldValidation;
const image = findImage(state.workingImages, imgId);
image.reviewed = isImageReviewed(image);
});
},

objectsLocked: (state, { payload }) => {
payload.objects.forEach(({ imgId, objId, locked }) => {
const object = findObject(state.workingImages, imgId, objId);
object.locked = locked;
const image = findImage(state.workingImages, imgId);
image.reviewed = isImageReviewed(image);
});
},

Expand All @@ -121,6 +134,7 @@ export const reviewSlice = createSlice({
if (img.newObject) {
const image = findImage(state.workingImages, img.imgId);
image.objects.push(img.newObject);
image.reviewed = isImageReviewed(image);
}
}
state.lastAction = 'marked-empty';
Expand Down

0 comments on commit e343a10

Please sign in to comment.