Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use setExtractionStatus action/reducer to propagate the message in th… #97

Merged
merged 10 commits into from
Dec 17, 2024
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "consort-frontend",
"version": "0.8.0",
"version": "0.9.0",
"description": "",
"engines": {
"npm": ">=8.11",
30 changes: 11 additions & 19 deletions src/actions/client.js
Original file line number Diff line number Diff line change
@@ -16,15 +16,6 @@ import {ADD_FILE_TO_DATASET, addFileToDataset, CREATE_DATASETS, createDataset} f

const clientInfo = await getClientInfo();

export function setStatement(statementType) {
return (dispatch) => {
dispatch({
type: 'SET_STATEMENT_TYPE',
statementType: statementType,
receivedAt: Date.now(),
});
};
}

// createUploadExtract thunk function
export function createUploadExtract(file, config) {
@@ -43,29 +34,30 @@ export function createUploadExtract(file, config) {
if (file_json !== undefined){
file_json["filename"] = file.name;
// submit uploaded file for extraction
dispatch(setExtractionStatus("Analyzing file"));
if (file.type == "application/vnd.openxmlformats-officedocument.wordprocessingml.document" || file.type =="application/msword"){
const word_pipeline_status = await wordPipeline(file_json, dataset_json, config, clientInfo);
if (word_pipeline_status) {
console.log("File extraction complete.");
dispatch(setExtractionStatus(SET_EXTRACTION_STATUS, true));
console.log("File extraction complete");
dispatch(setExtractionStatus("File extraction complete"));

}
else {
console.error("File extraction failed");
dispatch(setExtractionStatus(SET_EXTRACTION_STATUS, false));
dispatch(setExtractionStatus("File extraction failed"));
}

}
else if (file.type == "application/pdf") {
const pdf_pipeline_status = await pdfPipeline(file_json, dataset_json, config, clientInfo);
if (pdf_pipeline_status) {
console.log("File extraction complete.");
dispatch(setExtractionStatus(SET_EXTRACTION_STATUS, true));
dispatch(setExtractionStatus("File extraction complete"));

}
else {
console.error("File extraction failed");
dispatch(setExtractionStatus(SET_EXTRACTION_STATUS, false));
dispatch(setExtractionStatus("File extraction failed"));
}

// TODO add extracted output files to dataset state
@@ -75,20 +67,20 @@ export function createUploadExtract(file, config) {
else {
// TODO add error action
console.error("Error in file type");
dispatch(setExtractionStatus(SET_EXTRACTION_STATUS, false));
dispatch(setExtractionStatus("Error in file type"));
}
// after submitting uploaded file for extraction, add the file to dataset state
dispatch(addFileToDataset(ADD_FILE_TO_DATASET, file_json));
}
else {
console.error("Error in clowder upload of file ", file.name)
dispatch(setExtractionStatus(SET_EXTRACTION_STATUS, false));
dispatch(setExtractionStatus("Error in clowder upload of file " + file.name));
}
}
else {
console.error("Error in dataset creation");
dispatch(setExtractionStatus(SET_EXTRACTION_STATUS, false));
}
dispatch(setExtractionStatus("Error in dataset creation"));
}
};
}

4 changes: 2 additions & 2 deletions src/actions/file.js
Original file line number Diff line number Diff line change
@@ -89,10 +89,10 @@ export function fetchFileMetadataJsonld(id) {
}

export const SET_EXTRACTION_STATUS = "SET_EXTRACTION_STATUS";
export function setExtractionStatus(type, status) {
export function setExtractionStatus(status) {
return (dispatch) => {
dispatch({
type: type,
type: SET_EXTRACTION_STATUS,
extractionStatus: status
});
};
44 changes: 18 additions & 26 deletions src/components/childComponents/CreateAndUpload.js
Original file line number Diff line number Diff line change
@@ -13,7 +13,7 @@ import {getClientInfo} from "../../utils/common";
import Dropfile from "./Dropfile";
import {createUploadExtract} from "../../actions/client";
import {getDatasetMetadata, getFileInDataset} from "../../utils/dataset";
import {fetchFilePreviews} from "../../actions/file";
import { fetchFilePreviews, SET_EXTRACTION_STATUS, setExtractionStatus } from "../../actions/file";
import {SET_DATASET_METADATA, setDatasetMetadata} from "../../actions/dataset";
import {SET_STATEMENT_TYPE, setStatement} from '../../actions/statement';
import config from "../../app.config";
@@ -27,7 +27,6 @@ export default function CreateAndUpload() {
const rctExtractor = config.rct_extractor;

const [mouseHover, setMouseHover] = useState(false); // mouse hover state for dropzone
const [statementTypeSelected, setStatementTypeSelected] = useState(false); // user choice of statement type consort or spirit
const [loading, setLoading] = useState(false); // loading overlay state and button disabled state. set to active when dropfile is active
const [loading_text, setLoadingText] = useState("Processing"); // loading overlay text.
const [filename, setFilename] = useState(''); // uploaded filename
@@ -39,11 +38,11 @@ export default function CreateAndUpload() {
const extractionStatus = useSelector(state => state.file.extractionStatus);
const listFilePreviews = (fileId, clientInfo) => dispatch(fetchFilePreviews(fileId, clientInfo));
const datasetMetadata = (json) => dispatch(setDatasetMetadata(SET_DATASET_METADATA, json));
const statementType = useSelector(state => state.statement.statementType);

const handleStatementChange = (event) => {
dispatch(setStatement(SET_STATEMENT_TYPE, event.target.value));
config.statementType = event.target.value;
setStatementTypeSelected(true);
};

const onDropFile = (file) => {
@@ -55,13 +54,13 @@ export default function CreateAndUpload() {

// useEffect on extractionStatus for preview generation
useEffect(async () => {
if (extractionStatus !== null && extractionStatus === true) {
if (extractionStatus !== null) {
setLoadingText(extractionStatus);
const clientInfo = await getClientInfo();
const file_name = filename.replace(/\.[^/.]+$/, ""); // get filename without extension;
const dataset_id = datasets[0].id;
// check extraction status and html file generation in loop
const html_file_loop = async () => {
setLoadingText("Checking extraction status");
const highlights_filename = file_name + '_highlights' + '.json'
const highlightsFile = await getFileInDataset(dataset_id, "application/json", highlights_filename, clientInfo);
if (highlightsFile !== null && typeof highlightsFile.id === "string") {
@@ -86,7 +85,6 @@ export default function CreateAndUpload() {
}
datasetMetadata(metadata);

setLoadingText("Extraction completed");
setPreview(false) // Continue button activated
setSpinner(false); // stop display of spinner
} else {
@@ -102,35 +100,30 @@ export default function CreateAndUpload() {
}
}
else if (extractionStatus === false){
setLoadingText("Error in extraction");
dispatch(setExtractionStatus("Error in extraction"));
setSpinner(false); // stop display of spinner
}
}, [extractionStatus]);
}, [extractionStatus]); // TODO: This useEffect will trigger again when the extractionStatus is completed


// onDrop function to trigger createUploadExtract action dispatch
const onDrop = useCallback((acceptedFiles, rejectedFiles) => {
if (!statementTypeSelected) {
setLoadingText("Please select a statement type (Trial results or Trial protocol) first");
setSpinner(false);
return;
}

// this callback function is triggered when a file is dropped into the dropzone
setLoading(true);
try {
acceptedFiles.map(file => {
onDropFile(file)
})
rejectedFiles.map(file => {
setLoadingText("File rejected");
dispatch(setExtractionStatus("File rejected"));
setSpinner(false);
})
}
catch(error) {
setLoadingText("Upload failed", error)
dispatch(setExtractionStatus("Upload failed"));
setSpinner(false);
}
}, [mouseHover, statementTypeSelected]);
}, [mouseHover]);


const goToPreviewRoute = () => {
@@ -144,23 +137,22 @@ export default function CreateAndUpload() {
<Box className="createupload">
<LoadingOverlay active={loading} text={loading_text} spinner={spinner}>
<div className="mousehoverdrop" onMouseEnter={() => setMouseHover(true)}>
<Dropfile
onDrop={onDrop}
<Dropfile
onDrop={onDrop}
accept={{
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": [".docx"],
"application/msword": [".doc"],
"application/vnd.openxmlformats-officedocument.wordprocessingml.document": [".docx"],
"application/msword": [".doc"],
"application/pdf": [".pdf"]
}}
message={!statementTypeSelected ?
"Please select a statement type above before uploading files" :
"Drag and drop files here"}
message={"Drag and drop files here"}
/>
</div>
</LoadingOverlay>

<div className="radio-buttons-group-div">
<RadioGroup
name="radio-buttons-group"
<RadioGroup
defaultValue={statementType}
name="radio-buttons-group"
row
onChange={handleStatementChange}
>
2 changes: 1 addition & 1 deletion src/components/childComponents/FilePreview.js
Original file line number Diff line number Diff line change
@@ -33,7 +33,7 @@ export default function FilePreview() {
if (filePreviews !== undefined && filePreviews.length > 0) {
const previewsTemp = [];
// get either pdf preview / html preview
if (filePreviews.length === 1){
if (filePreviews.length > 0){
console.log("filePreviews:", filePreviews);
const fileId = filePreviews[0][0].file_id;
const previewsList = filePreviews[0][0].previews;
95 changes: 11 additions & 84 deletions src/components/previewers/Pdf.js
Original file line number Diff line number Diff line change
@@ -4,91 +4,16 @@ import {useDispatch, useSelector} from "react-redux";
import { pdfjs , Document, Page } from 'react-pdf';
import "react-pdf/dist/esm/Page/TextLayer.css";
import {SET_PAGE_NUMBER, setPageNumber} from "../../actions/pdfpreview";
import {
consort_highlight_color,
consort_label_color,
spirit_highlight_color,
spirit_label_color
} from '../styledComponents/HighlightColors';


pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${pdfjs.version}/pdf.worker.js`;

const highlight_color = {
"1a":"#88FF88",
"1b":"#88FF88",
"2a": "#CCAAFF",
"2b": "#CCAAFF",
"3a": "#88FFFF",
"3b": "#88FFFF",
"4a": "#88FFFF",
"4b": "#88FFFF",
"5": "#88FFFF",
"6a": "#88FFFF",
"6b": "#88FFFF",
"7a": "#88FFFF",
"7b": "#88FFFF",
"8a": "#88FFFF",
"8b": "#88FFFF",
"9" : "#88FFFF",
"10" :"#88FFFF",
"11a" :"#88FFFF",
"11b" :"#88FFFF",
"12a ":"#88FFFF",
"12b" :"#88FFFF",
"13a":"#bbff44",
"13b" :"#bbff44",
"14a" :"#bbff44",
"14b" :"#bbff44",
"15" :"#bbff44",
"16" :"#bbff44",
"17a" :"#bbff44",
"17b" :"#bbff44",
"18" :"#bbff44",
"19" :"#bbff44",
"20" :"#AACCFF",
"21" :"#AACCFF",
"22" :"#AACCFF",
"23" :"#FFAACC",
"24" :"#FFAACC",
"25" :"#FFAACC",
}

const label_color = {
"1a":"#009c00",
"1b":"#009c00",
"2a": "#4400aa",
"2b": "#4400aa",
"3a": "#009c9c",
"3b": "#009c9c",
"4a": "#009c9c",
"4b": "#009c9c",
"5": "#009c9c",
"6a": "#009c9c",
"6b": "#009c9c",
"7a": "#009c9c",
"7b": "#009c9c",
"8a": "#009c9c",
"8b": "#009c9c",
"9" : "#009c9c",
"10" :"#009c9c",
"11a" :"#009c9c",
"11b" :"#009c9c",
"12a ":"#009c9c",
"12b" :"#009c9c",
"13a":"#528100",
"13b" :"#528100",
"14a" :"#528100",
"14b" :"#528100",
"15" :"#528100",
"16" :"#528100",
"17a" :"#528100",
"17b" :"#528100",
"18" :"#528100",
"19" :"#528100",
"20" :"#0044aa",
"21" :"#0044aa",
"22" :"#0044aa",
"23" :"#0044aa",
"24" :"#0044aa",
"25" :"#0044aa",
}


export default function Pdf(props) {
const dispatch = useDispatch();

@@ -105,6 +30,8 @@ export default function Pdf(props) {
let pageNumber = useSelector((state) => state.pdfpreview.pageNumber);
const dispatchPageNumber = (number) => dispatch(setPageNumber(SET_PAGE_NUMBER, number));

const statementType = useSelector(state => state.statement.statementType);

const [pageWidth, setPageWidth] = useState(500);
const [pageHeight, setPageHeight]= useState(799);
let [canvas_width, setCanvasWidth] = useState(500);
@@ -197,16 +124,16 @@ export default function Pdf(props) {
function highlightText(context, label, x, y, width, height){
// rectangle highlights styling
context.globalAlpha = 0.2
context.fillStyle = highlight_color[label]; // 'rgb(255, 190, 60)';
context.fillStyle = statementType === "consort" ? consort_highlight_color[label] : spirit_highlight_color[label];
context.fillRect(x , y , width , height );
}

function highlightLabel(context, label, x, y){
context.globalAlpha = 1.0
context.fillStyle = highlight_color[label];
context.fillStyle = statementType === "consort" ? consort_highlight_color[label] : spirit_highlight_color[label];
context.fillRect(x, y, 25, 12);
context.font = "bold 12px Verdana";
context.fillStyle = label_color[label];
context.fillStyle = statementType === "consort" ? consort_label_color[label] : spirit_label_color[label];
context.textAlign = "start";
context.textBaseline = "top";
context.fillText(label, x, y);
187 changes: 187 additions & 0 deletions src/components/styledComponents/HighlightColors.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
export const consort_highlight_color = {
"1a":"#88FF88",
"1b":"#88FF88",
"2a": "#CCAAFF",
"2b": "#CCAAFF",
"3a": "#88FFFF",
"3b": "#88FFFF",
"4a": "#88FFFF",
"4b": "#88FFFF",
"5": "#88FFFF",
"6a": "#88FFFF",
"6b": "#88FFFF",
"7a": "#88FFFF",
"7b": "#88FFFF",
"8a": "#88FFFF",
"8b": "#88FFFF",
"9" : "#88FFFF",
"10" :"#88FFFF",
"11a" :"#88FFFF",
"11b" :"#88FFFF",
"12a ":"#88FFFF",
"12b" :"#88FFFF",
"13a":"#bbff44",
"13b" :"#bbff44",
"14a" :"#bbff44",
"14b" :"#bbff44",
"15" :"#bbff44",
"16" :"#bbff44",
"17a" :"#bbff44",
"17b" :"#bbff44",
"18" :"#bbff44",
"19" :"#bbff44",
"20" :"#AACCFF",
"21" :"#AACCFF",
"22" :"#AACCFF",
"23" :"#FFAACC",
"24" :"#FFAACC",
"25" :"#FFAACC",
}

export const consort_label_color = {
"1a":"#009c00",
"1b":"#009c00",
"2a": "#4400aa",
"2b": "#4400aa",
"3a": "#009c9c",
"3b": "#009c9c",
"4a": "#009c9c",
"4b": "#009c9c",
"5": "#009c9c",
"6a": "#009c9c",
"6b": "#009c9c",
"7a": "#009c9c",
"7b": "#009c9c",
"8a": "#009c9c",
"8b": "#009c9c",
"9" : "#009c9c",
"10" :"#009c9c",
"11a" :"#009c9c",
"11b" :"#009c9c",
"12a ":"#009c9c",
"12b" :"#009c9c",
"13a":"#528100",
"13b" :"#528100",
"14a" :"#528100",
"14b" :"#528100",
"15" :"#528100",
"16" :"#528100",
"17a" :"#528100",
"17b" :"#528100",
"18" :"#528100",
"19" :"#528100",
"20" :"#0044aa",
"21" :"#0044aa",
"22" :"#0044aa",
"23" :"#0044aa",
"24" :"#0044aa",
"25" :"#0044aa",
}

export const spirit_highlight_color = {
"1": "#AACCFF",
"2a": "#AACCFF",
"2b": "#AACCFF",
"3": "#AACCFF",
"4": "#AACCFF",
"5a": "#AACCFF",
"5b": "#AACCFF",
"5c": "#AACCFF",
"5d": "#AACCFF",
"6a": "#CCAAFF",
"6b": "#CCAAFF",
"7": "#CCAAFF",
"8": "#CCAAFF",
"9" : "#88FFFF",
"10" : "#88FFFF",
"11a" : "#88FFFF",
"11b" : "#88FFFF",
"11c" : "#88FFFF",
"11d" : "#88FFFF",
"12": "#88FFFF",
"13": "#88FFFF",
"14" : "#88FFFF",
"15" : "#88FFFF",
"16a" : "#88FFFF",
"16b" : "#88FFFF",
"16c" : "#88FFFF",
"17a" : "#88FFFF",
"17b" : "#88FFFF",
"18a" : "#88FFFF",
"18b" : "#88FFFF",
"19" : "#88FFFF",
"20a" : "#88FFFF",
"20b" : "#88FFFF",
"20c" : "#88FFFF",
"21a" : "#88FFFF",
"21b" : "#88FFFF",
"22" : "#88FFFF",
"23" : "#88FFFF",
"24" : "#bbff44",
"25" : "#bbff44",
"26a" : "#bbff44",
"26b" : "#bbff44",
"27" : "#bbff44",
"28" : "#bbff44",
"29" : "#bbff44",
"30" : "#bbff44",
"31a" : "#bbff44",
"31b" : "#bbff44",
"31c" : "#bbff44",
"32" : "#FFAACC",
"33" : "#FFAACC",
}

export const spirit_label_color = {
"1": "#0044aa",
"2a": "#0044aa",
"2b": "#0044aa",
"3": "#0044aa",
"4": "#0044aa",
"5a": "#0044aa",
"5b": "#0044aa",
"5c": "#0044aa",
"5d": "#0044aa",
"6a": "#4400aa",
"6b": "#4400aa",
"7": "#4400aa",
"8": "#4400aa",
"9" : "#009c9c",
"10" : "#009c9c",
"11a" : "#009c9c",
"11b" : "#009c9c",
"11c" : "#009c9c",
"11d" : "#009c9c",
"12": "#009c9c",
"13": "#009c9c",
"14" : "#009c9c",
"15" : "#009c9c",
"16a" : "#009c9c",
"16b" : "#009c9c",
"16c" : "#009c9c",
"17a" : "#009c9c",
"17b" : "#009c9c",
"18a" : "#009c9c",
"18b" : "#009c9c",
"19" : "#009c9c",
"20a" : "#009c9c",
"20b" : "#009c9c",
"20c" : "#009c9c",
"21a" : "#009c9c",
"21b" : "#009c9c",
"22" : "#009c9c",
"23" : "#009c9c",
"24" : "#528100",
"25" : "#528100",
"26a" : "#528100",
"26b" : "#528100",
"27" : "#528100",
"28" : "#528100",
"29" : "#528100",
"30" : "#528100",
"31a" : "#528100",
"31b" : "#528100",
"31c" : "#528100",
"32" : "#0044aa",
"33" : "#0044aa",
}
18 changes: 9 additions & 9 deletions src/utils/dataset.js
Original file line number Diff line number Diff line change
@@ -206,26 +206,26 @@ export async function setDatasetMetadata(dataset_id, content) {
export async function getDatasetMetadataLoop(dataset_id, extractor_name, clientInfo){
/**
* Asynchronously retrieves dataset metadata by repeatedly checking until it's available.
*
*
* @param {string} dataset_id - The ID of the dataset to retrieve metadata for.
* @param {string} extractor_name - The name of the extractor used (currently unused in the function).
* @param {Object} clientInfo - Client information required for API calls.
* @returns {Promise<Object|Array>} The dataset metadata content if successful, or an empty array if unsuccessful.
*
*
* This function implements a polling mechanism to check for dataset metadata:
* 1. It attempts to retrieve the metadata using the provided dataset ID and client info.
* 2. If metadata is available, it returns the content of the first metadata item.
* 3. If metadata is not yet available, it waits for 30 seconds before trying again.
* 4. This process repeats until metadata is found or an error occurs.
*
*
*/

const extractor_metadata_loop = async () => {
// get dataset metadata as json-ld
const metadata = await getDatasetMetadata(dataset_id, clientInfo);
if (metadata !== null && metadata.length > 0) {
// Filter metadata for the specified extractor
const relevantMetadata = metadata.filter(item =>
const relevantMetadata = metadata.filter(item =>
item.content && item.content.extractor === extractor_name
);

@@ -235,13 +235,13 @@ export async function getDatasetMetadataLoop(dataset_id, extractor_name, clientI
return relevantMetadata[0].content;
} else {
console.log(`No metadata found for extractor: ${extractor_name}`);
console.log("Waiting 30 seconds before checking again...");
await sleep(30000);
console.log("Waiting 5 seconds before checking again...");
await sleep(5000);
return await extractor_metadata_loop();
}
} else {
console.log("check for extraction metadata after 30s");
await sleep(30000);
console.log("check for extraction metadata after 5s");
await sleep(5000);
await extractor_metadata_loop();
}
};