labimotion 2.2.0.rc12 → 2.2.0.rc13
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/generic_element_api.rb +0 -1
- data/lib/labimotion/apis/labimotion_api.rb +0 -1
- data/lib/labimotion/apis/mtt_api.rb +31 -39
- data/lib/labimotion/helpers/element_helpers.rb +15 -21
- data/lib/labimotion/helpers/mtt_helpers.rb +92 -0
- data/lib/labimotion/models/concerns/element_fetchable.rb +2 -2
- data/lib/labimotion/models/element.rb +2 -2
- data/lib/labimotion/utils/search.rb +2 -2
- data/lib/labimotion/version.rb +1 -1
- data/lib/labimotion.rb +0 -1
- metadata +1 -2
- data/lib/labimotion/apis/dose_resp_request_api.rb +0 -241
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0ec9d0808468d0b4447f577e3932d0e421c248020df87eeee277a5242fc3a0e6
|
|
4
|
+
data.tar.gz: de0e10512eb0ce1e0bb72fc60983119637c869e43816aed20c0f88419a474d37
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eb60f1b75b504783636051ee83553e44b8edd71c03cf42abdfd8187405bd53ab150d5a136c7321a4a29b3a6a947aa397fba77b2ee8df180674a61f22b3c08ed5
|
|
7
|
+
data.tar.gz: 8cbad024d4e4b12275eb2fe938fcfcbf8540ab85f1d03cd6d86059ed4aa51bc5c2c555aa1c14e8a823031d74c6107f133fe302c5cea522ef901b8a1e305a97e9
|
|
@@ -432,7 +432,6 @@ module Labimotion
|
|
|
432
432
|
desc 'Return serialized elements of current user'
|
|
433
433
|
params do
|
|
434
434
|
optional :collection_id, type: Integer, desc: 'Collection id'
|
|
435
|
-
optional :sync_collection_id, type: Integer, desc: 'SyncCollectionsUser id'
|
|
436
435
|
optional :el_type, type: String, desc: 'element klass name'
|
|
437
436
|
optional :from_date, type: Integer, desc: 'created_date from in ms'
|
|
438
437
|
optional :to_date, type: Integer, desc: 'created_date to in ms'
|
|
@@ -14,7 +14,6 @@ module Labimotion
|
|
|
14
14
|
mount Labimotion::VocabularyAPI
|
|
15
15
|
mount Labimotion::UserAPI
|
|
16
16
|
mount Labimotion::MttAPI
|
|
17
|
-
mount Labimotion::DoseRespRequestAPI
|
|
18
17
|
mount Labimotion::ElementVariationAPI
|
|
19
18
|
mount Labimotion::LabimotionDoiAPI
|
|
20
19
|
mount Labimotion::LabimotionTemplateBrowseAPI
|
|
@@ -38,37 +38,7 @@ module Labimotion
|
|
|
38
38
|
.order(created_at: :desc)
|
|
39
39
|
|
|
40
40
|
# Return formatted response
|
|
41
|
-
requests.map
|
|
42
|
-
{
|
|
43
|
-
id: req.id,
|
|
44
|
-
request_id: req.request_id,
|
|
45
|
-
element_id: req.element_id,
|
|
46
|
-
state: req.state,
|
|
47
|
-
state_name: case req.state
|
|
48
|
-
when Labimotion::DoseRespRequest::STATE_ERROR then 'error'
|
|
49
|
-
when Labimotion::DoseRespRequest::STATE_INITIAL then 'initial'
|
|
50
|
-
when Labimotion::DoseRespRequest::STATE_PROCESSING then 'processing'
|
|
51
|
-
when Labimotion::DoseRespRequest::STATE_COMPLETED then 'completed'
|
|
52
|
-
else 'unknown'
|
|
53
|
-
end,
|
|
54
|
-
created_at: req.created_at,
|
|
55
|
-
expires_at: req.expires_at,
|
|
56
|
-
expired: req.expired?,
|
|
57
|
-
revoked: req.revoked?,
|
|
58
|
-
active: req.active?,
|
|
59
|
-
resp_message: req.resp_message,
|
|
60
|
-
last_accessed_at: req.last_accessed_at,
|
|
61
|
-
access_count: req.access_count || 0,
|
|
62
|
-
outputs: req.dose_resp_outputs.map do |output|
|
|
63
|
-
{
|
|
64
|
-
id: output.id,
|
|
65
|
-
output_data: output.output_data,
|
|
66
|
-
notes: output.notes,
|
|
67
|
-
created_at: output.created_at
|
|
68
|
-
}
|
|
69
|
-
end
|
|
70
|
-
}
|
|
71
|
-
end
|
|
41
|
+
requests.map { |req| mtt_request_json(req, include_outputs: true) }
|
|
72
42
|
end
|
|
73
43
|
|
|
74
44
|
desc 'Delete one or multiple MTT requests'
|
|
@@ -132,13 +102,7 @@ module Labimotion
|
|
|
132
102
|
id: request.id,
|
|
133
103
|
request_id: request.request_id,
|
|
134
104
|
state: request.state,
|
|
135
|
-
state_name:
|
|
136
|
-
when Labimotion::DoseRespRequest::STATE_ERROR then 'error'
|
|
137
|
-
when Labimotion::DoseRespRequest::STATE_INITIAL then 'initial'
|
|
138
|
-
when Labimotion::DoseRespRequest::STATE_PROCESSING then 'processing'
|
|
139
|
-
when Labimotion::DoseRespRequest::STATE_COMPLETED then 'completed'
|
|
140
|
-
else 'unknown'
|
|
141
|
-
end,
|
|
105
|
+
state_name: mtt_state_name(request.state),
|
|
142
106
|
resp_message: request.resp_message,
|
|
143
107
|
revoked: request.revoked?,
|
|
144
108
|
updated_at: request.updated_at
|
|
@@ -183,6 +147,34 @@ module Labimotion
|
|
|
183
147
|
rescue StandardError => e
|
|
184
148
|
error!("Error deleting outputs: #{e.message}", 500)
|
|
185
149
|
end
|
|
150
|
+
|
|
151
|
+
desc 'Delete a single result (by sample name) from an output'
|
|
152
|
+
params do
|
|
153
|
+
requires :id, type: Integer, desc: 'Output ID'
|
|
154
|
+
requires :sample_name, type: String, desc: 'Sample name (result[].name) to remove'
|
|
155
|
+
end
|
|
156
|
+
delete ':id/results' do
|
|
157
|
+
# Only operate on outputs whose parent request belongs to the current user
|
|
158
|
+
output = Labimotion::DoseRespOutput
|
|
159
|
+
.joins(:dose_resp_request)
|
|
160
|
+
.where(dose_resp_requests: { created_by: current_user.id })
|
|
161
|
+
.find_by(id: params[:id])
|
|
162
|
+
|
|
163
|
+
error!('Output not found or unauthorized', 404) unless output
|
|
164
|
+
|
|
165
|
+
outcome = remove_mtt_result_by_sample_name(output, params[:sample_name])
|
|
166
|
+
error!('Result not found in output', 404) unless outcome[:removed]
|
|
167
|
+
|
|
168
|
+
{
|
|
169
|
+
success: true,
|
|
170
|
+
message: "Removed result '#{params[:sample_name]}' from output #{params[:id]}",
|
|
171
|
+
output_id: params[:id],
|
|
172
|
+
output_deleted: outcome[:output_deleted],
|
|
173
|
+
output: outcome[:output_deleted] ? nil : mtt_output_json(output)
|
|
174
|
+
}
|
|
175
|
+
rescue StandardError => e
|
|
176
|
+
error!("Error deleting result: #{e.message}", 500)
|
|
177
|
+
end
|
|
186
178
|
end
|
|
187
179
|
|
|
188
180
|
namespace :create_mtt_request do
|
|
@@ -194,7 +186,7 @@ module Labimotion
|
|
|
194
186
|
# Find element and wellplates
|
|
195
187
|
element = Labimotion::Element.find_by(id: params[:id])
|
|
196
188
|
error!('Element not found', 404) unless element
|
|
197
|
-
|
|
189
|
+
|
|
198
190
|
# Verify user has update permission
|
|
199
191
|
error!('Unauthorized', 403) unless ElementPolicy.new(current_user, element).update?
|
|
200
192
|
|
|
@@ -215,7 +215,7 @@ module Labimotion
|
|
|
215
215
|
layer, field = params[:sort_column].split('.')
|
|
216
216
|
|
|
217
217
|
element_klass = Labimotion::ElementKlass.find_by(name: params[:el_type])
|
|
218
|
-
allowed_fields = element_klass
|
|
218
|
+
allowed_fields = element_klass&.properties_release&.dig(Labimotion::Prop::LAYERS, layer, Labimotion::Prop::FIELDS)&.pluck('field') || []
|
|
219
219
|
|
|
220
220
|
if field.in?(allowed_fields)
|
|
221
221
|
query = ActiveRecord::Base.sanitize_sql(
|
|
@@ -247,27 +247,21 @@ module Labimotion
|
|
|
247
247
|
end
|
|
248
248
|
|
|
249
249
|
def list_serialized_elements(params, current_user)
|
|
250
|
-
|
|
251
|
-
if params[:collection_id]
|
|
252
|
-
Collection
|
|
253
|
-
.belongs_to_or_shared_by(current_user.id, current_user.group_ids)
|
|
254
|
-
.find_by(id: params[:collection_id])&.id
|
|
255
|
-
elsif params[:sync_collection_id]
|
|
256
|
-
current_user
|
|
257
|
-
.all_sync_in_collections_users
|
|
258
|
-
.find_by(id: params[:sync_collection_id])&.collection&.id
|
|
259
|
-
end
|
|
250
|
+
scope = Labimotion::Element.none
|
|
260
251
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
252
|
+
if params[:collection_id]
|
|
253
|
+
begin
|
|
254
|
+
collection = Collection.accessible_for(current_user).find(params[:collection_id])
|
|
255
|
+
scope = collection.elements
|
|
256
|
+
.joins(:element_klass)
|
|
257
|
+
.where(element_klasses: { name: params[:el_type] })
|
|
258
|
+
.includes(:tag)
|
|
259
|
+
rescue ActiveRecord::RecordNotFound
|
|
260
|
+
Labimotion::Element.none
|
|
261
|
+
end
|
|
269
262
|
else
|
|
270
|
-
|
|
263
|
+
# All collection of current_user
|
|
264
|
+
scope = Labimotion::Element.for_user(current_user.id)
|
|
271
265
|
end
|
|
272
266
|
|
|
273
267
|
## TO DO: refactor labimotion
|
|
@@ -278,7 +272,7 @@ module Labimotion
|
|
|
278
272
|
layer, field = params[:sort_column].split('.')
|
|
279
273
|
|
|
280
274
|
element_klass = Labimotion::ElementKlass.find_by(name: params[:el_type])
|
|
281
|
-
allowed_fields = element_klass
|
|
275
|
+
allowed_fields = element_klass&.properties_release&.dig(Labimotion::Prop::LAYERS, layer, Labimotion::Prop::FIELDS)&.pluck('field') || []
|
|
282
276
|
|
|
283
277
|
if field.in?(allowed_fields)
|
|
284
278
|
query = ActiveRecord::Base.sanitize_sql(
|
|
@@ -25,6 +25,98 @@ module Labimotion
|
|
|
25
25
|
ENV['MTT_EXTERNAL_APP_URL'] || 'http://localhost:4050'
|
|
26
26
|
end
|
|
27
27
|
|
|
28
|
+
# --- Serialization helpers (shared across the MTT request endpoints) ---
|
|
29
|
+
|
|
30
|
+
def mtt_state_name(state)
|
|
31
|
+
case state
|
|
32
|
+
when Labimotion::DoseRespRequest::STATE_ERROR then 'error'
|
|
33
|
+
when Labimotion::DoseRespRequest::STATE_INITIAL then 'initial'
|
|
34
|
+
when Labimotion::DoseRespRequest::STATE_PROCESSING then 'processing'
|
|
35
|
+
when Labimotion::DoseRespRequest::STATE_COMPLETED then 'completed'
|
|
36
|
+
else 'unknown'
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def mtt_output_json(output)
|
|
41
|
+
{
|
|
42
|
+
id: output.id,
|
|
43
|
+
output_data: output.output_data,
|
|
44
|
+
notes: output.notes,
|
|
45
|
+
created_at: output.created_at
|
|
46
|
+
}
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def mtt_request_json(req, include_outputs: false)
|
|
50
|
+
json = {
|
|
51
|
+
id: req.id,
|
|
52
|
+
request_id: req.request_id,
|
|
53
|
+
element_id: req.element_id,
|
|
54
|
+
state: req.state,
|
|
55
|
+
state_name: mtt_state_name(req.state),
|
|
56
|
+
created_at: req.created_at,
|
|
57
|
+
expires_at: req.expires_at,
|
|
58
|
+
expired: req.expired?,
|
|
59
|
+
revoked: req.revoked?,
|
|
60
|
+
active: req.active?,
|
|
61
|
+
resp_message: req.resp_message,
|
|
62
|
+
last_accessed_at: req.last_accessed_at,
|
|
63
|
+
access_count: req.access_count || 0
|
|
64
|
+
}
|
|
65
|
+
json[:outputs] = req.dose_resp_outputs.map { |output| mtt_output_json(output) } if include_outputs
|
|
66
|
+
json
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# The sample name of a result node, i.e. result[0].name. Used to match a
|
|
70
|
+
# single result row across both output_data shapes (see below). JSONB columns
|
|
71
|
+
# deserialize with string keys; symbol keys are tolerated defensively.
|
|
72
|
+
def mtt_result_name(node)
|
|
73
|
+
return nil unless node.is_a?(Hash)
|
|
74
|
+
|
|
75
|
+
result = node['result'] || node[:result]
|
|
76
|
+
return nil unless result.is_a?(Array) && result.first.is_a?(Hash)
|
|
77
|
+
|
|
78
|
+
result.first['name'] || result.first[:name]
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Remove a single result row (matched by sample name) from an output's
|
|
82
|
+
# output_data JSON, supporting both the new (Output[].items[]) and the legacy
|
|
83
|
+
# (Output[].result[]) shapes. If the output has no results left afterwards the
|
|
84
|
+
# record is soft-deleted (acts_as_paranoid), consistent with the bulk delete.
|
|
85
|
+
#
|
|
86
|
+
# Returns { removed:, output_deleted: }.
|
|
87
|
+
def remove_mtt_result_by_sample_name(output, sample_name)
|
|
88
|
+
data = output.output_data || {}
|
|
89
|
+
groups = data['Output'] || data[:Output]
|
|
90
|
+
return { removed: false, output_deleted: false } unless groups.is_a?(Array)
|
|
91
|
+
|
|
92
|
+
removed = false
|
|
93
|
+
new_groups = groups.map do |group|
|
|
94
|
+
items = group['items'] || group[:items]
|
|
95
|
+
if items.is_a?(Array)
|
|
96
|
+
# New structure: drop the matching item(s) from the group.
|
|
97
|
+
kept = items.reject { |item| mtt_result_name(item) == sample_name }
|
|
98
|
+
removed ||= kept.length != items.length
|
|
99
|
+
kept.empty? ? nil : group.merge('items' => kept)
|
|
100
|
+
elsif mtt_result_name(group) == sample_name
|
|
101
|
+
# Legacy structure: drop the whole group.
|
|
102
|
+
removed = true
|
|
103
|
+
nil
|
|
104
|
+
else
|
|
105
|
+
group
|
|
106
|
+
end
|
|
107
|
+
end.compact
|
|
108
|
+
|
|
109
|
+
return { removed: false, output_deleted: false } unless removed
|
|
110
|
+
|
|
111
|
+
if new_groups.empty?
|
|
112
|
+
output.destroy
|
|
113
|
+
{ removed: true, output_deleted: true }
|
|
114
|
+
else
|
|
115
|
+
output.update!(output_data: data.merge('Output' => new_groups))
|
|
116
|
+
{ removed: true, output_deleted: false }
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
28
120
|
def validate_token(token)
|
|
29
121
|
# Find the request by access token
|
|
30
122
|
request = Labimotion::DoseRespRequest.find_by(access_token: token)
|
|
@@ -26,9 +26,9 @@ module Labimotion
|
|
|
26
26
|
joins(collections: :user).where(collections: { user_id: user_id })
|
|
27
27
|
)
|
|
28
28
|
|
|
29
|
-
# Shared
|
|
29
|
+
# Shared records
|
|
30
30
|
shared = apply_filters.call(
|
|
31
|
-
|
|
31
|
+
left_joins(:collection_shares).where(collection_shares: { shared_with_id: user_id })
|
|
32
32
|
)
|
|
33
33
|
|
|
34
34
|
# Combine (remove duplicates), order, and limit
|
|
@@ -141,9 +141,9 @@ module Labimotion
|
|
|
141
141
|
joins(collections: :user).where(collections: { user_id: user_id })
|
|
142
142
|
)
|
|
143
143
|
|
|
144
|
-
# Shared
|
|
144
|
+
# Shared elements
|
|
145
145
|
shared = apply_filters.call(
|
|
146
|
-
|
|
146
|
+
left_joins(:collection_shares).where(collection_shares: { shared_with_id: user_id })
|
|
147
147
|
)
|
|
148
148
|
|
|
149
149
|
# Combine (remove duplicates), order, and limit
|
|
@@ -12,7 +12,7 @@ module Labimotion
|
|
|
12
12
|
end
|
|
13
13
|
|
|
14
14
|
def self.elements_search(params, current_user, c_id, dl)
|
|
15
|
-
collection = Collection.
|
|
15
|
+
collection = Collection.accessible_for(current_user).find(c_id)
|
|
16
16
|
element_scope = Labimotion::Element.joins(:collections_elements).where('collections_elements.collection_id = ?', collection.id).joins(:element_klass).where('element_klasses.id = elements.element_klass_id AND element_klasses.name = ?', params[:selection][:genericElName])
|
|
17
17
|
element_scope = element_scope.where('elements.name like (?)', "%#{params[:selection][:searchName]}%") if params[:selection][:searchName].present?
|
|
18
18
|
element_scope = element_scope.where('elements.short_label like (?)', "%#{params[:selection][:searchShowLabel]}%") if params[:selection][:searchShowLabel].present?
|
|
@@ -97,7 +97,7 @@ module Labimotion
|
|
|
97
97
|
def self.samples_search(c_id = @c_id)
|
|
98
98
|
sqls = []
|
|
99
99
|
sps = params[:selection][:searchProperties]
|
|
100
|
-
collection = Collection.
|
|
100
|
+
collection = Collection.accessible_for(current_user).find(c_id)
|
|
101
101
|
element_scope = Sample.joins(:collections_samples).where('collections_samples.collection_id = ?', collection.id)
|
|
102
102
|
return element_scope if sps.empty?
|
|
103
103
|
|
data/lib/labimotion/version.rb
CHANGED
data/lib/labimotion.rb
CHANGED
|
@@ -29,7 +29,6 @@ module Labimotion
|
|
|
29
29
|
autoload :VocabularyAPI, 'labimotion/apis/vocabulary_api'
|
|
30
30
|
autoload :UserAPI, 'labimotion/apis/user_api'
|
|
31
31
|
autoload :MttAPI, 'labimotion/apis/mtt_api'
|
|
32
|
-
autoload :DoseRespRequestAPI, 'labimotion/apis/dose_resp_request_api'
|
|
33
32
|
autoload :ElementVariationAPI, 'labimotion/apis/element_variation_api'
|
|
34
33
|
autoload :LabimotionDoiAPI, 'labimotion/apis/labimotion_doi_api'
|
|
35
34
|
autoload :LabimotionTemplateBrowseAPI, 'labimotion/apis/labimotion_template_browse_api'
|
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.2.0.
|
|
4
|
+
version: 2.2.0.rc13
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Chia-Lin Lin
|
|
@@ -58,7 +58,6 @@ files:
|
|
|
58
58
|
- README.md
|
|
59
59
|
- lib/labimotion.rb
|
|
60
60
|
- lib/labimotion/apis/converter_api.rb
|
|
61
|
-
- lib/labimotion/apis/dose_resp_request_api.rb
|
|
62
61
|
- lib/labimotion/apis/element_variation_api.rb
|
|
63
62
|
- lib/labimotion/apis/exporter_api.rb
|
|
64
63
|
- lib/labimotion/apis/generic_dataset_api.rb
|
|
@@ -1,241 +0,0 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'labimotion/version'
|
|
4
|
-
|
|
5
|
-
module Labimotion
|
|
6
|
-
# Dose Response Request API
|
|
7
|
-
class DoseRespRequestAPI < Grape::API
|
|
8
|
-
helpers Labimotion::ParamHelpers
|
|
9
|
-
|
|
10
|
-
resource :dose_resp_requests do
|
|
11
|
-
desc 'Get all dose response requests for current user'
|
|
12
|
-
params do
|
|
13
|
-
optional :element_id, type: Integer, desc: 'Filter by element ID'
|
|
14
|
-
optional :state, type: Integer, desc: 'Filter by state (-1, 0, 1, 2)'
|
|
15
|
-
optional :page, type: Integer, desc: 'Page number', default: 1
|
|
16
|
-
optional :per_page, type: Integer, desc: 'Items per page', default: 20
|
|
17
|
-
end
|
|
18
|
-
get do
|
|
19
|
-
requests = Labimotion::DoseRespRequest.where(created_by: current_user.id)
|
|
20
|
-
|
|
21
|
-
# Apply filters
|
|
22
|
-
requests = requests.where(element_id: params[:element_id]) if params[:element_id]
|
|
23
|
-
requests = requests.where(state: params[:state]) if params[:state]
|
|
24
|
-
|
|
25
|
-
# Pagination
|
|
26
|
-
page = params[:page] || 1
|
|
27
|
-
per_page = [params[:per_page] || 20, 100].min # Max 100 per page
|
|
28
|
-
|
|
29
|
-
total = requests.count
|
|
30
|
-
requests = requests.order(created_at: :desc)
|
|
31
|
-
.offset((page - 1) * per_page)
|
|
32
|
-
.limit(per_page)
|
|
33
|
-
|
|
34
|
-
{
|
|
35
|
-
requests: requests.map do |req|
|
|
36
|
-
{
|
|
37
|
-
id: req.id,
|
|
38
|
-
request_id: req.request_id,
|
|
39
|
-
element_id: req.element_id,
|
|
40
|
-
state: req.state,
|
|
41
|
-
state_label: state_label(req.state),
|
|
42
|
-
expires_at: req.expires_at,
|
|
43
|
-
created_at: req.created_at,
|
|
44
|
-
updated_at: req.updated_at,
|
|
45
|
-
first_accessed_at: req.first_accessed_at,
|
|
46
|
-
last_accessed_at: req.last_accessed_at,
|
|
47
|
-
access_count: req.access_count,
|
|
48
|
-
resp_message: req.resp_message,
|
|
49
|
-
active: req.active?
|
|
50
|
-
}
|
|
51
|
-
end,
|
|
52
|
-
pagination: {
|
|
53
|
-
page: page,
|
|
54
|
-
per_page: per_page,
|
|
55
|
-
total: total,
|
|
56
|
-
total_pages: (total.to_f / per_page).ceil
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
rescue StandardError => e
|
|
60
|
-
error!("Error: #{e.message}", 500)
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
desc 'Get a dose response request by ID'
|
|
64
|
-
params do
|
|
65
|
-
requires :id, type: Integer, desc: 'Request ID'
|
|
66
|
-
end
|
|
67
|
-
get ':id' do
|
|
68
|
-
request = Labimotion::DoseRespRequest.find_by(id: params[:id])
|
|
69
|
-
error!('Request not found', 404) unless request
|
|
70
|
-
|
|
71
|
-
# Check authorization
|
|
72
|
-
error!('Unauthorized', 403) unless request.created_by == current_user.id
|
|
73
|
-
|
|
74
|
-
{
|
|
75
|
-
id: request.id,
|
|
76
|
-
request_id: request.request_id,
|
|
77
|
-
element_id: request.element_id,
|
|
78
|
-
state: request.state,
|
|
79
|
-
state_label: state_label(request.state),
|
|
80
|
-
wellplates_metadata: request.wellplates_metadata,
|
|
81
|
-
input_metadata: request.input_metadata,
|
|
82
|
-
expires_at: request.expires_at,
|
|
83
|
-
revoked_at: request.revoked_at,
|
|
84
|
-
created_at: request.created_at,
|
|
85
|
-
updated_at: request.updated_at,
|
|
86
|
-
first_accessed_at: request.first_accessed_at,
|
|
87
|
-
last_accessed_at: request.last_accessed_at,
|
|
88
|
-
access_count: request.access_count,
|
|
89
|
-
resp_message: request.resp_message,
|
|
90
|
-
active: request.active?,
|
|
91
|
-
expired: request.expired?,
|
|
92
|
-
revoked: request.revoked?
|
|
93
|
-
}
|
|
94
|
-
rescue StandardError => e
|
|
95
|
-
error!("Error: #{e.message}", 500)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
|
-
desc 'Update a dose response request'
|
|
99
|
-
params do
|
|
100
|
-
requires :id, type: Integer, desc: 'Request ID'
|
|
101
|
-
optional :state, type: Integer, desc: 'State', values: [-1, 0, 1, 2]
|
|
102
|
-
optional :resp_message, type: String, desc: 'Response message'
|
|
103
|
-
optional :wellplates_metadata, type: Hash, desc: 'Wellplates metadata'
|
|
104
|
-
end
|
|
105
|
-
put ':id' do
|
|
106
|
-
request = Labimotion::DoseRespRequest.find_by(id: params[:id])
|
|
107
|
-
error!('Request not found', 404) unless request
|
|
108
|
-
|
|
109
|
-
# Check authorization
|
|
110
|
-
error!('Unauthorized', 403) unless request.created_by == current_user.id
|
|
111
|
-
|
|
112
|
-
update_params = {}
|
|
113
|
-
update_params[:state] = params[:state] if params[:state]
|
|
114
|
-
update_params[:resp_message] = params[:resp_message] if params[:resp_message]
|
|
115
|
-
update_params[:wellplates_metadata] = params[:wellplates_metadata] if params[:wellplates_metadata]
|
|
116
|
-
|
|
117
|
-
request.update!(update_params)
|
|
118
|
-
|
|
119
|
-
{
|
|
120
|
-
success: true,
|
|
121
|
-
message: 'Request updated successfully',
|
|
122
|
-
request: {
|
|
123
|
-
id: request.id,
|
|
124
|
-
request_id: request.request_id,
|
|
125
|
-
state: request.state,
|
|
126
|
-
state_label: state_label(request.state),
|
|
127
|
-
resp_message: request.resp_message,
|
|
128
|
-
updated_at: request.updated_at
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
rescue ActiveRecord::RecordInvalid => e
|
|
132
|
-
error!("Validation error: #{e.message}", 422)
|
|
133
|
-
rescue StandardError => e
|
|
134
|
-
error!("Error: #{e.message}", 500)
|
|
135
|
-
end
|
|
136
|
-
|
|
137
|
-
desc 'Revoke a dose response request'
|
|
138
|
-
params do
|
|
139
|
-
requires :id, type: Integer, desc: 'Request ID'
|
|
140
|
-
end
|
|
141
|
-
post ':id/revoke' do
|
|
142
|
-
request = Labimotion::DoseRespRequest.find_by(id: params[:id])
|
|
143
|
-
error!('Request not found', 404) unless request
|
|
144
|
-
|
|
145
|
-
# Check authorization
|
|
146
|
-
error!('Unauthorized', 403) unless request.created_by == current_user.id
|
|
147
|
-
|
|
148
|
-
request.revoke!
|
|
149
|
-
|
|
150
|
-
{
|
|
151
|
-
success: true,
|
|
152
|
-
message: 'Request revoked successfully',
|
|
153
|
-
request: {
|
|
154
|
-
id: request.id,
|
|
155
|
-
request_id: request.request_id,
|
|
156
|
-
state: request.state,
|
|
157
|
-
revoked_at: request.revoked_at,
|
|
158
|
-
active: request.active?
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
rescue StandardError => e
|
|
162
|
-
error!("Error: #{e.message}", 500)
|
|
163
|
-
end
|
|
164
|
-
|
|
165
|
-
desc 'Delete a dose response request'
|
|
166
|
-
params do
|
|
167
|
-
requires :id, type: Integer, desc: 'Request ID'
|
|
168
|
-
end
|
|
169
|
-
delete ':id' do
|
|
170
|
-
request = Labimotion::DoseRespRequest.find_by(id: params[:id])
|
|
171
|
-
error!('Request not found', 404) unless request
|
|
172
|
-
|
|
173
|
-
# Check authorization
|
|
174
|
-
error!('Unauthorized', 403) unless request.created_by == current_user.id
|
|
175
|
-
|
|
176
|
-
# Soft delete if acts_as_paranoid is enabled
|
|
177
|
-
request.destroy
|
|
178
|
-
|
|
179
|
-
{
|
|
180
|
-
success: true,
|
|
181
|
-
message: 'Request deleted successfully'
|
|
182
|
-
}
|
|
183
|
-
rescue StandardError => e
|
|
184
|
-
error!("Error: #{e.message}", 500)
|
|
185
|
-
end
|
|
186
|
-
|
|
187
|
-
desc 'Get dose response requests by element ID'
|
|
188
|
-
params do
|
|
189
|
-
requires :element_id, type: Integer, desc: 'Element ID'
|
|
190
|
-
end
|
|
191
|
-
get 'by_element/:element_id' do
|
|
192
|
-
element = Labimotion::Element.find_by(id: params[:element_id])
|
|
193
|
-
error!('Element not found', 404) unless element
|
|
194
|
-
|
|
195
|
-
# Check if user has access to element
|
|
196
|
-
policy = ElementPolicy.new(current_user, element)
|
|
197
|
-
error!('Unauthorized', 403) unless policy.read?
|
|
198
|
-
|
|
199
|
-
requests = Labimotion::DoseRespRequest.where(element_id: params[:element_id])
|
|
200
|
-
.where(created_by: current_user.id)
|
|
201
|
-
.order(created_at: :desc)
|
|
202
|
-
|
|
203
|
-
{
|
|
204
|
-
element_id: element.id,
|
|
205
|
-
element_name: element.name,
|
|
206
|
-
requests: requests.map do |req|
|
|
207
|
-
{
|
|
208
|
-
id: req.id,
|
|
209
|
-
request_id: req.request_id,
|
|
210
|
-
state: req.state,
|
|
211
|
-
state_label: state_label(req.state),
|
|
212
|
-
expires_at: req.expires_at,
|
|
213
|
-
created_at: req.created_at,
|
|
214
|
-
access_count: req.access_count,
|
|
215
|
-
active: req.active?
|
|
216
|
-
}
|
|
217
|
-
end
|
|
218
|
-
}
|
|
219
|
-
rescue StandardError => e
|
|
220
|
-
error!("Error: #{e.message}", 500)
|
|
221
|
-
end
|
|
222
|
-
end
|
|
223
|
-
|
|
224
|
-
helpers do
|
|
225
|
-
def state_label(state)
|
|
226
|
-
case state
|
|
227
|
-
when Labimotion::DoseRespRequest::STATE_ERROR
|
|
228
|
-
'error'
|
|
229
|
-
when Labimotion::DoseRespRequest::STATE_INITIAL
|
|
230
|
-
'initial'
|
|
231
|
-
when Labimotion::DoseRespRequest::STATE_PROCESSING
|
|
232
|
-
'processing'
|
|
233
|
-
when Labimotion::DoseRespRequest::STATE_COMPLETED
|
|
234
|
-
'completed'
|
|
235
|
-
else
|
|
236
|
-
'unknown'
|
|
237
|
-
end
|
|
238
|
-
end
|
|
239
|
-
end
|
|
240
|
-
end
|
|
241
|
-
end
|