Commit 74758667 authored by Matija Obreza's avatar Matija Obreza

Merge branch '408-location-and-timing' into 'master'

Resolve "Location and timing"

Closes #408

See merge request !323
parents d77ec4ef 5834bf8b
Pipeline #7481 passed with stages
in 5 minutes and 50 seconds
...@@ -25,6 +25,8 @@ const optionsBase = { ...@@ -25,6 +25,8 @@ const optionsBase = {
return value.toUpperCase(); return value.toUpperCase();
} else if (format === 'lowercase') { } else if (format === 'lowercase') {
return value.toLowerCase(); return value.toLowerCase();
} else if (format === 'number') {
return value && value.toLocaleString();
} }
return value; return value;
}, },
......
...@@ -684,7 +684,8 @@ ...@@ -684,7 +684,8 @@
"excel": "Copy and paste the table from Excel into the text field \"List of accessions described in the dataset\".", "excel": "Copy and paste the table from Excel into the text field \"List of accessions described in the dataset\".",
"listDescribed": "List of accessions described in the dataset", "listDescribed": "List of accessions described in the dataset",
"placeholder": "Paste accessions data here (comma separated)", "placeholder": "Paste accessions data here (comma separated)",
"rowCount": "Accessions {{row, numeric}} rows" "rowCount": "{{count, number}} accession",
"rowCount_plural": "{{count, number}} accessions"
}, },
"pastingTraits": { "pastingTraits": {
"title": "Select descriptors in batch", "title": "Select descriptors in batch",
...@@ -868,7 +869,7 @@ ...@@ -868,7 +869,7 @@
"basicInfo": "Basic information", "basicInfo": "Basic information",
"import": { "import": {
"title": "Upload new descriptors", "title": "Upload new descriptors",
"uploaded": "Uploaded {{count}} descriptor definitions", "uploaded": "Uploaded {{count, number}} descriptor definitions",
"noDescriptors": "No descriptors uploaded", "noDescriptors": "No descriptors uploaded",
"error": "error", "error": "error",
"error_plural": "errors", "error_plural": "errors",
......
...@@ -104,7 +104,8 @@ ...@@ -104,7 +104,8 @@
"excel": "Copy and paste the table from Excel into the text field \"List of accessions described in the dataset\".", "excel": "Copy and paste the table from Excel into the text field \"List of accessions described in the dataset\".",
"listDescribed": "List of accessions described in the dataset", "listDescribed": "List of accessions described in the dataset",
"placeholder": "Paste accessions data here (comma separated)", "placeholder": "Paste accessions data here (comma separated)",
"rowCount": "Accessions {{rows, numeric}} rows" "rowCount": "{{count, number}} accession",
"rowCount_plural": "{{count, number}} accessions"
}, },
"pastingTraits": { "pastingTraits": {
"title": "Select descriptors in batch", "title": "Select descriptors in batch",
......
...@@ -141,7 +141,7 @@ class ListOfAccession extends React.Component<IListOfAccession, any> { ...@@ -141,7 +141,7 @@ class ListOfAccession extends React.Component<IListOfAccession, any> {
{ uploading && <Loading message={ uploadText } /> } { uploading && <Loading message={ uploadText } /> }
<h3>{ t('datasets.dashboard.p.stepper.listOfAccessions.rowCount', {rows: accessionRefs ? accessionRefs.length : 0}) }</h3> <h3>{ t('datasets.dashboard.p.stepper.listOfAccessions.rowCount', {count: accessionRefs ? accessionRefs.length : 0}) }</h3>
<AccessionRefsTable accessionRefs={ accessionRefs } /> <AccessionRefsTable accessionRefs={ accessionRefs } />
</div> </div>
); );
......
...@@ -11,7 +11,6 @@ import GeoService from 'service/GeoService'; ...@@ -11,7 +11,6 @@ import GeoService from 'service/GeoService';
// actions // actions
import {autocompleteGeoTerm} from 'vocabulary/actions/dashboard'; import {autocompleteGeoTerm} from 'vocabulary/actions/dashboard';
// ui // ui
import Markdown from 'ui/catalog/markdown';
import Menu from '@material-ui/core/Menu'; import Menu from '@material-ui/core/Menu';
import MenuItem from '@material-ui/core/MenuItem'; import MenuItem from '@material-ui/core/MenuItem';
import Input from '@material-ui/core/Input'; import Input from '@material-ui/core/Input';
...@@ -73,17 +72,15 @@ class SelectCountryInternal extends React.Component<ISelectCountryCodeInternal, ...@@ -73,17 +72,15 @@ class SelectCountryInternal extends React.Component<ISelectCountryCodeInternal,
public componentWillMount() { public componentWillMount() {
if (this.props.input.value) { if (this.props.input.value) {
GeoService.get(this.props.input.value).then((dl) => { this.setState({pickerList: {code: this.props.input.value}});
this.setState({pickerList: {code: dl.code, title: dl.title}});
});
} }
this.props.autocomplete('').then((data) => {
this.setState({suggestions: data});
});
} }
public componentWillReceiveProps(nextProps) { public componentWillReceiveProps(nextProps) {
if (nextProps.input.value) { if (! nextProps.input.value) {
return this.setState({pickerList: null});
}
if (nextProps.input.value !== this.props.input.value) {
const code = nextProps.input.value; const code = nextProps.input.value;
const countryCodeTerm = this.state.suggestions.find((dl) => dl.code === code); const countryCodeTerm = this.state.suggestions.find((dl) => dl.code === code);
if (countryCodeTerm) { if (countryCodeTerm) {
...@@ -93,8 +90,6 @@ class SelectCountryInternal extends React.Component<ISelectCountryCodeInternal, ...@@ -93,8 +90,6 @@ class SelectCountryInternal extends React.Component<ISelectCountryCodeInternal,
this.setState({pickerList: {code: dl.code, title: dl.title}}); this.setState({pickerList: {code: dl.code, title: dl.title}});
}); });
} }
} else {
this.setState({pickerList: null});
} }
} }
...@@ -112,10 +107,7 @@ class SelectCountryInternal extends React.Component<ISelectCountryCodeInternal, ...@@ -112,10 +107,7 @@ class SelectCountryInternal extends React.Component<ISelectCountryCodeInternal,
} } } }
> >
{ {
this.state.pickerList ? ( this.state.pickerList ? <MenuItem value={ inputValue }>{ this.state.pickerList.code }</MenuItem> : null
<MenuItem value={ inputValue }>
<Markdown basic source={ this.state.pickerList.title }/>
</MenuItem>) : null
} }
</Select> </Select>
<Menu <Menu
...@@ -132,9 +124,7 @@ class SelectCountryInternal extends React.Component<ISelectCountryCodeInternal, ...@@ -132,9 +124,7 @@ class SelectCountryInternal extends React.Component<ISelectCountryCodeInternal,
</MenuItem> </MenuItem>
{ {
this.state.suggestions.map((cCT, index) => ( this.state.suggestions.map((cCT, index) => (
<MenuItem onClick={ this.select(cCT.code) } key={ `country-iso3-${index}` }> <MenuItem onClick={ this.select(cCT.code) } key={ `country-iso3-${index}` }>{ `${cCT.code} - ${cCT.title}` }</MenuItem>
<Markdown basic source={ cCT.title }/>
</MenuItem>
)) ))
} }
</Menu> </Menu>
......
...@@ -19,9 +19,10 @@ interface IFormMapProps extends React.ClassAttributes<any> { ...@@ -19,9 +19,10 @@ interface IFormMapProps extends React.ClassAttributes<any> {
userCountry: any, userCountry: any,
stateProvince: any, stateProvince: any,
verbatimLocality: any, verbatimLocality: any,
countryCode: any,
}]; }];
onMouseOut: () => any; onMouseOut: () => any;
checkGeonames: (lat, lng) => Promise<{userCountry: string, stateProvince: string, verbatimLocality?: string}>; checkGeonames: (lat, lng) => Promise<{countryCode: string, userCountry: string, stateProvince: string, verbatimLocality?: string}>;
} }
const styleSheet = { const styleSheet = {
...@@ -65,7 +66,7 @@ class FormMap extends React.Component<IFormMapProps, any> { ...@@ -65,7 +66,7 @@ class FormMap extends React.Component<IFormMapProps, any> {
protected onMapClick = (e) => { protected onMapClick = (e) => {
const {checkGeonames} = this.props; const {checkGeonames} = this.props;
const {decimalLatitude, decimalLongitude, userCountry, stateProvince, verbatimLocality} = this.getCurrentLocation(); const {decimalLatitude, decimalLongitude, userCountry, countryCode, stateProvince, verbatimLocality} = this.getCurrentLocation();
const {lat, lng} = e.latlng; const {lat, lng} = e.latlng;
...@@ -74,6 +75,7 @@ class FormMap extends React.Component<IFormMapProps, any> { ...@@ -74,6 +75,7 @@ class FormMap extends React.Component<IFormMapProps, any> {
checkGeonames(lat, lng) checkGeonames(lat, lng)
.then((value) => { .then((value) => {
countryCode.input.onChange(value.countryCode);
userCountry.input.onChange(value.userCountry); userCountry.input.onChange(value.userCountry);
stateProvince.input.onChange(value.stateProvince); stateProvince.input.onChange(value.stateProvince);
......
...@@ -19,6 +19,7 @@ import Validators from 'utilities/Validators'; ...@@ -19,6 +19,7 @@ import Validators from 'utilities/Validators';
import IconButton from '@material-ui/core/IconButton'; import IconButton from '@material-ui/core/IconButton';
import DeleteIcon from '@material-ui/icons/Delete'; import DeleteIcon from '@material-ui/icons/Delete';
import GeoNamesService from 'service/GeoNamesService'; import GeoNamesService from 'service/GeoNamesService';
import GeoService from 'service/GeoService';
import CountryCodePicker from './CountryCodePicker'; import CountryCodePicker from './CountryCodePicker';
...@@ -44,7 +45,8 @@ class LocationForm extends React.Component<ILocationFormProps, any> { ...@@ -44,7 +45,8 @@ class LocationForm extends React.Component<ILocationFormProps, any> {
if (value[property] !== location[property]) { if (value[property] !== location[property]) {
this.checkGeonames(value.decimalLatitude, value.decimalLongitude) this.checkGeonames(value.decimalLatitude, value.decimalLongitude)
.then(({userCountry, stateProvince, verbatimLocality}) => { .then(({countryCode, userCountry, stateProvince, verbatimLocality}) => {
changeFieldValue(`locations[${index}].countryCode`, countryCode);
changeFieldValue(`locations[${index}].userCountry`, userCountry); changeFieldValue(`locations[${index}].userCountry`, userCountry);
changeFieldValue(`locations[${index}].stateProvince`, stateProvince); changeFieldValue(`locations[${index}].stateProvince`, stateProvince);
changeFieldValue(`locations[${index}].verbatimLocality`, verbatimLocality); changeFieldValue(`locations[${index}].verbatimLocality`, verbatimLocality);
...@@ -63,12 +65,13 @@ class LocationForm extends React.Component<ILocationFormProps, any> { ...@@ -63,12 +65,13 @@ class LocationForm extends React.Component<ILocationFormProps, any> {
this.props.createLocation(this.props.dataset.uuid); this.props.createLocation(this.props.dataset.uuid);
} }
protected checkGeonames = (lat, lng): Promise<{userCountry: string, stateProvince: string, verbatimLocality?: string}> => { protected checkGeonames = (lat, lng): Promise<{countryCode: string, userCountry: string, stateProvince: string, verbatimLocality?: string}> => {
if (!isNumeric(lat) || !isNumeric(lng)) { if (!isNumeric(lat) || !isNumeric(lng)) {
return Promise.resolve({ return Promise.resolve({
userCountry: null, userCountry: null,
stateProvince: null, stateProvince: null,
countryCode: null,
}); });
} }
...@@ -82,11 +85,15 @@ class LocationForm extends React.Component<ILocationFormProps, any> { ...@@ -82,11 +85,15 @@ class LocationForm extends React.Component<ILocationFormProps, any> {
const description = [name, adminName5, adminName4, adminName3].filter(Boolean).join(', '); const description = [name, adminName5, adminName4, adminName3].filter(Boolean).join(', ');
return Promise.resolve({ return GeoService.getCountry(values[0].data.countryCode)
userCountry: values[0].data.countryName, .then((country) => {
stateProvince: adminName1, return Promise.resolve({
verbatimLocality: description, countryCode: country.code3,
}); userCountry: values[0].data.countryName,
stateProvince: adminName1,
verbatimLocality: description,
});
});
}); });
} }
...@@ -119,6 +126,13 @@ class LocationForm extends React.Component<ILocationFormProps, any> { ...@@ -119,6 +126,13 @@ class LocationForm extends React.Component<ILocationFormProps, any> {
checkGeonames={ this.checkGeonames } checkGeonames={ this.checkGeonames }
component={ FormMap } component={ FormMap }
/> />
<Field
name={ `${location}.countryCode` }
component={ CountryCodePicker }
label={ t('datasets.dashboard.p.stepper.location.iso') }
placeholder={ t('datasets.dashboard.p.stepper.location.isoPlaceholder') }
validate={ [ Validators.maxLength(3) ] }
/>
<Field <Field
required required
name={ `${location}.userCountry` } name={ `${location}.userCountry` }
...@@ -181,13 +195,6 @@ class LocationForm extends React.Component<ILocationFormProps, any> { ...@@ -181,13 +195,6 @@ class LocationForm extends React.Component<ILocationFormProps, any> {
component={ MarkdownField } component={ MarkdownField }
label={ t('datasets.dashboard.p.stepper.location.description') } label={ t('datasets.dashboard.p.stepper.location.description') }
/> />
<Field
name={ `${location}.countryCode` }
component={ CountryCodePicker }
label={ t('datasets.dashboard.p.stepper.location.iso') }
placeholder={ t('datasets.dashboard.p.stepper.location.isoPlaceholder') }
validate={ [ Validators.maxLength(3) ] }
/>
</div> </div>
)) } )) }
<div className="back-white m-20"> <div className="back-white m-20">
......
...@@ -77,7 +77,7 @@ ...@@ -77,7 +77,7 @@
"basicInfo": "Basic information", "basicInfo": "Basic information",
"import": { "import": {
"title": "Upload new descriptors", "title": "Upload new descriptors",
"uploaded": "Uploaded {{count}} descriptor definitions", "uploaded": "Uploaded {{count, number}} descriptor definitions",
"noDescriptors": "No descriptors uploaded", "noDescriptors": "No descriptors uploaded",
"error": "error", "error": "error",
"error_plural": "errors", "error_plural": "errors",
......
...@@ -4,9 +4,11 @@ import * as UrlTemplate from 'url-template'; ...@@ -4,9 +4,11 @@ import * as UrlTemplate from 'url-template';
import {axiosBackend} from 'utilities/requestUtils'; import {axiosBackend} from 'utilities/requestUtils';
import VocabularyTerm from 'model/vocabulary/VocabularyTerm'; import VocabularyTerm from 'model/vocabulary/VocabularyTerm';
import Country from 'model/geo/Country';
const URL_AUTOCOMPLETE_GEO_TERM = `/api/v1/geo/iso3166/autocomplete`; const URL_AUTOCOMPLETE_GEO_TERM = `/api/v1/geo/iso3166/autocomplete`;
const URL_GET = UrlTemplate.parse(`/api/v1/geo/iso3166/{code}`); const URL_GET = UrlTemplate.parse(`/api/v1/geo/iso3166/{code}`);
const URL_GET_COUNTRY = UrlTemplate.parse(`/api/v1/geo/country/{iso3code}`);
/* /*
* Defined in Swagger as 'geo' * Defined in Swagger as 'geo'
...@@ -52,6 +54,24 @@ class GeoService { ...@@ -52,6 +54,24 @@ class GeoService {
}).then(({ data }) => data as any); }).then(({ data }) => data as any);
} }
/**
* getCountry at /api/v1/geo/country/{iso3code}
*
* @param iso3code iso3code
*/
public static getCountry(iso3code: string): Promise<Country> {
const apiUrl = URL_GET_COUNTRY.expand({iso3code});
// console.log(`Fetching from ${apiUrl}`);
const content = { /* No content in request body */ };
return axiosBackend.request({
url: apiUrl,
method: 'GET',
...content,
}).then(({ data }) => data as Country);
}
} }
export default GeoService; export default GeoService;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment