Skip to content

Commit

Permalink
Merge pull request #8 from coastal-science/4-annotation-fixes-and-enh…
Browse files Browse the repository at this point in the history
…ancement

4 annotation fixes and enhancement
  • Loading branch information
fsfrazao authored May 20, 2022
2 parents b262f4f + 473b735 commit f8d0368
Show file tree
Hide file tree
Showing 2 changed files with 145 additions and 80 deletions.
217 changes: 137 additions & 80 deletions frontend/src/components/Annotation/ImportAnnotations.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,11 @@ import {
import { useDispatch, useSelector } from "react-redux";
import { DataGrid } from "@material-ui/data-grid";
import { CSVReader } from "react-papaparse";
import { fetchSegments } from "../../reducers/segmentSlice";
import {
addSegments,
fetchSegments,
updateSegment,
} from "../../reducers/segmentSlice";
import { openAlert } from "../../reducers/errorSlice";
import {
addBatch,
Expand Down Expand Up @@ -59,14 +63,15 @@ const columns = [

const ImportAnnotations = ({ onClose, open }) => {
const classes = useStyles();
const { fileNames } = useSelector((state) => state.file);
const { fileNames, files } = useSelector((state) => state.file);
const { id, annotators, annotatorIds } = useSelector((state) => state.user);

const dispatch = useDispatch();

const [data, setData] = useState([]);
const [newBatch, setNewBatch] = useState("");
const [loading, setLoading] = useState(false);
const [segmentLength, setSegmentLength] = useState(60);
const processedData = useRef(null);
const processedDataArr = useRef(null);
const selectedAnnotationsArr = useRef(null);
Expand Down Expand Up @@ -224,8 +229,6 @@ const ImportAnnotations = ({ onClose, open }) => {

setLoading(true);

const batchSegments = [];

dispatch(
addBatch({
settings,
Expand All @@ -235,60 +238,46 @@ const ImportAnnotations = ({ onClose, open }) => {
})
)
.unwrap()
.then(async (res) => {
const batchId = res.data.id;

.then((res) => res.data.id)
.then(async (batchId) => {
const createdSegmentsIds = [];
//iterate the annotations objects, the key of the object is the filename
//and the value is the annotation array associated with the file
for (const [fileName, annotations] of Object.entries(
selectedAnnotations
)) {
const durations = [];
const requests = [];

let left = 0;
let right = 0;

while (left < annotations.length) {
while (
right < annotations.length &&
annotations[right].data[2] - annotations[left].data[1] <= 60
) {
right++;
}
durations.push({ left, right: right - 1 });
left = right;
//create segments for the file with the length
const fileId = fileNames[fileName];
const fileLength = files[fileId].duration;
const segmentsNumber = Math.trunc(fileLength / segmentLength) + 1;

//generate segments according to the segmentlength and file
const generatedSegments = [];

let i = 0;

while (i < segmentsNumber) {
generatedSegments.push({
file: fileId,
start: segmentLength * i,
end: segmentLength * i + segmentLength * 1,
model_developer: id,
});
i++;
}
const generatedAnnotations = [];

durations.forEach((duration) => {
const { left, right } = duration;
const start =
annotations[left].data[1] * 1 - 1 > 0
? annotations[left].data[1] * 1 - 1
: 0;
const end = annotations[right].data[2] * 1 + 1;
const file = fileNames[fileName];

requests.push(
axiosWithAuth.post(`/segment/`, [
{
start: start.toFixed(3) * 1,
end: end.toFixed(3) * 1,
file,
},
])
);
});

await Promise.all(requests).then((res) => {
const segmentsArr = [];
res.forEach((item) => {
segmentsArr.push(item.data[0].id);
batchSegments.push(item.data[0].id);
});
await dispatch(addSegments({ durations: generatedSegments }))
.unwrap()
.then(async (createdSegments) => {
//get ids from the created segments
createdSegments.forEach((segment) =>
createdSegmentsIds.push(segment.id)
);

const annotationsArr = [];
let currentSegmentIndex = 0;

durations.forEach((item, index) => {
for (let i = item.left; i <= item.right; i++) {
annotations.forEach(async (annotation) => {
const [
,
start,
Expand All @@ -300,32 +289,89 @@ const ImportAnnotations = ({ onClose, open }) => {
pod,
call_type,
comments,
] = annotations[i].data;

for (const annotator in selectedAnnotators) {
if (selectedAnnotators[annotator]) {
annotationsArr.push({
segment: segmentsArr[index],
batch: batchId,
annotator,
start: (+start).toFixed(3),
end: (+end).toFixed(3),
comments,
pod,
kw_ecotype,
call_type,
freq_min: (+freq_min).toFixed(3),
freq_max: (+freq_max).toFixed(3),
sound_id_species,
});
] = annotation.data;

while (currentSegmentIndex < createdSegments.length) {
if (+start > +createdSegments[currentSegmentIndex].end) {
//skip to the next segment
currentSegmentIndex++;
} else if (
+start >= +createdSegments[currentSegmentIndex].start &&
+end <= +createdSegments[currentSegmentIndex].end
) {
//do something
for (const annotator in selectedAnnotators) {
if (selectedAnnotators[annotator]) {
generatedAnnotations.push({
segment: createdSegments[currentSegmentIndex].id,
batch: batchId,
annotator,
start: (+start).toFixed(3),
end: (+end).toFixed(3),
comments,
pod,
kw_ecotype,
call_type,
freq_min: (+freq_min).toFixed(3),
freq_max: (+freq_max).toFixed(3),
sound_id_species,
});
}
}
break;
} else if (
+start >= +createdSegments[currentSegmentIndex].start &&
+end > +createdSegments[currentSegmentIndex].end
) {
//edge case when the end of the annotation exceeds the segment's
//update the segment with the annotation's end time
const edgeCases = [];
await dispatch(
updateSegment({
end: (+end).toFixed(3),
segmentId: createdSegments[currentSegmentIndex].id,
})
)
.unwrap()
.then((segment) => segment.id)
.then((segmentId) => {
for (const annotator in selectedAnnotators) {
if (selectedAnnotators[annotator]) {
edgeCases.push({
segment: segmentId,
batch: batchId,
annotator,
start: (+start).toFixed(3),
end: (+end).toFixed(3),
comments,
pod,
kw_ecotype,
call_type,
freq_min: (+freq_min).toFixed(3),
freq_max: (+freq_max).toFixed(3),
sound_id_species,
});
}
}
})
.then(() => dispatch(addBatchAnnotations(edgeCases)));
currentSegmentIndex++;
break;
}
}
}
});
return generatedAnnotations;
})
.then((generatedAnnotations) => {
dispatch(addBatchAnnotations(generatedAnnotations));
});
dispatch(addBatchAnnotations(annotationsArr));
});
}
dispatch(updateBatchSegments({ segments: batchSegments, batchId }));
return { createdSegmentsIds, batchId };
})
.then(({ createdSegmentsIds, batchId }) => {
dispatch(
updateBatchSegments({ segments: createdSegmentsIds, batchId })
);
})
.then(() => dispatch(fetchUser(id)))
.then(() => dispatch(fetchBatches()))
Expand Down Expand Up @@ -375,13 +421,24 @@ const ImportAnnotations = ({ onClose, open }) => {
</Grid>
{data.length > 0 && (
<Grid item xs={6} container spacing={2}>
<Grid item xs={6}>
<Typography>Created a new Batch</Typography>
<TextField
value={newBatch}
onChange={(e) => setNewBatch(e.target.value)}
variant="outlined"
/>
<Grid item container xs={6} spacing={1}>
<Grid item xs={12}>
<Typography>Created a new Batch</Typography>
<TextField
value={newBatch}
onChange={(e) => setNewBatch(e.target.value)}
variant="outlined"
/>
</Grid>
<Grid item xs={12}>
<Typography>Segment Length(s):</Typography>
<TextField
value={segmentLength}
onChange={(e) => setSegmentLength(e.target.value)}
variant="outlined"
type="number"
/>
</Grid>
</Grid>
<Annotators
selectedAnnotators={selectedAnnotators}
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/reducers/segmentSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ export const addSegments = createAsyncThunk(
}
);

export const updateSegment = createAsyncThunk(
'segment/updateSegment',
async ({ end, segmentId }) => {
const { data } = await axiosWithAuth.patch(`/segment/${segmentId}/`, { end });
return data;
}
);

export const removeSegments = createAsyncThunk(
'segment/removeSegments',
async ({ checked }) => {
Expand Down

0 comments on commit f8d0368

Please sign in to comment.