labimotion 2.1.0 → 2.1.2
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/helpers/element_helpers.rb +4 -3
- data/lib/labimotion/libs/converter.rb +28 -17
- data/lib/labimotion/libs/nmr_mapper.rb +31 -2
- data/lib/labimotion/models/concerns/attachment_converter.rb +24 -0
- data/lib/labimotion/utils/mapper_utils.rb +20 -11
- data/lib/labimotion/version.rb +1 -1
- data/lib/labimotion.rb +5 -0
- metadata +2 -3
- data/lib/labimotion/libs/data/layer/StdDataset.json +0 -212
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2be4b8936c18a5560a7aed2c4ca7ad3ea6f3fc17245b7442e3658255a0e84494
|
|
4
|
+
data.tar.gz: 88f5beed8cea656e69f5e9ba13aaa3acbe92dddce489a131e5238b09ca63cc87
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 98cfc7e3e0f672a3371a6fc523fd58de260e2cde2e83696be0976ad44c2efc79ebdf60fd414969b996cfa09f7d4f2c384168fd04503f0713c6ccdf4f5905da87
|
|
7
|
+
data.tar.gz: 22052eb592542f2e8a43368b6c08dac67e301352234f968ffc175a12d6af66133857876884b85ee1f35e449a4edcae3e37ff39c0233673f73323f9c7a39b8812
|
|
@@ -87,12 +87,13 @@ module Labimotion
|
|
|
87
87
|
}
|
|
88
88
|
element = Labimotion::Element.new(attributes)
|
|
89
89
|
|
|
90
|
-
|
|
90
|
+
all_coll = Collection.get_all_collection_for_user(current_user.id)
|
|
91
|
+
element.collections << all_coll
|
|
92
|
+
|
|
93
|
+
if params[:collection_id] && params[:collection_id] != all_coll.id
|
|
91
94
|
collection = current_user.collections.find(params[:collection_id])
|
|
92
95
|
element.collections << collection
|
|
93
96
|
end
|
|
94
|
-
all_coll = Collection.get_all_collection_for_user(current_user.id)
|
|
95
|
-
element.collections << all_coll
|
|
96
97
|
element.save!
|
|
97
98
|
element.properties = update_sample_association(params[:properties], current_user, element)
|
|
98
99
|
# element.properties = update_vocabularies(_properties, current_user, element)
|
|
@@ -6,6 +6,7 @@ require 'json'
|
|
|
6
6
|
require 'date'
|
|
7
7
|
require 'labimotion/version'
|
|
8
8
|
require 'labimotion/utils/utils'
|
|
9
|
+
require 'labimotion/utils/mapper_utils'
|
|
9
10
|
require 'labimotion/helpers/converter_helpers'
|
|
10
11
|
|
|
11
12
|
# rubocop: disable Metrics/AbcSize
|
|
@@ -20,27 +21,36 @@ module Labimotion
|
|
|
20
21
|
end
|
|
21
22
|
|
|
22
23
|
def self.process_ds(id, current_user = {})
|
|
23
|
-
|
|
24
|
+
ctx = { att: nil, ols: nil, dsr: [] }
|
|
25
|
+
# Savepoint: a failure leaves no half-built dataset and keeps the caller's transaction usable
|
|
26
|
+
Attachment.transaction(requires_new: true) { map_ds(id, current_user, ctx) }
|
|
27
|
+
rescue StandardError => e
|
|
28
|
+
Labimotion::Converter.logger.error ["Att ID: #{ctx[:att]&.id}, OLS: #{ctx[:ols]}", "DSR: #{ctx[:dsr]}", e.message, *e.backtrace].join($INPUT_RECORD_SEPARATOR)
|
|
29
|
+
Labimotion.report_exception(e)
|
|
30
|
+
# Terminal state: do not fail the request, and do not retry on the next save
|
|
31
|
+
ctx[:att]&.update_column(:con_state, Labimotion::ConState::ERROR)
|
|
32
|
+
Labimotion::ConState::ERROR
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def self.map_ds(id, current_user, ctx)
|
|
36
|
+
att = ctx[:att] = Attachment.find_by(id: id, con_state: Labimotion::ConState::CONVERTED)
|
|
24
37
|
return if att.nil? || att.attachable_id.nil? || att.attachable_type != 'Container' || att.filename.split('.')&.last != 'zip'
|
|
25
38
|
|
|
26
39
|
eln_ds = Container.find_by(id: att.attachable_id, container_type: 'dataset')
|
|
27
40
|
return if eln_ds.nil? || eln_ds.parent.nil? || eln_ds.parent&.container_type != 'analysis'
|
|
28
41
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
dsr.
|
|
37
|
-
dataset = build_ds(att.attachable_id, ols)
|
|
38
|
-
update_ds(dataset, dsr, current_user) if dataset.present?
|
|
42
|
+
# Never close: closing can rewrite the stored file in place
|
|
43
|
+
zip_file = Zip::File.new(att.attachment_attacher.file.url)
|
|
44
|
+
res = Labimotion::Converter.collect_metadata(zip_file, current_user)
|
|
45
|
+
ctx[:ols] = res[:o] unless res&.dig(:o).nil?
|
|
46
|
+
ctx[:dsr].push(res[:d]) unless res&.dig(:d).nil?
|
|
47
|
+
ctx[:dsr].flatten!
|
|
48
|
+
dataset = build_ds(att.attachable_id, ctx[:ols])
|
|
49
|
+
update_ds(dataset, ctx[:dsr], current_user) if dataset.present?
|
|
39
50
|
att.update_column(:con_state, Labimotion::ConState::COMPLETED)
|
|
40
|
-
|
|
41
|
-
Labimotion::Converter.logger.error ["Att ID: #{att&.id}, OLS: #{ols}", "DSR: #{dsr}", e.message, *e.backtrace].join($INPUT_RECORD_SEPARATOR)
|
|
42
|
-
raise e
|
|
51
|
+
Labimotion::ConState::COMPLETED
|
|
43
52
|
end
|
|
53
|
+
private_class_method :map_ds
|
|
44
54
|
|
|
45
55
|
def self.uri(api_name)
|
|
46
56
|
url = Rails.configuration.converter.url
|
|
@@ -99,7 +109,7 @@ module Labimotion
|
|
|
99
109
|
zip_file.each do |entry|
|
|
100
110
|
next unless entry.name == 'metadata/converter.json'
|
|
101
111
|
|
|
102
|
-
metadata = entry.
|
|
112
|
+
metadata = Labimotion::MapperUtils.read_entry(entry).force_encoding('UTF-8')
|
|
103
113
|
jdata = JSON.parse(metadata)
|
|
104
114
|
|
|
105
115
|
ols = jdata['ols']
|
|
@@ -164,8 +174,9 @@ module Labimotion
|
|
|
164
174
|
)
|
|
165
175
|
end
|
|
166
176
|
if response.ok?
|
|
167
|
-
Labimotion::Converter.handle_response(data[:a], response, current_user)
|
|
168
|
-
|
|
177
|
+
state = Labimotion::Converter.handle_response(data[:a], response, current_user)
|
|
178
|
+
# A failed dataset mapping also marks the source attachment as ERROR
|
|
179
|
+
state == Labimotion::ConState::ERROR ? state : Labimotion::ConState::PROCESSED
|
|
169
180
|
else
|
|
170
181
|
Labimotion::Converter.logger.error ["Converter Response Error: id: [#{data[:a]&.id}], filename: [#{data[:a]&.filename}], response: #{response}"].join($INPUT_RECORD_SEPARATOR)
|
|
171
182
|
Labimotion::ConState::ERROR
|
|
@@ -30,7 +30,21 @@ module Labimotion
|
|
|
30
30
|
|
|
31
31
|
class << self
|
|
32
32
|
def process_ds(id, current_user = {})
|
|
33
|
-
|
|
33
|
+
ctx = { att: nil }
|
|
34
|
+
# Savepoint: a failure leaves no half-built dataset and keeps the caller's transaction usable
|
|
35
|
+
Attachment.transaction(requires_new: true) { map_ds(id, current_user, ctx) }
|
|
36
|
+
rescue StandardError => e
|
|
37
|
+
Labimotion.log_exception(e, current_user)
|
|
38
|
+
# Not a readable zip: nothing to map, and not an error
|
|
39
|
+
return Labimotion::ConState::NONE if invalid_archive?(e, ctx[:att])
|
|
40
|
+
|
|
41
|
+
# Terminal state: do not fail the request, and do not retry on the next save
|
|
42
|
+
Labimotion.report_exception(e)
|
|
43
|
+
Labimotion::ConState::ERROR
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def map_ds(id, current_user, ctx)
|
|
47
|
+
att = ctx[:att] = find_attachment(id)
|
|
34
48
|
return Labimotion::ConState::NONE if att.nil?
|
|
35
49
|
|
|
36
50
|
result = process(att)
|
|
@@ -132,6 +146,20 @@ module Labimotion
|
|
|
132
146
|
Attachment.find_by(id: id, con_state: Labimotion::ConState::NMR)
|
|
133
147
|
end
|
|
134
148
|
|
|
149
|
+
# True when the upload itself is not a readable zip. A missing or changed file, an encrypted zip
|
|
150
|
+
# or an unsupported compression method is not covered and stays ERROR.
|
|
151
|
+
def invalid_archive?(error, att)
|
|
152
|
+
return false unless error.instance_of?(Zip::Error) || error.is_a?(Zip::DecompressionError)
|
|
153
|
+
|
|
154
|
+
file = att&.attachment_attacher&.file
|
|
155
|
+
path = file&.url
|
|
156
|
+
on_disk = path && File.size?(path)
|
|
157
|
+
return false if on_disk.nil?
|
|
158
|
+
|
|
159
|
+
recorded = file.size
|
|
160
|
+
recorded.nil? || recorded == on_disk
|
|
161
|
+
end
|
|
162
|
+
|
|
135
163
|
def handle_process_result(result, att, id, current_user)
|
|
136
164
|
if result[:is_bagit]
|
|
137
165
|
handle_bagit_result(att, id, current_user)
|
|
@@ -145,7 +173,8 @@ module Labimotion
|
|
|
145
173
|
def handle_bagit_result(att, id, current_user)
|
|
146
174
|
att.update_column(:con_state, Labimotion::ConState::CONVERTED)
|
|
147
175
|
Labimotion::Converter.metadata(id, current_user)
|
|
148
|
-
|
|
176
|
+
# Return the state Converter.metadata stored; CONVERTED means still waiting for an analysis dataset
|
|
177
|
+
Attachment.find_by(id: id)&.con_state || Labimotion::ConState::NONE
|
|
149
178
|
end
|
|
150
179
|
|
|
151
180
|
def invalid_metadata?(result)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'zip'
|
|
4
|
+
|
|
3
5
|
module Labimotion
|
|
4
6
|
module AttachmentConverter
|
|
5
7
|
ACCEPTED_FORMATS = (Rails.configuration.try(:converter).try(:ext) || []).freeze
|
|
@@ -27,6 +29,8 @@ module Labimotion
|
|
|
27
29
|
|
|
28
30
|
return if attachable_id.nil? && con_state != Labimotion::ConState::WAIT
|
|
29
31
|
|
|
32
|
+
# No attachable_id-changed guard: an attachment created already attached must still be processed.
|
|
33
|
+
# Each branch leaves its pending state, so the work runs once.
|
|
30
34
|
current_user = User.find_by(id: created_by)
|
|
31
35
|
case con_state
|
|
32
36
|
when Labimotion::ConState::NMR
|
|
@@ -39,6 +43,26 @@ module Labimotion
|
|
|
39
43
|
Labimotion::Converter.metadata(id, current_user)
|
|
40
44
|
end
|
|
41
45
|
end
|
|
46
|
+
|
|
47
|
+
# Run the zip mapping again for an attachment in ERROR, e.g. after its file was restored.
|
|
48
|
+
# Returns the new con_state.
|
|
49
|
+
def reprocess_converter!
|
|
50
|
+
return con_state unless has_attribute?(:con_state) && File.extname(filename.to_s.downcase) == '.zip'
|
|
51
|
+
|
|
52
|
+
path = attachment_attacher&.file&.url
|
|
53
|
+
return con_state unless path && File.size?(path)
|
|
54
|
+
|
|
55
|
+
self.con_state = converter_output?(path) ? Labimotion::ConState::CONVERTED : Labimotion::ConState::NMR
|
|
56
|
+
update_column(:con_state, con_state)
|
|
57
|
+
exec_converter
|
|
58
|
+
reload.con_state
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
private def converter_output?(path)
|
|
62
|
+
!Zip::File.new(path).find_entry('metadata/converter.json').nil?
|
|
63
|
+
rescue Zip::Error
|
|
64
|
+
false # not a readable zip: the NMR mapper classifies it
|
|
65
|
+
end
|
|
42
66
|
end
|
|
43
67
|
end
|
|
44
68
|
end
|
|
@@ -6,8 +6,18 @@ require 'zip'
|
|
|
6
6
|
require 'labimotion/constants'
|
|
7
7
|
|
|
8
8
|
module Labimotion
|
|
9
|
+
# rubyzip 2.x does not detect encrypted entries and would inflate them as garbage
|
|
10
|
+
class EncryptedZipError < Zip::Error; end
|
|
11
|
+
|
|
9
12
|
class MapperUtils
|
|
10
13
|
class << self
|
|
14
|
+
# Read an entry and close its stream, so no handle on the stored file stays open
|
|
15
|
+
def read_entry(entry)
|
|
16
|
+
raise EncryptedZipError, "Encrypted zip entry: #{entry.name}" if entry.encrypted?
|
|
17
|
+
|
|
18
|
+
entry.get_input_stream(&:read)
|
|
19
|
+
end
|
|
20
|
+
|
|
11
21
|
def load_config(config_json)
|
|
12
22
|
JSON.parse(config_json)
|
|
13
23
|
rescue JSON::ParserError => e
|
|
@@ -37,13 +47,8 @@ module Labimotion
|
|
|
37
47
|
def extract_data_from_zip(zip_file_url, source_map)
|
|
38
48
|
return nil if zip_file_url.nil?
|
|
39
49
|
|
|
50
|
+
# nil means nothing to map; errors are raised so the caller can record ERROR
|
|
40
51
|
process_zip_file(zip_file_url, source_map)
|
|
41
|
-
rescue Zip::Error => e
|
|
42
|
-
Rails.logger.error "Zip file error: #{e.message}"
|
|
43
|
-
nil
|
|
44
|
-
rescue StandardError => e
|
|
45
|
-
Rails.logger.error "Unexpected error extracting metadata: #{e.message}"
|
|
46
|
-
nil
|
|
47
52
|
end
|
|
48
53
|
|
|
49
54
|
def extract_parameters(file_content, parameter_names)
|
|
@@ -121,10 +126,12 @@ module Labimotion
|
|
|
121
126
|
def process_zip_file(zip_file_url, source_map)
|
|
122
127
|
final_parameters = {}
|
|
123
128
|
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
129
|
+
# Never close: closing can rewrite the stored file in place
|
|
130
|
+
zip_file = Zip::File.new(zip_file_url)
|
|
131
|
+
source_map['sourceSelector'].each do |source_name|
|
|
132
|
+
result = process_source(zip_file, source_map[source_name], final_parameters)
|
|
133
|
+
# A BagIt zip is mapped by the converter instead
|
|
134
|
+
return result if result&.dig(:is_bagit)
|
|
128
135
|
end
|
|
129
136
|
|
|
130
137
|
return { is_bagit: false, metadata: final_parameters } if final_parameters.present?
|
|
@@ -136,6 +143,8 @@ module Labimotion
|
|
|
136
143
|
return if invalid_source_config?(source_config)
|
|
137
144
|
|
|
138
145
|
zip_file.each do |entry|
|
|
146
|
+
next if entry.directory?
|
|
147
|
+
|
|
139
148
|
if source_file?(entry, source_config)
|
|
140
149
|
process_file_entry(entry, source_config['parameters'], final_parameters)
|
|
141
150
|
elsif bagit_metadata_file?(entry)
|
|
@@ -145,7 +154,7 @@ module Labimotion
|
|
|
145
154
|
end
|
|
146
155
|
|
|
147
156
|
def process_file_entry(entry, parameters, final_parameters)
|
|
148
|
-
file_content = entry.
|
|
157
|
+
file_content = read_entry(entry).force_encoding(Constants::File::ENCODING)
|
|
149
158
|
extracted_parameters = extract_parameters(file_content, parameters)
|
|
150
159
|
final_parameters.merge!(extracted_parameters) if extracted_parameters.present?
|
|
151
160
|
end
|
data/lib/labimotion/version.rb
CHANGED
data/lib/labimotion.rb
CHANGED
|
@@ -13,6 +13,11 @@ module Labimotion
|
|
|
13
13
|
Labimotion.logger.error(exception.backtrace.join("\n"))
|
|
14
14
|
end
|
|
15
15
|
|
|
16
|
+
# Report a rescued exception to Sentry, if the host app uses it
|
|
17
|
+
def self.report_exception(exception)
|
|
18
|
+
Sentry.capture_exception(exception) if defined?(Sentry)
|
|
19
|
+
end
|
|
20
|
+
|
|
16
21
|
autoload :MapperUtils, 'labimotion/utils/mapper_utils'
|
|
17
22
|
autoload :Utils, 'labimotion/utils/utils'
|
|
18
23
|
|
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.1.
|
|
4
|
+
version: 2.1.2
|
|
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-01
|
|
12
|
+
date: 2026-10-01 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: caxlsx
|
|
@@ -98,7 +98,6 @@ files:
|
|
|
98
98
|
- lib/labimotion/helpers/vocabulary_helpers.rb
|
|
99
99
|
- lib/labimotion/libs/attachment_handler.rb
|
|
100
100
|
- lib/labimotion/libs/converter.rb
|
|
101
|
-
- lib/labimotion/libs/data/layer/StdDataset.json
|
|
102
101
|
- lib/labimotion/libs/data/mapper/Chemwiki.json
|
|
103
102
|
- lib/labimotion/libs/data/mapper/Source.json
|
|
104
103
|
- lib/labimotion/libs/data/vocab/Standard.json
|
|
@@ -1,212 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"layers": {
|
|
3
|
-
"std_general": {
|
|
4
|
-
"wf": false,
|
|
5
|
-
"key": "std_general",
|
|
6
|
-
"std": true,
|
|
7
|
-
"cols": 4,
|
|
8
|
-
"color": "default",
|
|
9
|
-
"label": "Standard General description",
|
|
10
|
-
"style": "panel_generic_heading",
|
|
11
|
-
"fields": [
|
|
12
|
-
{
|
|
13
|
-
"type": "text",
|
|
14
|
-
"field": "title",
|
|
15
|
-
"label": "title",
|
|
16
|
-
"default": "",
|
|
17
|
-
"ontology": {
|
|
18
|
-
"id": "afo:class:http://purl.allotrope.org/ontologies/result#AFR_0001080",
|
|
19
|
-
"iri": "http://purl.allotrope.org/ontologies/result#AFR_0001080",
|
|
20
|
-
"type": "class",
|
|
21
|
-
"label": "title",
|
|
22
|
-
"obo_id": "AFR:0001080",
|
|
23
|
-
"short_form": "AFR_0001080",
|
|
24
|
-
"description": [
|
|
25
|
-
"A title is a prefix or suffix added to a personal name. A title can be a title of honor, that is a title bestowed as an award, a honorary title (academic), that is a recognition of contributions by a non-employee or employee, a hereditary title, that is a title that remains in a family or a job title, that is a designation of a person's position in an organization. [Wikipedia]"
|
|
26
|
-
],
|
|
27
|
-
"ontology_name": "afo",
|
|
28
|
-
"ontology_prefix": "AFO"
|
|
29
|
-
},
|
|
30
|
-
"position": 1,
|
|
31
|
-
"required": false,
|
|
32
|
-
"sub_fields": [],
|
|
33
|
-
"text_sub_fields": []
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"type": "text",
|
|
37
|
-
"field": "data_type",
|
|
38
|
-
"label": "Type of the data ",
|
|
39
|
-
"default": "",
|
|
40
|
-
"readonly": true,
|
|
41
|
-
"position": 2,
|
|
42
|
-
"required": false,
|
|
43
|
-
"sub_fields": [],
|
|
44
|
-
"description": "Ontologies!!!! type of entity",
|
|
45
|
-
"placeholder": "e.g. sample or reaction analysis",
|
|
46
|
-
"text_sub_fields": []
|
|
47
|
-
},
|
|
48
|
-
{
|
|
49
|
-
"type": "text",
|
|
50
|
-
"field": "date",
|
|
51
|
-
"label": "date",
|
|
52
|
-
"default": "",
|
|
53
|
-
"position": 3,
|
|
54
|
-
"required": false,
|
|
55
|
-
"sub_fields": [],
|
|
56
|
-
"text_sub_fields": []
|
|
57
|
-
},
|
|
58
|
-
{
|
|
59
|
-
"type": "text",
|
|
60
|
-
"field": "time",
|
|
61
|
-
"label": "starting time",
|
|
62
|
-
"default": "",
|
|
63
|
-
"position": 4,
|
|
64
|
-
"required": false,
|
|
65
|
-
"sub_fields": [],
|
|
66
|
-
"text_sub_fields": []
|
|
67
|
-
},
|
|
68
|
-
{
|
|
69
|
-
"type": "text",
|
|
70
|
-
"field": "contributor",
|
|
71
|
-
"label": "contributor",
|
|
72
|
-
"default": "",
|
|
73
|
-
"position": 5,
|
|
74
|
-
"required": false,
|
|
75
|
-
"sub_fields": [],
|
|
76
|
-
"description": "",
|
|
77
|
-
"text_sub_fields": []
|
|
78
|
-
},
|
|
79
|
-
{
|
|
80
|
-
"type": "text",
|
|
81
|
-
"field": "creator",
|
|
82
|
-
"label": "creator",
|
|
83
|
-
"default": "",
|
|
84
|
-
"position": 6,
|
|
85
|
-
"required": false,
|
|
86
|
-
"sub_fields": [],
|
|
87
|
-
"description": "the person who upload the analysis",
|
|
88
|
-
"text_sub_fields": []
|
|
89
|
-
},
|
|
90
|
-
{
|
|
91
|
-
"type": "text",
|
|
92
|
-
"field": "operator",
|
|
93
|
-
"label": "operator",
|
|
94
|
-
"default": "",
|
|
95
|
-
"position": 7,
|
|
96
|
-
"required": false,
|
|
97
|
-
"sub_fields": [],
|
|
98
|
-
"text_sub_fields": []
|
|
99
|
-
},
|
|
100
|
-
{
|
|
101
|
-
"type": "text",
|
|
102
|
-
"field": "comments",
|
|
103
|
-
"label": "comments",
|
|
104
|
-
"default": "",
|
|
105
|
-
"position": 8,
|
|
106
|
-
"required": false,
|
|
107
|
-
"sub_fields": [],
|
|
108
|
-
"text_sub_fields": []
|
|
109
|
-
},
|
|
110
|
-
{
|
|
111
|
-
"opid": 8,
|
|
112
|
-
"type": "text",
|
|
113
|
-
"field": "Producer",
|
|
114
|
-
"label": "Producer of the sample",
|
|
115
|
-
"is_voc": true,
|
|
116
|
-
"source": "Segment",
|
|
117
|
-
"default": "",
|
|
118
|
-
"field_id": "Producer",
|
|
119
|
-
"layer_id": "bsi",
|
|
120
|
-
"ontology": {
|
|
121
|
-
"id": "schema:property:https://schema.org/producer",
|
|
122
|
-
"iri": "https://schema.org/producer",
|
|
123
|
-
"type": "property",
|
|
124
|
-
"label": "producer",
|
|
125
|
-
"obo_id": "schema:producer",
|
|
126
|
-
"short_form": "schema_producer",
|
|
127
|
-
"description": [
|
|
128
|
-
"The person or organization who produced the work (e.g. music album, movie, TV/radio series etc.)."
|
|
129
|
-
],
|
|
130
|
-
"ontology_name": "schema",
|
|
131
|
-
"ontology_prefix": "schema"
|
|
132
|
-
},
|
|
133
|
-
"position": 9,
|
|
134
|
-
"source_id": "dce363d5-56d0-4324-9914-0476be999016",
|
|
135
|
-
"identifier": "357b7cd3-ca47-49e0-be47-154d907c2cf5",
|
|
136
|
-
"sub_fields": [],
|
|
137
|
-
"text_sub_fields": []
|
|
138
|
-
}
|
|
139
|
-
],
|
|
140
|
-
"position": -2,
|
|
141
|
-
"condition": "",
|
|
142
|
-
"wf_position": 0
|
|
143
|
-
},
|
|
144
|
-
"std_sample_details": {
|
|
145
|
-
"wf": false,
|
|
146
|
-
"key": "std_sample_details",
|
|
147
|
-
"cols": 3,
|
|
148
|
-
"std": true,
|
|
149
|
-
"color": "none",
|
|
150
|
-
"label": "Standard Sample details",
|
|
151
|
-
"style": "panel_generic_heading",
|
|
152
|
-
"fields": [
|
|
153
|
-
{
|
|
154
|
-
"type": "text",
|
|
155
|
-
"field": "label",
|
|
156
|
-
"label": "label",
|
|
157
|
-
"default": "",
|
|
158
|
-
"position": 1,
|
|
159
|
-
"required": false,
|
|
160
|
-
"sub_fields": [],
|
|
161
|
-
"text_sub_fields": []
|
|
162
|
-
},
|
|
163
|
-
{
|
|
164
|
-
"type": "text",
|
|
165
|
-
"field": "id",
|
|
166
|
-
"label": "id",
|
|
167
|
-
"default": "",
|
|
168
|
-
"position": 2,
|
|
169
|
-
"required": false,
|
|
170
|
-
"sub_fields": [],
|
|
171
|
-
"text_sub_fields": []
|
|
172
|
-
},
|
|
173
|
-
{
|
|
174
|
-
"type": "text",
|
|
175
|
-
"field": "amount",
|
|
176
|
-
"label": "amount",
|
|
177
|
-
"default": "",
|
|
178
|
-
"position": 3,
|
|
179
|
-
"required": false,
|
|
180
|
-
"sub_fields": [],
|
|
181
|
-
"text_sub_fields": []
|
|
182
|
-
},
|
|
183
|
-
{
|
|
184
|
-
"type": "text",
|
|
185
|
-
"field": "volume",
|
|
186
|
-
"label": "volume",
|
|
187
|
-
"default": "",
|
|
188
|
-
"position": 4,
|
|
189
|
-
"required": false,
|
|
190
|
-
"sub_fields": [],
|
|
191
|
-
"text_sub_fields": []
|
|
192
|
-
},
|
|
193
|
-
{
|
|
194
|
-
"type": "text",
|
|
195
|
-
"field": "size",
|
|
196
|
-
"label": "size",
|
|
197
|
-
"default": "",
|
|
198
|
-
"position": 5,
|
|
199
|
-
"required": false,
|
|
200
|
-
"sub_fields": [],
|
|
201
|
-
"text_sub_fields": []
|
|
202
|
-
}
|
|
203
|
-
],
|
|
204
|
-
"position": -1,
|
|
205
|
-
"timeRecord": "",
|
|
206
|
-
"wf_position": 0
|
|
207
|
-
}
|
|
208
|
-
},
|
|
209
|
-
"version": "0.1",
|
|
210
|
-
"select_options": {
|
|
211
|
-
}
|
|
212
|
-
}
|