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 }) => (
+
+
+ { fileName }
+
+ ))}
+
+ document.getElementById("file-select").click()}
+ color="green"
+ > {`${this.context("Browse")}...`}
+
+ 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") }
- {(/swadesh$/.test(mode) || this.state.library_present
- ) && this.state.result !== null && (
+ { (/swadesh$/.test(mode) || /morphology$/.test(mode) || this.state.library_present
+ ) && this.state.result !== null && ! this.state.cleanResult && (
- {this.context("Analysis results")}:
-
-
-
{this.state.dictionary_count}
-
{this.context("dictionaries")}
-
-
-
{this.state.group_count}
-
{this.context("cognate groups")}
-
-
-
{this.state.transcription_count}
-
{this.context("transcriptions analysed")}
-
+ { ! /complex_distance$/.test(mode) && (
+ <>
+
{this.context("Analysis results")}:
-
- {`${this.state.not_enough_count} ${this.context(
- "cognate groups were excluded from the analysis due to not having lexical entries in at least two selected dictionaries"
- )}.`}
-
-
+
+
+
{this.state.dictionary_count}
+
{this.context("dictionaries")}
+
+
+
{this.state.group_count}
+
{this.context("cognate groups")}
+
+
+
{this.state.transcription_count}
+
{this.context("transcriptions analysed")}
+
-
- {this.state.result.length > 0 && mode !== "suggestions" && mode !== "multi_suggestions" && (
-
- )}
- {this.state.result.length > 0 && this.state.intermediate_url_list && (
-
-
{this.context("Intermediate data")}:
-
- {map(this.state.intermediate_url_list, intermediate_url => (
-
-
{intermediate_url}
+
+ {this.state.result.length > 0 && mode !== "suggestions" && mode !== "multi_suggestions" && (
+
+ )}
+
+ {this.state.result.length > 0 && this.state.intermediate_url_list && (
+
+
{this.context("Intermediate data")}:
+
+ {map(this.state.intermediate_url_list, intermediate_url => (
+
+ ))}
- ))}
-
+
+ )}
- )}
-
- {this.state.result.length <= 0 && (
-
- {this.context("No data for cognate analysis")}.
-
- )}
+ {this.state.result.length <= 0 && (
+
+ {this.context("No data for cognate analysis")}.
+
+ )}
- {this.state.suggestion_list && (
-
-
- {this.state.suggestion_list.length} {this.context("suggestions")}:
-
+ {this.state.suggestion_list && (
+
+
+ {this.state.suggestion_list.length} {this.context("suggestions")}:
+
- {this.props.user.id === undefined ? (
-
- {this.context("Unauthorized user")}
-
- {this.context(
- "Only authorized users can create new cognate connections based on cognate suggestions."
- )}
-
+ {this.props.user.id === undefined ? (
+
+ {this.context("Unauthorized user")}
+
+ {this.context(
+ "Only authorized users can create new cognate connections based on cognate suggestions."
+ )}
+
+
+ ) : (
+
{this.suggestions_render()}
+ )}
- ) : (
-
{this.suggestions_render()}
)}
-
+ >
)}
{this.state.plotly_data.length > 0 && (
@@ -3141,7 +3249,7 @@ class CognateAnalysisModal extends React.Component {
)}
- {! /swadesh$/.test(mode) && ! /morphology$/.test(mode) && (
+ {! /swadesh$/.test(mode) && ! /morphology$/.test(mode) && ! /complex_distance$/.test(mode) && (
) || (
@@ -3160,7 +3268,9 @@ CognateAnalysisModal.propTypes = {
perspectiveId: PropTypes.array.isRequired,
closeModal: PropTypes.func.isRequired,
computeCognateAnalysis: PropTypes.func.isRequired,
- computeSwadeshAnalysis: PropTypes.func.isRequired
+ computeSwadeshAnalysis: PropTypes.func.isRequired,
+ computeMorphCognateAnalysis: PropTypes.func.isRequired,
+ computeComplexDistance: PropTypes.func.isRequired
};
export default compose(
@@ -3173,6 +3283,7 @@ export default compose(
graphql(computeCognateAnalysisMutation, { name: "computeCognateAnalysis" }),
graphql(computeSwadeshAnalysisMutation, { name: "computeSwadeshAnalysis" }),
graphql(computeMorphCognateAnalysisMutation, { name: "computeMorphCognateAnalysis" }),
+ graphql(computeComplexDistanceMutation, { name: "computeComplexDistance" }),
graphql(connectMutation, { name: "connectGroup" }),
withApollo
)(CognateAnalysisModal);
diff --git a/src/pages/ComplexDistance/index.js b/src/pages/ComplexDistance/index.js
deleted file mode 100644
index 3693338c..00000000
--- a/src/pages/ComplexDistance/index.js
+++ /dev/null
@@ -1,149 +0,0 @@
-import { connect } from "react-redux";
-import { Button, Dimmer, Icon, Input, Label, Loader, Message, Segment } from "semantic-ui-react";
-import { gql, useMutation } from "@apollo/client";
-import React, { useContext, useState, useEffect } from "react";
-
-import "./style.scss";
-
-import TranslationContext from "Layout/TranslationContext";
-
-const complexDistanceMutation = gql`
- mutation complexDistance (
- $resultPool: [ObjectVal]!
- $debugFlag: Boolean
- ) {
- complex_distance(
- result_pool: $resultPool
- debug_flag: $debugFlag
- ) {
- result
- minimum_spanning_tree
- embedding_2d
- embedding_3d
- language_name_list
- message
- triumph
- }
- }
-`;
-
-const ComplexDistance = connect(state => state.user)(({user}) => {
-
- const [cleanResult, setCleanResult] = useState(false);
- const [fileSuite, setFileSuite] = useState(null);
- const [getComplexDistance, { data, error, loading }] = useMutation(complexDistanceMutation);
-
- useEffect(() => setCleanResult(false), [loading, data]);
- const getTranslation = useContext(TranslationContext);
-
- const debugFlag = false;
-
- const runMutation = () => {
-
- if (loading)
- return;
-
- const resultPool = new Array(fileSuite.length);
-
- for (const [index, file] of fileSuite.entries()) {
- const reader = new FileReader();
- reader.onload = () => {
- resultPool[index] = JSON.parse(reader.result);
- if ((index + 1) == fileSuite.length) {
- getComplexDistance(
- { variables:
- {
- resultPool,
- debugFlag
- }
- }
- );
- }
- };
- reader.readAsText(file);
- }
- }
-
- return (
-
- {(user.id === undefined) && !loading ? (
-
- {getTranslation("Please sign in")}
- {getTranslation("This page is available for registered users only")}
-
- ) : loading ? (
-
-
- {getTranslation("Loading")}...
-
-
- ) : (
-
{ if (e.key === 'Enter') document.getElementById("get-result").click(); }} tabIndex="0">
-
- { getTranslation(
- fileSuite ? "Json file(s) for complex result:" : "Please select result file(s) for calculating."
- )}
-
-
- { fileSuite && fileSuite.map(({ name: fileName }) => (
-
-
- { fileName }
-
- ))}
-
- document.getElementById("file-select").click()}>
- {`${getTranslation("Browse")}...`}
-
-
- setFileSuite(Array.from(e.target.files))}
- />
-
-
- { error && !cleanResult && (
-
- {getTranslation("Request error")}
-
- {getTranslation("Please contact developers at")}
-
- {getTranslation("Support@Telegram")}
-
- {getTranslation("or at")}
- {getTranslation("Lingvodoc Github")}
- .
-
- {error.message}
-
- )}
- { data && !data.complex_distance.triumph && !error && !cleanResult && (
-
- {getTranslation("Request error")}
- {data.complex_distance.message}
-
- )}
- { data && data.complex_distance.triumph && !error && !cleanResult && (
-
- {getTranslation("Summary distance matrix grouped and merged by languages:")}
-
-
- )}
-
- )}
-
- );
-})
-
-ComplexDistance.contextType = TranslationContext;
-
-export default ComplexDistance;
diff --git a/src/pages/ComplexDistance/style.scss b/src/pages/ComplexDistance/style.scss
deleted file mode 100644
index 47d93f0b..00000000
--- a/src/pages/ComplexDistance/style.scss
+++ /dev/null
@@ -1,3 +0,0 @@
-pre {
- white-space: pre-wrap;
-}
\ No newline at end of file
diff --git a/src/pages/ToolsRoute/index.js b/src/pages/ToolsRoute/index.js
index 9eae121d..50253875 100644
--- a/src/pages/ToolsRoute/index.js
+++ b/src/pages/ToolsRoute/index.js
@@ -1,7 +1,8 @@
import React, { useContext } from "react";
import { connect } from "react-redux";
import { Link } from "react-router-dom";
-
+import { bindActionCreators } from "redux";
+import { Button } from "semantic-ui-react";
import Footer from "components/Footer";
import TranslationContext from "Layout/TranslationContext";
@@ -13,9 +14,11 @@ import imageSearch from "../../images/location_search.svg";
import imageTranslations from "../../images/text_field.svg";
import imageValency from "../../images/verb_valency.svg";
+import { openModal as cognateAnalysisOpenModal } from "ducks/cognateAnalysis";
+
import "./styles.scss";
-function ToolsRoute(props) {
+function ToolsRoute({ user, actions }) {
const getTranslation = useContext(TranslationContext);
return (
@@ -42,7 +45,7 @@ function ToolsRoute(props) {
{getTranslation("Library of linguistic maps")}
- {props.user && props.user.id && (
+ {user && user.id && (
{getTranslation("Language genetic proximity map")}
@@ -52,35 +55,35 @@ function ToolsRoute(props) {
{getTranslation("Edit of the dialects classification")}
- {props.user && props.user.id == 1 && (
+ {user && user.id == 1 && (
{getTranslation("Edit translations")}
)}
- {props.user.id !== undefined && (
+ {user.id !== undefined && (
{getTranslation("Verb valency")}
)}
- {props.user.id !== undefined && (
+ {user.id !== undefined && (
{getTranslation("Adverb specificity")}
)}
- {props.user.id !== undefined && (
+ {user.id !== undefined && (
{getTranslation("Cognates summary")}
)}
- {props.user.id !== undefined && (
-
+ {user.id !== undefined && (
+
actions.cognateAnalysisOpenModal(null, "complex_distance")}>
{getTranslation("Get complex distance between languages")}
-
+
)}
@@ -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);