diff --git a/package.json b/package.json index 1a98468..89aa6dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "consort-frontend", - "version": "0.8.0", + "version": "0.9.0", "description": "", "engines": { "npm": ">=8.11", diff --git a/src/actions/client.js b/src/actions/client.js index 2964afa..ac83ee2 100644 --- a/src/actions/client.js +++ b/src/actions/client.js @@ -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")); + } }; } diff --git a/src/actions/file.js b/src/actions/file.js index 5ec6bb3..d309205 100644 --- a/src/actions/file.js +++ b/src/actions/file.js @@ -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 }); }; diff --git a/src/components/childComponents/CreateAndUpload.js b/src/components/childComponents/CreateAndUpload.js index b5fff12..6db2cc3 100644 --- a/src/components/childComponents/CreateAndUpload.js +++ b/src/components/childComponents/CreateAndUpload.js @@ -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() {
setMouseHover(true)}> -
- diff --git a/src/components/childComponents/FilePreview.js b/src/components/childComponents/FilePreview.js index f8687e0..188e53b 100644 --- a/src/components/childComponents/FilePreview.js +++ b/src/components/childComponents/FilePreview.js @@ -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; diff --git a/src/components/previewers/Pdf.js b/src/components/previewers/Pdf.js index 55bdb82..d166a51 100644 --- a/src/components/previewers/Pdf.js +++ b/src/components/previewers/Pdf.js @@ -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); diff --git a/src/components/styledComponents/HighlightColors.js b/src/components/styledComponents/HighlightColors.js new file mode 100644 index 0000000..37ef7cf --- /dev/null +++ b/src/components/styledComponents/HighlightColors.js @@ -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", +} diff --git a/src/utils/dataset.js b/src/utils/dataset.js index 912f20a..18d6dae 100644 --- a/src/utils/dataset.js +++ b/src/utils/dataset.js @@ -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} 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(); } };