diff --git a/src/Layout/Routes.js b/src/Layout/Routes.js index 4f645908..573c4d5a 100644 --- a/src/Layout/Routes.js +++ b/src/Layout/Routes.js @@ -4,14 +4,13 @@ import { Route, Routes } from "react-router-dom"; // eslint-disable-next-line import/no-unresolved import config from "config"; import AuthorsRoute from "pages/AuthorsRoute"; +import CorpImport from "pages/CorpImport"; import { CreateCorpus, CreateDictionary } from "pages/CreateDictionary"; import { CorpusDashboard, DictionaryDashboard, ParallelCorporaDashboard } from "pages/Dashboard"; import DashboardRoute from "pages/DashboardRoute"; import Desktop from "pages/Desktop"; import DialeqtImport from "pages/DialeqtImport"; import DictImport from "pages/DictImport"; -import ComplexDistance from "pages/ComplexDistance"; -import CorpImport from "pages/CorpImport"; import DictionariesAll from "pages/DictionariesAll"; import DistanceMap from "pages/DistanceMap"; import MapSelectedLanguages from "pages/DistanceMap/map"; @@ -82,7 +81,6 @@ const AppRoutes = () => ( } /> } /> } /> - } /> } /> ); diff --git a/src/components/CognateAnalysisModal/index.js b/src/components/CognateAnalysisModal/index.js index fc8d5917..7e1aa9ce 100644 --- a/src/components/CognateAnalysisModal/index.js +++ b/src/components/CognateAnalysisModal/index.js @@ -12,7 +12,9 @@ import { Loader, Modal, Pagination, - Select + Select, + Input, + Label } from "semantic-ui-react"; import { gql } from "@apollo/client"; import { graphql, withApollo } from "@apollo/client/react/hoc"; @@ -247,6 +249,26 @@ const computeMorphCognateAnalysisMutation = gql` } `; +const computeComplexDistanceMutation = gql` + mutation complexDistance ( + $resultPool: [ObjectVal]! + $debugFlag: Boolean + ) { + complex_distance( + result_pool: $resultPool + debug_flag: $debugFlag + ) { + result + minimum_spanning_tree + embedding_2d + embedding_3d + perspective_name_list: language_name_list + message + triumph + } + } +`; + const SUGGESTIONS_PER_PAGE = 50; function equalIds(id_a, id_b) { @@ -1265,6 +1287,9 @@ class CognateAnalysisModal extends React.Component { xlsx_url: "", json_url: "", figure_url: "", + fileSuite: null, + lang_mode: null, + cleanResult: false, minimum_spanning_tree: [], embedding_2d: [], @@ -1340,11 +1365,18 @@ class CognateAnalysisModal extends React.Component { this.admin_section_render = this.admin_section_render.bind(this); this.suggestions_render = this.suggestions_render.bind(this); + this.browse_files_render = this.browse_files_render.bind(this); this.sg_connect = this.sg_connect.bind(this); } componentDidMount() { + + if (this.props.mode === "complex_distance") { + this.setState({ lang_mode: "none", initialized: true }); + return; + } + const multi = this.props.mode === "multi_analysis" || this.props.mode === "multi_reconstruction" || @@ -1353,6 +1385,7 @@ class CognateAnalysisModal extends React.Component { this.props.mode === "multi_morphology"; (multi ? this.initialize_multi : this.initialize_single)(); + this.setState({ lang_mode: multi ? "multi" : "single" }); } initialize_common(allFields, columns, tree, english_status) { @@ -1800,13 +1833,7 @@ class CognateAnalysisModal extends React.Component { /* Selecting grouping field for many languages. */ - if ( - this.props.mode === "multi_analysis" || - this.props.mode === "multi_reconstruction" || - this.props.mode === "multi_suggestions" || - this.props.mode === "multi_swadesh" || - this.props.mode === "multi_morphology" - ) { + if (this.state.lang_mode === "multi") { this.state.groupFieldIdStr = value; const { perspectiveSelectionMap, perspectiveSelectionCountMap } = this.state; @@ -1845,7 +1872,7 @@ class CognateAnalysisModal extends React.Component { this.setState({ groupFieldIdStr: value }); - } else { + } else if (this.state.lang_mode === "single") { /* Selecting grouping field for a single language. */ this.setState({ groupFieldIdStr: value, @@ -1985,13 +2012,25 @@ class CognateAnalysisModal extends React.Component { } } + handleComplexDistanceResult({ data: { complex_distance }}) + { + this.setState({ + ...complex_distance, + /* Calculate plotly data */ + ...this.handleResult(complex_distance), + computing: false, + cleanResult: false + }); + } + handleSwadeshResult({ data: { swadesh_analysis }}) { this.setState({ ...swadesh_analysis, /* Calculate plotly data */ ...this.handleResult(swadesh_analysis), - computing: false + computing: false, + cleanResult: false }); } @@ -2001,7 +2040,8 @@ class CognateAnalysisModal extends React.Component { ...morph_cognate_analysis, /* Calculate plotly data */ ...this.handleResult(morph_cognate_analysis), - computing: false + computing: false, + cleanResult: false }); } @@ -2064,6 +2104,7 @@ class CognateAnalysisModal extends React.Component { ...this.handleResult(cognate_analysis), library_present: true, computing: false, + cleanResult: false, sg_select_list, sg_state_list, sg_count, @@ -2103,65 +2144,66 @@ class CognateAnalysisModal extends React.Component { } handleCreate() { - const { perspectiveId, computeCognateAnalysis, computeSwadeshAnalysis, computeMorphCognateAnalysis } = this.props; - - const groupField = this.fieldDict[this.state.groupFieldIdStr]; + const { + perspectiveId, + computeCognateAnalysis, + computeSwadeshAnalysis, + computeMorphCognateAnalysis, + computeComplexDistance + } = this.props; - /* Gathering info of perspectives we are to analyze. */ + if (this.state.lang_mode === "single" || this.state.lang_mode === "multi") { + const groupField = this.fieldDict[this.state.groupFieldIdStr]; - let perspectiveInfoList = []; - const multiList = []; + /* Gathering info of perspectives we are to analyze. */ + let perspectiveInfoList = []; + const multiList = []; - if ( - this.props.mode === "multi_analysis" || - this.props.mode === "multi_reconstruction" || - this.props.mode === "multi_suggestions" || - this.props.mode === "multi_swadesh" || - this.props.mode === "multi_morphology" - ) { - for (const language of this.state.language_list) { - let p_count = 0; + if (this.state.lang_mode === "multi") { + for (const language of this.state.language_list) { + let p_count = 0; - for (const { perspective, treePathList: [subLanguage,] } of language.perspective_list) { - const p_key = id2str(perspective.id); + for (const { perspective, treePathList: [subLanguage,] } of language.perspective_list) { + const p_key = id2str(perspective.id); - if (this.state.perspectiveSelectionMap[p_key]) { - perspectiveInfoList.push([ - subLanguage.__typename === "Language" ? subLanguage.id : this.baseLanguageId, - perspective.id, - this.fieldDict[this.state.transcriptionFieldIdStrMap[p_key]].id, - this.fieldDict[this.state.translationFieldIdStrMap[p_key]].id, - this.fieldDict[this.state.lexemeFieldIdStrMap[p_key]].id - ]); + if (this.state.perspectiveSelectionMap[p_key]) { + perspectiveInfoList.push([ + subLanguage.__typename === "Language" ? subLanguage.id : this.baseLanguageId, + perspective.id, + this.fieldDict[this.state.transcriptionFieldIdStrMap[p_key]].id, + this.fieldDict[this.state.translationFieldIdStrMap[p_key]].id, + this.fieldDict[this.state.lexemeFieldIdStrMap[p_key]].id + ]); - p_count++; + p_count++; + } } + + multiList.push([language.id, p_count]); } + } else { + perspectiveInfoList = this.perspective_list + + .map(({ perspective, treePathList: [subLanguage,] }, index) => [ + subLanguage.__typename === "Language" ? subLanguage.id : this.baseLanguageId, + perspective.id, + this.fieldDict[this.state.transcriptionFieldIdStrList[index]].id, + this.fieldDict[this.state.translationFieldIdStrList[index]].id, + this.fieldDict[this.state.lexemeFieldIdStrList[index]].id + ]) - multiList.push([language.id, p_count]); + .filter((perspective_info, index) => this.state.perspectiveSelectionList[index]); } - } else { - perspectiveInfoList = this.perspective_list - .map(({ perspective, treePathList: [subLanguage,] }, index) => [ - subLanguage.__typename === "Language" ? subLanguage.id : this.baseLanguageId, - perspective.id, - this.fieldDict[this.state.transcriptionFieldIdStrList[index]].id, - this.fieldDict[this.state.translationFieldIdStrList[index]].id, - this.fieldDict[this.state.lexemeFieldIdStrList[index]].id - ]) + /* Match translations parameter for suggestions. */ - .filter((perspective_info, index) => this.state.perspectiveSelectionList[index]); + const matchTranslationsValue = this.state.matchTranslationsFlag + ? this.state.matchTranslationsValue === "first_three" + ? 1 + : 2 + : 0; } - /* Match translations parameter for suggestions. */ - - const matchTranslationsValue = this.state.matchTranslationsFlag - ? this.state.matchTranslationsValue === "first_three" - ? 1 - : 2 - : 0; - /* If we are to perform acoustic analysis, we will try to launch it in the background. */ if (this.props.mode === "acoustic") { @@ -2214,11 +2256,38 @@ class CognateAnalysisModal extends React.Component { data => this.handleMorphologyResult(data), error_data => this.handleError(error_data) ); + } else if (this.props.mode === "complex_distance") { + this.setState({ computing: true }); + + const { fileSuite, debugFlag } = this.state; + const resultPool = new Array(fileSuite.length); + + for (const [index, file] of fileSuite.entries()) { + const reader = new FileReader(); + reader.onload = () => { + try { + resultPool[index] = JSON.parse(reader.result); + + if ((index + 1) == fileSuite.length) { + computeComplexDistance({ + variables: { + resultPool, + debugFlag + } + }).then( + data => this.handleComplexDistanceResult(data), + error_data => this.handleError(error_data) + ); + } + } catch(error_data) { + this.handleError(error_data) + } + }; + reader.readAsText(file); + } } else { /* Otherwise we will launch it as usual and then will wait for results to display them. */ - this.setState({ - computing: true - }); + this.setState({ computing: true }); const backend_mode = this.props.mode === "multi_analysis" @@ -2784,6 +2853,44 @@ class CognateAnalysisModal extends React.Component { ); } + browse_files_render() { + + const { fileSuite } = this.state; + + return ( +
+ + { this.context( + fileSuite + ? "Json file(s) for complex result:" + : "Please choose result files for merging (use button for multiselect)" + )} + + + { fileSuite && fileSuite.map(({ name: fileName }) => ( + + ))} + + + + this.setState({ cleanResult: true, fileSuite: Array.from(e.target.files) })} + /> +
+ ) + } + render() { if (!this.state.initialized) { return ( @@ -2795,19 +2902,13 @@ class CognateAnalysisModal extends React.Component { const { mode } = this.props; - const multi = - mode === "multi_analysis" || - mode === "multi_reconstruction" || - mode === "multi_suggestions" || - mode === "multi_swadesh" || - mode === "multi_morphology"; - - const { language_list, perspectiveSelectionCountMap } = this.state; + const { language_list, perspectiveSelectionCountMap, lang_mode, fileSuite } = this.state; const disabledCompute = ( - (!multi && (this.perspective_list.length <= 1 || + (lang_mode === "none" && !fileSuite) || + (lang_mode === "single" && (this.perspective_list.length <= 1 || !this.state.perspectiveSelectionList.some(enabled => enabled))) || - (multi && + (lang_mode === "multi" && (language_list.length <= 0 || (mode === "multi_reconstruction" && language_list.filter(language => perspectiveSelectionCountMap[id2str(language.id)] > 0).length <= @@ -2847,10 +2948,12 @@ class CognateAnalysisModal extends React.Component { ? this.context("Morphology distance") : mode === "multi_morphology" ? this.context("Morphology distance multi-language") + : mode === "complex_distance" + ? this.context("Complex distance") : this.context("Cognate analysis")} - {this.language_render(multi)} + { lang_mode === "none" ? this.browse_files_render() : this.language_render(lang_mode === "multi") } - - setFileSuite(Array.from(e.target.files))} - /> -

- )} @@ -90,4 +93,13 @@ function ToolsRoute(props) { ); } -export default connect(state => state.user)(ToolsRoute); +export default connect( + state => state.user, + dispatch => ({ + actions: bindActionCreators({ + cognateAnalysisOpenModal, + }, + dispatch) + }) +)( +ToolsRoute);