labimotion 2.2.0.rc18 → 2.4.0.rc1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/lib/labimotion/apis/converter_api.rb +98 -23
- data/lib/labimotion/apis/generic_element_api.rb +22 -0
- data/lib/labimotion/entities/properties_entity.rb +28 -0
- data/lib/labimotion/helpers/param_helpers.rb +1 -0
- data/lib/labimotion/libs/linked_element.rb +193 -0
- data/lib/labimotion/utils/units.rb +177 -20
- data/lib/labimotion/version.rb +1 -1
- data/lib/labimotion.rb +1 -0
- metadata +3 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 20affa359afd5082ac4bc5e900b3e66c2c537a4b83fc9032883e33f25dc60362
|
|
4
|
+
data.tar.gz: e8bb4ae665e5de5166790ac27ecf42a95394057a2fd93d5deebc95dc858a5189
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 7e6d407b44837ccb8758aa2a86b6c4b109350dfa50dde7ebb3affae93b070c1efba512b4db2b08f0608b165a25c1a53c4caaa3450f55c1a15e5c1401a1ca87de
|
|
7
|
+
data.tar.gz: 60c1e3df69128dda039990086d98ad816e7fcedbf74e1a90b896b9b94eb7446b3e5feb613e42770be1f8807d72e5ee3a7c07ec8eb8e158b776d5904335d07671
|
|
@@ -2,39 +2,103 @@
|
|
|
2
2
|
|
|
3
3
|
module Labimotion
|
|
4
4
|
class ConverterAPI < Grape::API
|
|
5
|
+
helpers Labimotion::DatasetHelpers
|
|
5
6
|
helpers do
|
|
7
|
+
def load_converter_config!
|
|
8
|
+
@conf = Rails.configuration.converter&.url
|
|
9
|
+
@profile = Rails.configuration.converter&.profile
|
|
10
|
+
error!(406) unless @conf && @profile
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def require_converter_admin!
|
|
14
|
+
error!(401) unless current_user.profile&.data&.fetch('converter_admin', false)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def uploaded_file
|
|
18
|
+
params[:file].is_a?(Array) ? params[:file][0] : params[:file]
|
|
19
|
+
end
|
|
6
20
|
end
|
|
21
|
+
|
|
7
22
|
resource :converter do
|
|
23
|
+
resource :datasets do
|
|
24
|
+
desc 'list Generic Dataset Klass'
|
|
25
|
+
get do
|
|
26
|
+
list = klass_list(true, false)
|
|
27
|
+
list.map do |kl|
|
|
28
|
+
pr = kl.properties_release
|
|
29
|
+
pr['name'] = kl.label
|
|
30
|
+
pr['ols'] = kl.ols_term_id
|
|
31
|
+
pr
|
|
32
|
+
end || []
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
resource :datasets_units do
|
|
37
|
+
desc 'list Generic Dataset Klass'
|
|
38
|
+
get do
|
|
39
|
+
Labimotion::Units::FIELDS
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
8
43
|
resource :profiles do
|
|
44
|
+
|
|
9
45
|
before do
|
|
10
|
-
|
|
11
|
-
@profile = Rails.configuration.try(:converter).try(:profile)
|
|
12
|
-
error!(406) unless @conf && @profile
|
|
46
|
+
load_converter_config!
|
|
13
47
|
end
|
|
14
|
-
|
|
48
|
+
|
|
49
|
+
desc 'Fetch profiles (no admin required)'
|
|
15
50
|
get do
|
|
16
51
|
profiles = Labimotion::Converter.fetch_profiles
|
|
17
52
|
{ profiles: profiles, client: @profile }
|
|
18
53
|
end
|
|
19
|
-
|
|
54
|
+
|
|
55
|
+
desc 'Create profile'
|
|
20
56
|
post do
|
|
57
|
+
require_converter_admin!
|
|
21
58
|
Labimotion::Converter.create_profile(params)
|
|
22
59
|
end
|
|
23
|
-
|
|
60
|
+
|
|
24
61
|
route_param :id do
|
|
62
|
+
desc 'Update profile'
|
|
25
63
|
put do
|
|
64
|
+
require_converter_admin!
|
|
26
65
|
Labimotion::Converter.update_profile(params)
|
|
27
66
|
end
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
route_param :id do
|
|
67
|
+
|
|
68
|
+
desc 'Delete profile'
|
|
31
69
|
delete do
|
|
32
|
-
|
|
33
|
-
Labimotion::Converter.delete_profile(id)
|
|
70
|
+
require_converter_admin!
|
|
71
|
+
Labimotion::Converter.delete_profile(params[:id])
|
|
34
72
|
end
|
|
35
73
|
end
|
|
36
|
-
end
|
|
37
74
|
|
|
75
|
+
resource :restore do
|
|
76
|
+
route_param :profile_id,
|
|
77
|
+
requirements: {
|
|
78
|
+
profile_id: /[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}/
|
|
79
|
+
} do
|
|
80
|
+
|
|
81
|
+
route_param :profile_version,
|
|
82
|
+
requirements: {
|
|
83
|
+
profile_version: /\d+\.\d+/
|
|
84
|
+
} do
|
|
85
|
+
get do
|
|
86
|
+
{test_version: params[:profile_id], version: params[:profile_version]}
|
|
87
|
+
end
|
|
88
|
+
desc 'Restore profile'
|
|
89
|
+
post do
|
|
90
|
+
require_converter_admin!
|
|
91
|
+
|
|
92
|
+
Labimotion::Converter.restore(
|
|
93
|
+
params[:profile_id],
|
|
94
|
+
params[:profile_version],
|
|
95
|
+
params[:hard]
|
|
96
|
+
)
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
38
102
|
resource :structure do
|
|
39
103
|
helpers do
|
|
40
104
|
def convert_structure(molfile)
|
|
@@ -66,10 +130,8 @@ module Labimotion
|
|
|
66
130
|
|
|
67
131
|
resource :options do
|
|
68
132
|
before do
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
@profile = Rails.configuration.try(:converter).try(:profile)
|
|
72
|
-
error!(406) unless @conf && @profile
|
|
133
|
+
load_converter_config!
|
|
134
|
+
require_converter_admin!
|
|
73
135
|
end
|
|
74
136
|
desc 'fetch options'
|
|
75
137
|
get do
|
|
@@ -80,18 +142,31 @@ module Labimotion
|
|
|
80
142
|
|
|
81
143
|
resource :tables do
|
|
82
144
|
before do
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
@profile = Rails.configuration.try(:converter).try(:profile)
|
|
86
|
-
error!(406) unless @conf && @profile
|
|
145
|
+
load_converter_config!
|
|
146
|
+
require_converter_admin!
|
|
87
147
|
end
|
|
88
148
|
desc 'create tables'
|
|
89
149
|
post do
|
|
90
|
-
|
|
91
|
-
res
|
|
150
|
+
file = uploaded_file
|
|
151
|
+
res = Labimotion::Converter.create_tables(file['tempfile']) unless file.nil?
|
|
152
|
+
res['metadata']['file_name'] = file['filename']
|
|
92
153
|
res
|
|
93
154
|
end
|
|
94
155
|
end
|
|
156
|
+
|
|
157
|
+
resource :conversions do
|
|
158
|
+
before do
|
|
159
|
+
load_converter_config!
|
|
160
|
+
require_converter_admin!
|
|
161
|
+
end
|
|
162
|
+
desc 'convert file'
|
|
163
|
+
post do
|
|
164
|
+
file = uploaded_file
|
|
165
|
+
res = Labimotion::Converter.test_conversions(file['tempfile'], params[:format]) unless file.nil?
|
|
166
|
+
res
|
|
167
|
+
end
|
|
168
|
+
end
|
|
169
|
+
|
|
95
170
|
end
|
|
96
171
|
end
|
|
97
|
-
end
|
|
172
|
+
end
|
|
@@ -88,6 +88,28 @@ module Labimotion
|
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
|
|
91
|
+
namespace :linked_attributes do
|
|
92
|
+
desc 'List the selectable inline attributes of an element linked from a drag_element field'
|
|
93
|
+
params do
|
|
94
|
+
optional :el_klass, type: String, desc: 'Generic element klass name or permit-target key (reaction, sample, ...)'
|
|
95
|
+
optional :el_type, type: String, desc: 'Linked element type (element, sample, molecule, ...)'
|
|
96
|
+
requires :el_id, type: Integer, desc: 'Linked element id'
|
|
97
|
+
end
|
|
98
|
+
get do
|
|
99
|
+
linked = Labimotion::LinkedElement.new(params[:el_klass], params[:el_id], params[:el_type])
|
|
100
|
+
record = linked.record
|
|
101
|
+
error!('404 Not Found', 404) if record.nil?
|
|
102
|
+
# Reference data (molecules) is public; everything else is collection-scoped.
|
|
103
|
+
readable = record.is_a?(::Molecule) || ElementPolicy.new(current_user, record).read?
|
|
104
|
+
error!('401 Unauthorized', 401) unless readable
|
|
105
|
+
|
|
106
|
+
{ attributes: linked.available }
|
|
107
|
+
rescue StandardError => e
|
|
108
|
+
Labimotion.log_exception(e, current_user)
|
|
109
|
+
{ attributes: [], error: e.message }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
91
113
|
namespace :search_basic_by_like do
|
|
92
114
|
desc 'Search basic elements by name and short label (case-insensitive like search)'
|
|
93
115
|
params do
|
|
@@ -20,6 +20,7 @@ module Labimotion
|
|
|
20
20
|
|
|
21
21
|
def process_fields(key, layer)
|
|
22
22
|
process_sample_and_molecule_fields(key, layer)
|
|
23
|
+
process_linked_attr_fields(key, layer)
|
|
23
24
|
process_reaction_fields(key, layer)
|
|
24
25
|
process_table_fields(key, layer)
|
|
25
26
|
# process_voc_fields(key, layer)
|
|
@@ -32,6 +33,18 @@ module Labimotion
|
|
|
32
33
|
end
|
|
33
34
|
end
|
|
34
35
|
|
|
36
|
+
# Resolve the linked attributes a user chose to surface inline on a drag field
|
|
37
|
+
# (stored as value['el_attrs']). Only the selection is persisted; the current
|
|
38
|
+
# values are refreshed here from the linked element on every load. Works for
|
|
39
|
+
# drag_element (generic element or any permit-target type) as well as
|
|
40
|
+
# drag_sample / drag_molecule.
|
|
41
|
+
def process_linked_attr_fields(key, layer)
|
|
42
|
+
select_fields(layer, [Labimotion::FieldType::DRAG_ELEMENT, Labimotion::FieldType::DRAG_SAMPLE,
|
|
43
|
+
Labimotion::FieldType::DRAG_MOLECULE]).each do |field, idx|
|
|
44
|
+
update_linked_attr_field(key, field, idx)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
35
48
|
def process_reaction_fields(key, layer)
|
|
36
49
|
select_fields(layer, [Labimotion::FieldType::SYS_REACTION]).each do |field, idx|
|
|
37
50
|
update_reaction_field(key, field, idx)
|
|
@@ -80,6 +93,21 @@ module Labimotion
|
|
|
80
93
|
})
|
|
81
94
|
end
|
|
82
95
|
|
|
96
|
+
def update_linked_attr_field(key, field, idx)
|
|
97
|
+
return unless field['value'].is_a?(Hash)
|
|
98
|
+
|
|
99
|
+
attrs = field.dig('value', 'el_attrs')
|
|
100
|
+
return unless attrs.is_a?(Array) && attrs.present?
|
|
101
|
+
return unless object.properties.dig(Labimotion::Prop::LAYERS, key, Labimotion::Prop::FIELDS, idx,
|
|
102
|
+
'value').present?
|
|
103
|
+
|
|
104
|
+
el_klass = field.dig('value', 'el_klass')
|
|
105
|
+
el_id = field.dig('value', 'el_id')
|
|
106
|
+
el_type = field.dig('value', 'el_type')
|
|
107
|
+
object.properties[Labimotion::Prop::LAYERS][key][Labimotion::Prop::FIELDS][idx]['value']['el_attrs'] =
|
|
108
|
+
Labimotion::LinkedElement.new(el_klass, el_id, el_type).resolve(attrs)
|
|
109
|
+
end
|
|
110
|
+
|
|
83
111
|
def update_reaction_field(key, field, idx)
|
|
84
112
|
return unless field['value'].is_a?(Hash)
|
|
85
113
|
|
|
@@ -91,6 +91,7 @@ module Labimotion
|
|
|
91
91
|
optional :desc, type: String, desc: 'Segment Klass Desc'
|
|
92
92
|
optional :place, type: String, desc: 'Segment Klass Place', default: '100'
|
|
93
93
|
optional :identifier, type: String, desc: 'Segment Identifier'
|
|
94
|
+
optional :metadata, type: Hash, desc: 'Klass metadata'
|
|
94
95
|
end
|
|
95
96
|
|
|
96
97
|
params :create_segment_klass_params do
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Labimotion
|
|
4
|
+
# Resolves the "inline attributes" of an element linked from a generic
|
|
5
|
+
# drag_element field.
|
|
6
|
+
#
|
|
7
|
+
# A drag_element link target is identified by the stored value's +el_klass+:
|
|
8
|
+
# * a generic element klass name -> the target is a Labimotion::Element and
|
|
9
|
+
# its selectable attributes are its own property fields (layer + field);
|
|
10
|
+
# * a permit-target key (reaction, sample, molecule, wellplate, screen,
|
|
11
|
+
# research_plan, device_description) -> the target is a standard ELN model
|
|
12
|
+
# and its selectable attributes are a curated set of database columns.
|
|
13
|
+
#
|
|
14
|
+
# Used both to list the attributes a user can pick (#available) and to refresh
|
|
15
|
+
# the current values of a previously stored selection (#resolve).
|
|
16
|
+
class LinkedElement
|
|
17
|
+
SOURCE_PROPERTY = 'property'
|
|
18
|
+
SOURCE_COLUMN = 'column'
|
|
19
|
+
|
|
20
|
+
# el_klass (permit-target key) => model class name
|
|
21
|
+
MODELS = {
|
|
22
|
+
'reaction' => '::Reaction',
|
|
23
|
+
'sample' => '::Sample',
|
|
24
|
+
'molecule' => '::Molecule',
|
|
25
|
+
'wellplate' => '::Wellplate',
|
|
26
|
+
'screen' => '::Screen',
|
|
27
|
+
'research_plan' => '::ResearchPlan',
|
|
28
|
+
'device_description' => '::DeviceDescription'
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
# Curated, scalar columns exposed per type: [column/method, label, unit_column?]
|
|
32
|
+
COLUMNS = {
|
|
33
|
+
'reaction' => [
|
|
34
|
+
%w[name Name], %w[short_label], ['status', 'Status'], ['role', 'Role'],
|
|
35
|
+
['rxno', 'Reaction Type'], ['conditions', 'Conditions'],
|
|
36
|
+
['duration', 'Duration'], %w[solvent Solvent]
|
|
37
|
+
],
|
|
38
|
+
'sample' => [
|
|
39
|
+
%w[name Name], %w[short_label], ['external_label', 'External Label'],
|
|
40
|
+
['sum_formula', 'Sum Formula'], ['molecular_mass', 'Molecular Mass'],
|
|
41
|
+
%w[purity Purity], %w[density Density],
|
|
42
|
+
['real_amount_value', 'Real Amount', 'real_amount_unit'],
|
|
43
|
+
['target_amount_value', 'Target Amount', 'target_amount_unit'],
|
|
44
|
+
%w[location Location]
|
|
45
|
+
],
|
|
46
|
+
'molecule' => [
|
|
47
|
+
['iupac_name', 'IUPAC Name'], ['sum_formular', 'Sum Formula'],
|
|
48
|
+
['molecular_weight', 'Molecular Weight'],
|
|
49
|
+
['exact_molecular_weight', 'Exact Molecular Weight'],
|
|
50
|
+
['cano_smiles', 'Canonical SMILES'], ['melting_point', 'Melting Point'],
|
|
51
|
+
['boiling_point', 'Boiling Point'], %w[density Density]
|
|
52
|
+
],
|
|
53
|
+
'wellplate' => [
|
|
54
|
+
%w[name Name], %w[short_label], ['description', 'Description'],
|
|
55
|
+
%w[width Width], %w[height Height]
|
|
56
|
+
],
|
|
57
|
+
'screen' => [
|
|
58
|
+
%w[name Name], ['description', 'Description'], %w[result Result],
|
|
59
|
+
%w[collaborator Collaborator], ['conditions', 'Conditions'],
|
|
60
|
+
%w[requirements Requirements]
|
|
61
|
+
],
|
|
62
|
+
'research_plan' => [
|
|
63
|
+
%w[name Name], %w[short_label]
|
|
64
|
+
],
|
|
65
|
+
'device_description' => [
|
|
66
|
+
%w[name Name], %w[short_label], ['serial_number', 'Serial Number'],
|
|
67
|
+
['device_class', 'Device Class'], ['operation_mode', 'Operation Mode'],
|
|
68
|
+
['application_name', 'Application Name'], ['vendor_url', 'Vendor URL'],
|
|
69
|
+
%w[institute Institute], %w[building Building], %w[room Room]
|
|
70
|
+
]
|
|
71
|
+
}.freeze
|
|
72
|
+
|
|
73
|
+
# Property field types whose value is a scalar worth surfacing inline.
|
|
74
|
+
PROPERTY_TYPES = %w[
|
|
75
|
+
text textarea number integer select select-multi checkbox
|
|
76
|
+
datetime system-defined formula-field text-formula ontology-select
|
|
77
|
+
].freeze
|
|
78
|
+
|
|
79
|
+
def initialize(el_klass, el_id, el_type = nil)
|
|
80
|
+
@el_klass = el_klass.to_s
|
|
81
|
+
@el_type = el_type.to_s
|
|
82
|
+
@el_id = el_id
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# drag_element keeps the linked class in +el_klass+; drag_sample / drag_molecule
|
|
86
|
+
# keep it in +el_type+ (and may omit el_klass). Resolve one effective dispatch key.
|
|
87
|
+
def target_key
|
|
88
|
+
return @target_key if defined?(@target_key)
|
|
89
|
+
|
|
90
|
+
@target_key =
|
|
91
|
+
if MODELS.key?(@el_klass) || generic_klass?(@el_klass)
|
|
92
|
+
@el_klass
|
|
93
|
+
elsif MODELS.key?(@el_type)
|
|
94
|
+
@el_type
|
|
95
|
+
else
|
|
96
|
+
''
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def column_based?
|
|
101
|
+
MODELS.key?(target_key)
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def generic?
|
|
105
|
+
generic_klass?(target_key)
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def record
|
|
109
|
+
return @record if defined?(@record)
|
|
110
|
+
|
|
111
|
+
@record =
|
|
112
|
+
if column_based?
|
|
113
|
+
MODELS[target_key].safe_constantize&.find_by(id: @el_id)
|
|
114
|
+
elsif generic?
|
|
115
|
+
Labimotion::Element.find_by(id: @el_id)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
# All attributes the user can pick, each with its current value.
|
|
120
|
+
def available
|
|
121
|
+
return generic_attributes if generic?
|
|
122
|
+
return column_attributes if column_based?
|
|
123
|
+
|
|
124
|
+
[]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Refresh the values/labels of a previously stored selection. Selections whose
|
|
128
|
+
# source attribute no longer exists are kept but flagged as missing; for an
|
|
129
|
+
# unknown/unsupported el_klass the selection is returned untouched.
|
|
130
|
+
def resolve(selected)
|
|
131
|
+
return selected unless selected.is_a?(Array)
|
|
132
|
+
return selected unless generic? || column_based?
|
|
133
|
+
|
|
134
|
+
index = available.index_by { |a| [a['source'], a['key']] }
|
|
135
|
+
selected.map do |attr|
|
|
136
|
+
next attr unless attr.is_a?(Hash)
|
|
137
|
+
|
|
138
|
+
index[[attr['source'], attr['key']]] || attr.merge('value' => nil, 'missing' => true)
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
private
|
|
143
|
+
|
|
144
|
+
def generic_klass?(name)
|
|
145
|
+
name.present? && !MODELS.key?(name) && Labimotion::ElementKlass.exists?(name: name)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
def generic_attributes
|
|
149
|
+
el = record
|
|
150
|
+
return [] unless el&.properties.is_a?(Hash)
|
|
151
|
+
|
|
152
|
+
attrs = []
|
|
153
|
+
layers = el.properties[Labimotion::Prop::LAYERS] || {}
|
|
154
|
+
layers.values.sort_by { |layer| layer['position'] || 0 }.each do |layer|
|
|
155
|
+
(layer[Labimotion::Prop::FIELDS] || []).each do |field|
|
|
156
|
+
next unless PROPERTY_TYPES.include?(field['type'])
|
|
157
|
+
|
|
158
|
+
attr = {
|
|
159
|
+
'source' => SOURCE_PROPERTY,
|
|
160
|
+
'key' => "#{layer['key']}::#{field['field']}",
|
|
161
|
+
'label' => field['label'].presence || field['field'],
|
|
162
|
+
'type' => field['type'],
|
|
163
|
+
'value' => field['value'],
|
|
164
|
+
'group' => layer['label'].presence || layer['key']
|
|
165
|
+
}
|
|
166
|
+
attr['value_system'] = field['value_system'] if field['value_system'].present?
|
|
167
|
+
attrs << attr
|
|
168
|
+
end
|
|
169
|
+
end
|
|
170
|
+
attrs
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
def column_attributes
|
|
174
|
+
rec = record
|
|
175
|
+
return [] if rec.blank?
|
|
176
|
+
|
|
177
|
+
(COLUMNS[target_key] || []).filter_map do |col, label, unit_col|
|
|
178
|
+
next unless rec.respond_to?(col)
|
|
179
|
+
|
|
180
|
+
attr = {
|
|
181
|
+
'source' => SOURCE_COLUMN,
|
|
182
|
+
'key' => col,
|
|
183
|
+
'label' => label.presence || col.to_s.humanize,
|
|
184
|
+
'type' => SOURCE_COLUMN,
|
|
185
|
+
'value' => rec.public_send(col),
|
|
186
|
+
'group' => target_key
|
|
187
|
+
}
|
|
188
|
+
attr['value_system'] = rec.public_send(unit_col) if unit_col && rec.respond_to?(unit_col) && rec.public_send(unit_col).present?
|
|
189
|
+
attr
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
end
|
|
193
|
+
end
|
|
@@ -51,6 +51,20 @@ module Labimotion
|
|
|
51
51
|
{ "key": "pmol", "label": "pmol", "nm": 1.0e12 }
|
|
52
52
|
]
|
|
53
53
|
},
|
|
54
|
+
{
|
|
55
|
+
"type": "numeric",
|
|
56
|
+
"field": "areal_density",
|
|
57
|
+
"label": "Areal density",
|
|
58
|
+
"default": "",
|
|
59
|
+
"position": 38,
|
|
60
|
+
"placeholder": "areal density",
|
|
61
|
+
"units": [
|
|
62
|
+
{ "key": "kg_m2", "label": "kg m<sup>-2</sup>", "nm": 1 },
|
|
63
|
+
{ "key": "t_m2", "label": "t m<sup>-2</sup>", "nm": 0.001 },
|
|
64
|
+
{ "key": "kg_cm2", "label": "kg cm<sup>-2</sup>", "nm": 1e-4 },
|
|
65
|
+
{ "key": "g_m2", "label": "g m<sup>-2</sup>", "nm": 1000 }
|
|
66
|
+
]
|
|
67
|
+
},
|
|
54
68
|
{
|
|
55
69
|
"type": "numeric",
|
|
56
70
|
"field": "molarity",
|
|
@@ -93,16 +107,34 @@ module Labimotion
|
|
|
93
107
|
"field": "conductivity",
|
|
94
108
|
"label": "Conductivity",
|
|
95
109
|
"default": "",
|
|
96
|
-
"position":
|
|
110
|
+
"position": 62,
|
|
97
111
|
"placeholder": "conductivity",
|
|
98
|
-
"units": [
|
|
112
|
+
"units": [
|
|
113
|
+
{ "key": "s_m", "label": "S/m", "nm": 1 },
|
|
114
|
+
{ "key": "ms_cm", "label": "mS/cm", "nm": 10 }
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
{
|
|
118
|
+
"type": "numeric",
|
|
119
|
+
"field": "currency",
|
|
120
|
+
"label": "Currency",
|
|
121
|
+
"default": "",
|
|
122
|
+
"position": 64,
|
|
123
|
+
"placeholder": "Currency",
|
|
124
|
+
"units": [
|
|
125
|
+
{ "key": "eur", "label": "EUR", "nm": 1, "unit_type": "eur" },
|
|
126
|
+
{ "key": "chf", "label": "CHF", "nm": 1, "unit_type": "chf" },
|
|
127
|
+
{ "key": "gbp", "label": "GBP", "nm": 1, "unit_type": "gbp" },
|
|
128
|
+
{ "key": "usd", "label": "USD", "nm": 1, "unit_type": "usd" },
|
|
129
|
+
{ "key": "jpy", "label": "JPY", "nm": 1, "unit_type": "jpy" }
|
|
130
|
+
]
|
|
99
131
|
},
|
|
100
132
|
{
|
|
101
133
|
"type": "numeric",
|
|
102
134
|
"field": "current",
|
|
103
135
|
"label": "Current",
|
|
104
136
|
"default": "",
|
|
105
|
-
"position":
|
|
137
|
+
"position": 67,
|
|
106
138
|
"placeholder": "Current",
|
|
107
139
|
"units": [
|
|
108
140
|
{ "key": "A", "label": "A", "nm": 1 },
|
|
@@ -251,6 +283,17 @@ module Labimotion
|
|
|
251
283
|
{ "key": "k_j", "label": "kJ", "nm": 1 }
|
|
252
284
|
]
|
|
253
285
|
},
|
|
286
|
+
{
|
|
287
|
+
"type": "numeric",
|
|
288
|
+
"field": "free_energy",
|
|
289
|
+
"label": "Free Energy/enthalpy",
|
|
290
|
+
"default": "",
|
|
291
|
+
"position": 89,
|
|
292
|
+
"placeholder": "Free Energy/enthalpy",
|
|
293
|
+
"units": [
|
|
294
|
+
{ "key": "kj_mol", "label": "kJ/mol", "nm": 1 }
|
|
295
|
+
]
|
|
296
|
+
},
|
|
254
297
|
{
|
|
255
298
|
"type": "numeric",
|
|
256
299
|
"field": "enzyme_activity",
|
|
@@ -260,9 +303,9 @@ module Labimotion
|
|
|
260
303
|
"placeholder": "Enzyme activity",
|
|
261
304
|
"units": [
|
|
262
305
|
{ "key": "u_l", "label": "U/L", "nm": 1 },
|
|
263
|
-
{ "key": "u_ml", "label": "U/mL", "nm":
|
|
264
|
-
{ "key": "u_g", "label": "U/g", "nm":
|
|
265
|
-
{ "key": "u_mg", "label": "U/mg", "nm":
|
|
306
|
+
{ "key": "u_ml", "label": "U/mL", "nm": 1e-3 },
|
|
307
|
+
{ "key": "u_g", "label": "U/g", "nm": 1e-3 },
|
|
308
|
+
{ "key": "u_mg", "label": "U/mg", "nm": 1e-6 }
|
|
266
309
|
]
|
|
267
310
|
},
|
|
268
311
|
{
|
|
@@ -284,9 +327,10 @@ module Labimotion
|
|
|
284
327
|
"position": 100,
|
|
285
328
|
"placeholder": "Flow rate",
|
|
286
329
|
"units": [
|
|
287
|
-
{ "key": "
|
|
288
|
-
{ "key": "ml_min", "label": "
|
|
289
|
-
{ "key": "
|
|
330
|
+
{ "key": "l_m", "label": "L/min", "nm": 1 },
|
|
331
|
+
{ "key": "ml_min", "label": "mL/min", "nm": 1000 },
|
|
332
|
+
{ "key": "ul_min", "label": "µL/min", "nm": 1000000 },
|
|
333
|
+
{ "key": "ml_h", "label": "mL/h", "nm": 60000 }
|
|
290
334
|
]
|
|
291
335
|
},
|
|
292
336
|
{
|
|
@@ -297,19 +341,26 @@ module Labimotion
|
|
|
297
341
|
"position": 103,
|
|
298
342
|
"placeholder": "frequency",
|
|
299
343
|
"units": [
|
|
300
|
-
{ "key": "
|
|
301
|
-
{ "key": "
|
|
302
|
-
{ "key": "
|
|
344
|
+
{ "key": "hz", "label": "Hz", "nm": 1 },
|
|
345
|
+
{ "key": "khz", "label": "kHz", "nm": 1e-3 },
|
|
346
|
+
{ "key": "mhz", "label": "MHz", "nm": 1e-6 }
|
|
303
347
|
]
|
|
304
348
|
},
|
|
305
349
|
{
|
|
306
350
|
"type": "numeric",
|
|
307
351
|
"field": "heating_rate",
|
|
308
|
-
"label": "Heating rate",
|
|
352
|
+
"label": "Heating rate (Temperature ramp)",
|
|
309
353
|
"default": "",
|
|
310
354
|
"position": 106,
|
|
311
355
|
"placeholder": "heating rate",
|
|
312
|
-
"units": [
|
|
356
|
+
"units": [
|
|
357
|
+
{ "key": "k_min", "label": "K/min", "nm": 1 },
|
|
358
|
+
{ "key": "k_s", "label": "K/s", "nm": 0.016666666666667 },
|
|
359
|
+
{ "key": "k_h", "label": "K/h", "nm": 60 },
|
|
360
|
+
{ "key": "degc_min", "label": "°C min<sup>-1</sup>", "nm": 1 },
|
|
361
|
+
{ "key": "degc_s", "label": "°C s<sup>-1</sup>", "nm": 0.016666666666667 },
|
|
362
|
+
{ "key": "degc_h", "label": "°C h<sup>-1</sup>", "nm": 60 }
|
|
363
|
+
]
|
|
313
364
|
},
|
|
314
365
|
{
|
|
315
366
|
"type": "numeric",
|
|
@@ -346,6 +397,29 @@ module Labimotion
|
|
|
346
397
|
{ "key": "ug", "label": "µg", "nm": 1000000 }
|
|
347
398
|
]
|
|
348
399
|
},
|
|
400
|
+
{
|
|
401
|
+
"type": "numeric",
|
|
402
|
+
"field": "mass_fraction",
|
|
403
|
+
"label": "Mass fraction",
|
|
404
|
+
"default": "",
|
|
405
|
+
"position": 123,
|
|
406
|
+
"placeholder": "mass fraction",
|
|
407
|
+
"units": [
|
|
408
|
+
{ "key": "wt_p", "label": "wt.%", "nm": 1 }
|
|
409
|
+
]
|
|
410
|
+
},
|
|
411
|
+
{
|
|
412
|
+
"type": "numeric",
|
|
413
|
+
"field": "mass_loading",
|
|
414
|
+
"label": "Mass loading",
|
|
415
|
+
"default": "",
|
|
416
|
+
"position": 125,
|
|
417
|
+
"placeholder": "mass loading",
|
|
418
|
+
"units": [
|
|
419
|
+
{ "key": "g_cm2", "label": "g cm<sup>-2</sup>", "nm": 1 },
|
|
420
|
+
{ "key": "mg_cm2", "label": "mg cm<sup>-2</sup>", "nm": 1000 }
|
|
421
|
+
]
|
|
422
|
+
},
|
|
349
423
|
{
|
|
350
424
|
"type": "numeric",
|
|
351
425
|
"field": "mass_molecule",
|
|
@@ -358,6 +432,28 @@ module Labimotion
|
|
|
358
432
|
{ "key": "kilo_dalton", "label": "kD", "nm": 1 }
|
|
359
433
|
]
|
|
360
434
|
},
|
|
435
|
+
{
|
|
436
|
+
"type": "numeric",
|
|
437
|
+
"field": "molar_conductivity",
|
|
438
|
+
"label": "Molar conductivity",
|
|
439
|
+
"default": "",
|
|
440
|
+
"position": 127,
|
|
441
|
+
"placeholder": "molar conductivity",
|
|
442
|
+
"units": [
|
|
443
|
+
{ "key": "s_cm2_mol", "label": "S cm<sup>2</sup> mol<sup>-1</sup>", "nm": 1 }
|
|
444
|
+
]
|
|
445
|
+
},
|
|
446
|
+
{
|
|
447
|
+
"type": "numeric",
|
|
448
|
+
"field": "molar_entropy",
|
|
449
|
+
"label": "Molar Entropy",
|
|
450
|
+
"default": "",
|
|
451
|
+
"position": 128,
|
|
452
|
+
"placeholder": "Molar Entropy",
|
|
453
|
+
"units": [
|
|
454
|
+
{ "key": "j_mol_k", "label": "J/(mol*K)", "nm": 1 }
|
|
455
|
+
]
|
|
456
|
+
},
|
|
361
457
|
{
|
|
362
458
|
"type": "numeric",
|
|
363
459
|
"field": "molecular_weight",
|
|
@@ -402,6 +498,30 @@ module Labimotion
|
|
|
402
498
|
{ "key": "mbar", "label": "mbar", "nm": 1013.25 }
|
|
403
499
|
]
|
|
404
500
|
},
|
|
501
|
+
{
|
|
502
|
+
"type": "numeric",
|
|
503
|
+
"field": "process_rate",
|
|
504
|
+
"label": "Process rate",
|
|
505
|
+
"default": "",
|
|
506
|
+
"position": 143,
|
|
507
|
+
"placeholder": "Process rate",
|
|
508
|
+
"units": [
|
|
509
|
+
{ "key": "1_s", "label": "1/s", "nm": 1 },
|
|
510
|
+
{ "key": "1_min", "label": "1/min", "nm": 60 },
|
|
511
|
+
{ "key": "1_h", "label": "1/h", "nm": 3600 }
|
|
512
|
+
]
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
"type": "numeric",
|
|
516
|
+
"field": "rate_constant",
|
|
517
|
+
"label": "Rate constant",
|
|
518
|
+
"default": "",
|
|
519
|
+
"position": 146,
|
|
520
|
+
"placeholder": "Rate constant",
|
|
521
|
+
"units": [
|
|
522
|
+
{ "key": "1_m_s", "label": "1/(M*s)", "nm": 1 }
|
|
523
|
+
]
|
|
524
|
+
},
|
|
405
525
|
{
|
|
406
526
|
"type": "numeric",
|
|
407
527
|
"field": "reaction_rate",
|
|
@@ -410,8 +530,8 @@ module Labimotion
|
|
|
410
530
|
"position": 150,
|
|
411
531
|
"placeholder": "Reaction rate",
|
|
412
532
|
"units": [
|
|
413
|
-
{ "key": "mol_lmin", "label": "mol
|
|
414
|
-
{ "key": "mol_lsec", "label": "mol
|
|
533
|
+
{ "key": "mol_lmin", "label": "mol L<sup>-1</sup> min<sup>-1</sup>", "nm": 1 },
|
|
534
|
+
{ "key": "mol_lsec", "label": "mol L<sup>-1</sup> s<sup>-1</sup>", "nm": 0.016666666666667 }
|
|
415
535
|
]
|
|
416
536
|
},
|
|
417
537
|
{
|
|
@@ -448,8 +568,9 @@ module Labimotion
|
|
|
448
568
|
"units": [
|
|
449
569
|
{ "key": "ma_g", "label": "mA/g", "nm": 1000, "unit_type": "mass" },
|
|
450
570
|
{ "key": "a_g", "label": "A/g", "nm": 1, "unit_type": "mass" },
|
|
571
|
+
{ "key": "ma_cm2", "label": "mA/cm<sup>2</sup>", "nm": 1000, "unit_type": "area" },
|
|
451
572
|
{ "key": "a_cm2", "label": "A/cm<sup>2</sup>", "nm": 1, "unit_type": "area" },
|
|
452
|
-
{ "key": "
|
|
573
|
+
{ "key": "a_mm2", "label": "A/mm<sup>2</sup>", "nm": 0.01, "unit_type": "area" }
|
|
453
574
|
]
|
|
454
575
|
},
|
|
455
576
|
{
|
|
@@ -495,7 +616,7 @@ module Labimotion
|
|
|
495
616
|
{ "key": "cm_s", "label": "cm/s", "nm": 1 },
|
|
496
617
|
{ "key": "mm_s", "label": "mm/s", "nm": 10 },
|
|
497
618
|
{ "key": "um_m", "label": "µm/min", "nm": 600000 },
|
|
498
|
-
{ "key": "nm_m", "label": "nm/min", "nm":
|
|
619
|
+
{ "key": "nm_m", "label": "nm/min", "nm": 600000000 },
|
|
499
620
|
{ "key": "cm_h", "label": "cm/h", "nm": 3600 },
|
|
500
621
|
{ "key": "mm_h", "label": "mm/h", "nm": 36000 }
|
|
501
622
|
]
|
|
@@ -540,6 +661,31 @@ module Labimotion
|
|
|
540
661
|
{ "key": "K", "label": "K" }
|
|
541
662
|
]
|
|
542
663
|
},
|
|
664
|
+
{
|
|
665
|
+
"type": "numeric",
|
|
666
|
+
"field": "tensile_force",
|
|
667
|
+
"label": "Tensile force",
|
|
668
|
+
"default": "",
|
|
669
|
+
"position": 185,
|
|
670
|
+
"placeholder": "tensile force",
|
|
671
|
+
"units": [
|
|
672
|
+
{ "key": "n", "label": "N", "nm": 1 },
|
|
673
|
+
{ "key": "kn", "label": "kN", "nm": 0.001 },
|
|
674
|
+
{ "key": "mn", "label": "mN", "nm": 1000 }
|
|
675
|
+
]
|
|
676
|
+
},
|
|
677
|
+
{
|
|
678
|
+
"type": "numeric",
|
|
679
|
+
"field": "tensile_stress",
|
|
680
|
+
"label": "Tensile stress",
|
|
681
|
+
"default": "",
|
|
682
|
+
"position": 186,
|
|
683
|
+
"placeholder": "tensile stress",
|
|
684
|
+
"units": [
|
|
685
|
+
{ "key": "n_cm2", "label": "N cm<sup>-2</sup>", "nm": 1 },
|
|
686
|
+
{ "key": "n_mm2", "label": "N mm<sup>-2</sup>", "nm": 0.01 }
|
|
687
|
+
]
|
|
688
|
+
},
|
|
543
689
|
{
|
|
544
690
|
"type": "numeric",
|
|
545
691
|
"field": "turnover_number",
|
|
@@ -575,7 +721,7 @@ module Labimotion
|
|
|
575
721
|
"field": "voltage",
|
|
576
722
|
"label": "Voltage",
|
|
577
723
|
"default": "",
|
|
578
|
-
"position":
|
|
724
|
+
"position": 206,
|
|
579
725
|
"placeholder": "voltage",
|
|
580
726
|
"units": [
|
|
581
727
|
{ "key": "mv", "label": "mV", "nm": 1000 },
|
|
@@ -597,6 +743,17 @@ module Labimotion
|
|
|
597
743
|
{ "key": "nl", "label": "nl", "nm": 1000000000 }
|
|
598
744
|
]
|
|
599
745
|
},
|
|
746
|
+
{
|
|
747
|
+
"type": "numeric",
|
|
748
|
+
"field": "volume_fraction",
|
|
749
|
+
"label": "Volume fraction",
|
|
750
|
+
"default": "",
|
|
751
|
+
"position": 218,
|
|
752
|
+
"placeholder": "volume fraction",
|
|
753
|
+
"units": [
|
|
754
|
+
{ "key": "vol_p", "label": "vol.%", "nm": 1 }
|
|
755
|
+
]
|
|
756
|
+
},
|
|
600
757
|
{
|
|
601
758
|
"type": "numeric",
|
|
602
759
|
"field": "volumes_metric",
|
|
@@ -610,6 +767,6 @@ module Labimotion
|
|
|
610
767
|
{ "key": "m3", "label": "m³", "nm": 0.000001 }
|
|
611
768
|
]
|
|
612
769
|
}
|
|
613
|
-
|
|
770
|
+
]
|
|
614
771
|
end
|
|
615
772
|
end
|
data/lib/labimotion/version.rb
CHANGED
data/lib/labimotion.rb
CHANGED
|
@@ -89,6 +89,7 @@ module Labimotion
|
|
|
89
89
|
autoload :ExportDataset, 'labimotion/libs/export_dataset'
|
|
90
90
|
autoload :SampleAssociation, 'labimotion/libs/sample_association'
|
|
91
91
|
autoload :PropertiesHandler, 'labimotion/libs/properties_handler'
|
|
92
|
+
autoload :LinkedElement, 'labimotion/libs/linked_element'
|
|
92
93
|
autoload :AttachmentHandler, 'labimotion/libs/attachment_handler'
|
|
93
94
|
autoload :VocabularyHandler, 'labimotion/libs/vocabulary_handler'
|
|
94
95
|
autoload :XlsxExporter, 'labimotion/libs/xlsx_exporter'
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: labimotion
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.4.0.rc1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chia-Lin Lin
|
|
@@ -9,7 +9,7 @@ authors:
|
|
|
9
9
|
autorequire:
|
|
10
10
|
bindir: bin
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date: 2026-07-
|
|
12
|
+
date: 2026-07-03 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: caxlsx
|
|
@@ -118,6 +118,7 @@ files:
|
|
|
118
118
|
- lib/labimotion/libs/dataset_builder.rb
|
|
119
119
|
- lib/labimotion/libs/export_dataset.rb
|
|
120
120
|
- lib/labimotion/libs/export_element.rb
|
|
121
|
+
- lib/labimotion/libs/linked_element.rb
|
|
121
122
|
- lib/labimotion/libs/nmr_mapper.rb
|
|
122
123
|
- lib/labimotion/libs/properties_handler.rb
|
|
123
124
|
- lib/labimotion/libs/sample_association.rb
|