Commit 6dd10f3c authored by pierre.tremouilhac's avatar pierre.tremouilhac
Browse files

Merge branch 'development' into '243-report-format-spaces-before-rf'

# Conflicts:
#   app/assets/javascripts/components/report/SectionSiSynthesis.js
parents 26e944dc 5307212f
......@@ -3,7 +3,8 @@ require 'capistrano/setup'
# Include default deployment tasks
require 'capistrano/deploy'
require "capistrano/scm/git"
install_plugin Capistrano::SCM::Git
require 'capistrano/rvm' # Ruby version manager
require 'capistrano/nvm' # Node version manager
require 'capistrano/npm' # Node package manager
......@@ -12,6 +13,7 @@ require 'capistrano/rails/assets'
require 'capistrano/rails/migrations'
#require 'capistrano/delayed_job'
require 'whenever/capistrano'
require 'slackistrano/capistrano'
require_relative 'lib/slackistrano_custom_messaging'
# Load custom tasks from `lib/capistrano/tasks` if you have any defined
Dir.glob('lib/capistrano/tasks/*.rake').each { |r| import r }
......@@ -99,6 +99,7 @@ gem 'paranoia', '~> 2.0'
gem 'backup'
gem 'whenever', require: false
gem 'rubocop', require: false
gem 'yaml_db'
gem 'ruby-ole'
......@@ -160,13 +161,14 @@ group :development, :test do
# Remove all assets requests
gem 'quiet_assets'
gem 'capistrano', '3.4.1'
gem 'capistrano', '3.9.1'
gem 'capistrano-bundler'
gem 'capistrano-npm'
gem 'capistrano-nvm', require: false
gem 'capistrano-rails'
gem 'capistrano-rvm'
# gem 'capistrano3-delayed-job'
gem 'slackistrano'
end
group :test do
......
......@@ -101,11 +101,14 @@ GEM
thread_safe (~> 0.3, >= 0.3.4)
tzinfo (~> 1.1)
addressable (2.4.0)
airbrussh (1.3.0)
sshkit (>= 1.6.1, != 1.7.0)
ancestry (2.1.0)
activerecord (>= 3.0.0)
archive-zip (0.7.0)
io-like (~> 0.3.0)
arel (6.0.4)
ast (2.3.0)
autoprefixer-rails (6.3.6)
execjs
awesome_print (1.6.1)
......@@ -139,10 +142,11 @@ GEM
uniform_notifier (~> 1.10.0)
byebug (8.2.5)
callsite (0.0.11)
capistrano (3.4.1)
capistrano (3.9.1)
airbrussh (>= 1.0.0)
i18n
rake (>= 10.0.0)
sshkit (~> 1.3)
sshkit (>= 1.9.0)
capistrano-bundler (1.1.4)
capistrano (~> 3.1)
sshkit (~> 1.2)
......@@ -291,7 +295,7 @@ GEM
httparty (0.13.7)
json (~> 1.8)
multi_xml (>= 0.5.2)
i18n (0.7.0)
i18n (0.8.6)
i18n_data (0.7.0)
ice_nine (0.11.2)
io-like (0.3.0)
......@@ -353,7 +357,7 @@ GEM
net-ssh (>= 2.6.5)
net-sftp (2.1.2)
net-ssh (>= 2.6.5)
net-ssh (3.1.1)
net-ssh (4.2.0)
nokogiri (1.6.7.2)
mini_portile2 (~> 2.0.0.rc2)
open4 (1.3.4)
......@@ -364,14 +368,18 @@ GEM
cocaine (~> 0.5.5)
mime-types
mimemagic (~> 0.3.0)
parallel (1.11.2)
paranoia (2.1.5)
activerecord (~> 4.0)
parser (2.4.0.0)
ast (~> 2.2)
pdf-core (0.6.1)
pg (0.18.4)
pg_search (1.0.5)
activerecord (>= 3.1)
activesupport (>= 3.1)
arel
powerpack (0.1.1)
prawn (2.1.0)
pdf-core (~> 0.6.1)
ttfunk (~> 1.4.0)
......@@ -416,7 +424,9 @@ GEM
activesupport (= 4.2.0)
rake (>= 0.8.7)
thor (>= 0.18.1, < 2.0)
rake (12.0.0)
rainbow (2.2.2)
rake
rake (12.1.0)
rb-fsevent (0.9.7)
rb-inotify (0.9.7)
ffi (>= 0.5.0)
......@@ -449,8 +459,16 @@ GEM
rspec-support (~> 3.4.0)
rspec-support (3.4.1)
rtf (0.3.3)
rubocop (0.49.1)
parallel (~> 1.10)
parser (>= 2.3.3.1, < 3.0)
powerpack (~> 0.1)
rainbow (>= 1.99.1, < 3.0)
ruby-progressbar (~> 1.7)
unicode-display_width (~> 1.0, >= 1.0.1)
ruby-mailchecker (3.0.27)
ruby-ole (1.2.12)
ruby-progressbar (1.8.1)
ruby_parser (3.8.4)
sexp_processor (~> 4.1)
rubyzip (1.1.7)
......@@ -483,6 +501,8 @@ GEM
skinny (0.2.4)
eventmachine (~> 1.0.0)
thin (>= 1.5, < 1.7)
slackistrano (3.8.1)
capistrano (>= 3.8.1)
spring (1.7.1)
sprite-factory (1.7.1)
sprockets (3.7.1)
......@@ -493,7 +513,7 @@ GEM
activesupport (>= 4.0)
sprockets (>= 3.0.0)
sqlite3 (1.3.11)
sshkit (1.10.0)
sshkit (1.14.0)
net-scp (>= 1.1.2)
net-ssh (>= 2.8.0)
stackprof (0.2.10)
......@@ -517,6 +537,7 @@ GEM
thread_safe (~> 0.1)
uglifier (3.0.0)
execjs (>= 0.3.0, < 3)
unicode-display_width (1.2.1)
unicode_utils (1.4.0)
uniform_notifier (1.10.0)
virtus (1.0.5)
......@@ -562,7 +583,7 @@ DEPENDENCIES
browserify-rails (~> 3.0.1)
bullet
byebug
capistrano (= 3.4.1)
capistrano (= 3.9.1)
capistrano-bundler
capistrano-npm
capistrano-nvm
......@@ -621,12 +642,14 @@ DEPENDENCIES
rqrcode
rspec-rails
rtf
rubocop
ruby-mailchecker
ruby-ole
sablon!
sass-rails (~> 5.0, >= 5.0.6)
sdoc (~> 0.4.0)
selenium-webdriver (~> 3.0.5)
slackistrano
spring
stackprof
swot
......@@ -640,4 +663,4 @@ DEPENDENCIES
yaml_db
BUNDLED WITH
1.15.3
1.15.4
......@@ -42,6 +42,26 @@ module Chemotion
molecule.load_cas if molecule
molecule
end
desc 'return names of the molecule'
params do
requires :inchikey, type: String, desc: 'Molecule inchikey'
optional :new_name, type: String, desc: 'New molecule_name'
end
get :names do
inchikey = params[:inchikey]
new_name = params[:new_name]
mol = Molecule.find_by(inchikey: inchikey)
return [] if mol.blank?
user_id = current_user.id
mol.create_molecule_name_by_user(new_name, user_id) if new_name.present?
mol.molecule_names.map do |mn|
{ id: mn.id, name: mn.name }
end
end
end
end
end
......@@ -13,15 +13,15 @@ module Chemotion
def excluded_field
%w(
id molecule_id created_by deleted_at
user_id fingerprint_id sample_svg_file xref
id molecule_id created_by deleted_at identifier molecule_name_id
user_id fingerprint_id sample_svg_file xref impurities ancestry
)
end
def included_field
%w(
molecule.cano_smiles molecule.sum_formular
molecule.inchistring molecule.molecular_weight
molecule.cano_smiles molecule.sum_formular molecule_name
molecule.inchistring molecule.molecular_weight molecule.inchikey
)
end
......@@ -31,11 +31,11 @@ module Chemotion
elements = "#{type}s".to_sym
if checkedAll
Collection.find(currentCollection)
.send(elements).where.not(id: uncheckedIds)
.send(elements).where.not(id: uncheckedIds).order(updated_at: :desc)
else
Collection.find(currentCollection)
.send(elements).where(id: checkedIds)
.order("position(samples.id::text in '#{checkedIds}')")
.order("position(#{type}s.id::text in '#{checkedIds}')")
end
end
......@@ -69,7 +69,7 @@ module Chemotion
requires :uncheckedIds, type: Array
requires :checkedAll, type: Boolean
requires :currentCollection, type: Integer
requires :removedColumns, type: Array
requires :removedColumns, type: Array[String]
requires :exportType, type: Integer
end
post :export_samples_from_selections do
......@@ -101,7 +101,7 @@ module Chemotion
type, checkedAll, checkedIds, uncheckedIds, currentCollection
)
samples = if type == 'sample'
elements.includes(:molecule)
elements.includes([:molecule,:molecule_name])
elsif type == 'reaction'
elements.map { |r|
r.starting_materials + r.reactants + r.products
......
......@@ -247,6 +247,7 @@ module Chemotion
optional :residues, type: Array
optional :elemental_compositions, type: Array
optional :xref, type: Hash
optional :molecule_name_id, type: Integer
requires :container, type: Hash
#use :root_container_params
end
......@@ -316,6 +317,7 @@ module Chemotion
optional :residues, type: Array
optional :elemental_compositions, type: Array
optional :xref, type: Hash
optional :molecule_name_id, type: Integer
requires :container, type: Hash
end
post do
......@@ -343,7 +345,8 @@ module Chemotion
residues: params[:residues],
elemental_compositions: params[:elemental_compositions],
created_by: current_user.id,
xref: params[:xref]
xref: params[:xref],
molecule_name_id: params[:molecule_name_id]
}
# otherwise ActiveRecord::UnknownAttributeError appears
......
import React from 'react';
import ReactDOM from 'react-dom';
import PropTypes from 'prop-types';
import Quill from 'quill';
import Delta from 'quill-delta';
......@@ -256,18 +257,18 @@ export default class QuillEditor extends React.Component {
}
QuillEditor.propTypes = {
value: React.PropTypes.object,
customToolbar: React.PropTypes.object,
toolbarSymbol: React.PropTypes.array,
theme: React.PropTypes.string,
height: React.PropTypes.string,
disabled: React.PropTypes.bool,
onChange: React.PropTypes.func,
value: PropTypes.object,
customToolbar: PropTypes.oneOfType([PropTypes.object, PropTypes.string]),
toolbarSymbol: PropTypes.array,
theme: PropTypes.string,
height: PropTypes.string,
disabled: PropTypes.bool,
onChange: PropTypes.func,
};
QuillEditor.defaultProps = {
value: {},
customToolbar: "",
customToolbar: '',
toolbarSymbol: [],
theme: 'snow',
height: '230px',
......
......@@ -150,7 +150,7 @@ const HeaderNormal = ({ sample, container, mode, readOnly, isDisabled, serial,
const kind = container.extended_metadata.kind || '';
const status = container.extended_metadata.status || '';
const previewImg = previewImage(container);
const content = container.extended_metadata.content;
const content = container.extended_metadata.content || { ops: [{ insert: '' }] };
const contentOneLine = {
ops: content.ops.map((x) => {
const c = Object.assign({}, x);
......
......@@ -2,7 +2,7 @@ import React from 'react';
import { Button, Checkbox, FormGroup, FormControl, InputGroup, ControlLabel,
Table, Glyphicon } from 'react-bootstrap';
import Select from 'react-select';
import DetailActions from './actions/DetailActions';
import NumeralInputWithUnitsCompo from './NumeralInputWithUnitsCompo';
import { solventOptions } from './staticDropdownOptions/options';
......@@ -13,9 +13,16 @@ export default class SampleForm extends React.Component {
this.state = {
sample: props.sample,
molarityBlocked: (props.sample.molarity_value || 0) <= 0,
isMolNameLoading: false,
};
this.handleFieldChanged = this.handleFieldChanged.bind(this);
this.updateMolName = this.updateMolName.bind(this);
this.addMolName = this.addMolName.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({ isMolNameLoading: false });
}
handleAmountChanged(amount) {
......@@ -74,18 +81,53 @@ export default class SampleForm extends React.Component {
return (<span />);
}
openMolName(moleculeNames) {
const { sample } = this.state;
if (moleculeNames.length <= 1) {
this.setState({ isMolNameLoading: true });
DetailActions.updateMoleculeNames(sample);
}
}
addMolName(moleculeName) {
const { sample } = this.state;
this.setState({ isMolNameLoading: true });
DetailActions.updateMoleculeNames(sample, moleculeName.label);
}
updateMolName(e) {
const { sample } = this.state;
sample.molecule_name = e;
this.props.parent.setState({ sample });
}
moleculeInput(sample) {
const mnos = sample.molecule_names;
const mno = sample.molecule_name;
let moleculeNames = mno ? [mno] : [];
if (sample && mnos) {
moleculeNames = mnos.map(n => (
Object.assign({ label: n.name, value: n.id })
));
}
const onOpenMolName = () => this.openMolName(moleculeNames);
return (
<FormGroup style={{ width: '100%' }}>
<ControlLabel>Molecule</ControlLabel>
<InputGroup>
<FormControl
type="text"
ref="moleculeInput"
value={sample.molecule_name}
<Select.Creatable
name="moleculeName"
multi={false}
disabled={!sample.can_update}
readOnly={!sample.can_update}
onChange={(e) => this.handleFieldChanged(sample, 'molecule_iupac_name', e.target.value)}
options={moleculeNames}
onOpen={onOpenMolName}
onChange={this.updateMolName}
isLoading={this.state.isMolNameLoading}
value={mno && mno.value}
onNewOptionClick={this.addMolName}
clearable={false}
/>
<InputGroup.Button>
{this.structureEditorButton(!sample.can_update)}
......
......@@ -29,6 +29,23 @@ class DetailActions {
})
}
}
updateMoleculeNames(sample, newMolName = '') {
const inchikey = sample.molecule.inchikey;
if (!inchikey) { return null; }
return (dispatch) => {
MoleculesFetcher
.updateNames(inchikey, newMolName)
.then((result) => {
const mn = result.find(r => r.name === newMolName);
if (mn) sample.molecule_name = { label: mn.name, value: mn.id };
sample.molecule_names = result;
dispatch(sample);
})
.catch(errorMessage => console.log(errorMessage));
};
}
}
export default alt.createActions(DetailActions)
import React from 'react'
import {Table} from 'react-bootstrap'
import PropTypes from 'prop-types'
const CheckBoxs = ({items, toggleCheckbox, toggleCheckAll, checkedAll}) => {
const CheckBoxs = ({items, toggleCheckbox, toggleCheckAll, checkedAll, customClass = 'check-box-list' , customStyle = {}}) => {
let checkBoxs = items.map( (setting, i) => {
const {text, checked} = setting
return(
......@@ -11,7 +12,10 @@ const CheckBoxs = ({items, toggleCheckbox, toggleCheckAll, checkedAll}) => {
toggleCheckbox={toggleCheckbox.bind(null, text, checked)} />
)
})
const lgth = items && items.length
if (customClass == 'check-box-list') {
customClass = lgth && lgth < 4 ? `check-box-list-${lgth}` : 'check-box-list'
}
return (
<Table striped>
<thead>
......@@ -31,7 +35,7 @@ const CheckBoxs = ({items, toggleCheckbox, toggleCheckAll, checkedAll}) => {
<tbody>
<tr >
<td>
<ul id="export-import">
<ul className={customClass} style={customStyle}>
{checkBoxs}
</ul>
</td>
......@@ -41,11 +45,12 @@ const CheckBoxs = ({items, toggleCheckbox, toggleCheckAll, checkedAll}) => {
)
}
CheckBoxs.propTypes = {
items: React.PropTypes.array,
checkedAll: React.PropTypes.bool,
toggleCheckAll: React.PropTypes.func,
toggleCheckbox: React.PropTypes.func,
items: PropTypes.array,
checkedAll: PropTypes.bool,
toggleCheckAll: PropTypes.func,
toggleCheckbox: PropTypes.func,
customClass: PropTypes.string,
customStyle: PropTypes.object,
}
const CheckBox = ({text, checked, toggleCheckbox}) => {
......@@ -60,10 +65,11 @@ const CheckBox = ({text, checked, toggleCheckbox}) => {
)
}
CheckBox.propTypes = {
text: React.PropTypes.string,
checked: React.PropTypes.bool,
toggleCheckbox: React.PropTypes.func,
text: PropTypes.string,
checked: PropTypes.bool,
toggleCheckbox: PropTypes.func,
}
export default CheckBoxs
import React, {Component} from 'react';
import {Tooltip, OverlayTrigger} from 'react-bootstrap';
import Formula from './Formula'
//var _ = require('lodash');
import React from 'react';
import Formula from './Formula';
export default class SampleName extends React.Component {
render() {
let {sample} = this.props;
const sumFormula = sf => <Formula formula={sf} />;
let molecule_name = sample._molecule.iupac_name
let sum_formular = (<Formula formula={sample._molecule.sum_formular}/>)
if(sample.contains_residues) {
let polymer_name = sample.polymer_type.charAt(0).toUpperCase()
+ sample.polymer_type.slice(1);
return (
<div>
<p>
{polymer_name.replace('_', '-') + ' - '}
{sum_formular}
</p>
<p>{molecule_name}</p>
</div>
)
} else {
return (
<div>
<p>{sum_formular}</p>
<p>{molecule_name}</p>
</div>
)
}
const sampleNameWithResidues = (polymer_type, sumFormulaCom, moleculeName) => {
const polymerName = (polymer_type.charAt(0).toUpperCase()
+ polymer_type.slice(1)).replace('_', '-') + ' - ';
return (
<div>
<p>
{polymerName}
{sumFormulaCom}
</p>
<p>{moleculeName}</p>
</div>
);
};
const SampleName = ({ sample }) => {
const { sum_formular, iupac_name } = sample._molecule;
const { contains_residues, polymer_type } = sample;
const mnl = sample.molecule_name_label;
const moleculeName = mnl && mnl !== sum_formular ? mnl : iupac_name;
const sumFormulaCom = sumFormula(sum_formular);
if (contains_residues) {
return sampleNameWithResidues(polymer_type, sumFormulaCom, moleculeName);
}
}
return (
<div>
<p>{sumFormulaCom}</p>
<p>{moleculeName}</p>
</div>
);
};
export default SampleName;
......@@ -14,28 +14,30 @@ export default class ModalExport extends React.Component {
columns: [
{value: "Image", text: "image", checked: true},
{value: "name", text: "name", checked: true},
{value: "description", text: "description", checked: true},
{value: "molecule_name", text: "molecule name", checked: true},
{value: "cano_smiles", text: "canonical smiles", checked: true},
{value: "inchistring", text: "InChIstring", checked: true},
{value: "inchikey", text: "InChIkey", checked: true},
{value: "sum_formular", text: "sum formula", checked: true},
{value: "inchistring", text: "inchistring", checked: true},
{value: "external_label", text: "external label", checked: false},
{value: "short_label", text: "short label", checked: false},
{value: "description", text: "description", checked: true},
{value: "real_amount_value,real_amount_unit", text: "real amount", checked: false},
{value: "target_amount_value, target_amount_unit", text: "target amount", checked: false},
{value: "created_at", text: "created at", checked: false},
{value: "updated_at", text: "updated at", checked: false},
{value: "molarity_value, molarity_unit", text: "molarity", checked: false},
{value: "density", text: "density", checked: false},
{value: "molfile", text: "molfile", checked: false},
{value: "purity", text: "purity", checked: false},
{value: "solvent", text: "solvent", checked: false},