reaction_api.rb 8.55 KB
Newer Older
Marco Sehrer's avatar
Marco Sehrer committed
1
2
3
4
5
module Chemotion
  class ReactionAPI < Grape::API
    include Grape::Kaminari

    resource :reactions do
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
      namespace :ui_state do
        desc "Delete reactions by UI state"
        params do
          requires :ui_state, type: Hash, desc: "Selected reactions from the UI" do
            optional :all, type: Boolean
            optional :included_ids, type: Array
            optional :excluded_ids, type: Array
          end
        end

        before do
          error!('401 Unauthorized', 401) unless ElementsPolicy.new(@current_user, Reaction.for_ui_state(params[:ui_state])).destroy?
        end

        delete do
          Reaction.for_ui_state(params[:ui_state]).destroy_all
        end
      end
Marco Sehrer's avatar
Marco Sehrer committed
24
25
26
27
28

      desc "Return serialized reactions"
      params do
        optional :collection_id, type: Integer, desc: "Collection id"
      end
29
      paginate per_page: 7, max_per_page: 25, offset: 0
Marco Sehrer's avatar
Marco Sehrer committed
30
31
32

      get do
        scope = if params[:collection_id]
33
          Collection.belongs_to_or_shared_by(current_user.id).find(params[:collection_id]).reactions
Marco Sehrer's avatar
Marco Sehrer committed
34
        else
35
          Reaction.joins(:collections).where('collections.user_id = ?', current_user.id).uniq
Marco Sehrer's avatar
Marco Sehrer committed
36
37
38
39
40
        end.order("created_at DESC")

        paginate(scope)
      end

Fernando D'Agostino's avatar
Fernando D'Agostino committed
41
      desc "Return serialized reaction by id"
Marco Sehrer's avatar
Marco Sehrer committed
42
43
44
45
      params do
        requires :id, type: Integer, desc: "Reaction id"
      end
      route_param :id do
46
47
48
49
        before do
          error!('401 Unauthorized', 401) unless ElementPolicy.new(@current_user, Reaction.find(params[:id])).read?
        end

Marco Sehrer's avatar
Marco Sehrer committed
50
51
52
53
54
        get do
          Reaction.find(params[:id])
        end
      end

55
56
57
58
      desc "Delete a reaction by id"
      params do
        requires :id, type: Integer, desc: "Reaction id"
      end
59
60
61
62
63
64
65
66
67
68
      route_param :id do
        delete do
          Reaction.find(params[:id]).destroy
        end
      end

      desc "Delete reactions by UI state"
      params do
        requires :ui_state, type: Hash, desc: "Selected reactions from the UI"
      end
69
70
71
72
      route_param :id do
        before do
          error!('401 Unauthorized', 401) unless ElementPolicy.new(@current_user, Reaction.find(params[:id])).destroy?
        end
73

74
75
76
        delete do
          Reaction.for_ui_state(params[:ui_state]).destroy_all
        end
77
      end
78
79
80
81
82

      desc "Update reaction by id"
      params do
        requires :id, type: Integer, desc: "Reaction id"
        optional :name, type: String
83
84
85
86
87
88
89
90
91
92
93
        optional :description, type: String
        optional :timestamp_start, type: String
        optional :timestamp_stop, type: String
        optional :observation, type: String
        optional :purification, type: Array, default: []
        optional :dangerous_products, type: Array, default: []
        optional :solvents, type: String
        optional :tlc_description, type: String
        optional :rf_value, type: String
        optional :temperature, type: String
        optional :status, type: String
Marco Sehrer's avatar
Marco Sehrer committed
94
        optional :reaction_svg_file, type: String
95

96
        requires :materials, type: Hash
97
        #optional :literatures, type: Array, default: []
98
99
100
101
102
103
104
105
      end
      route_param :id do

        before do
          error!('401 Unauthorized', 401) unless ElementPolicy.new(@current_user, Reaction.find(params[:id])).update?
        end

        put do
106
107
108
          attributes = declared(params, include_missing: false).symbolize_keys
          materials = attributes.delete(:materials)
          id = attributes.delete(:id)
109

110
111
          if reaction = Reaction.find(id)
            reaction.update_attributes(attributes)
Marco Sehrer's avatar
Marco Sehrer committed
112
            reaction.touch
113
            ReactionUpdator.update_materials_for_reaction(reaction, materials)
114
115
116
117
118
119
120
121
122
123
            reaction.reload
            reaction
          end
        end
      end

      desc "Creates reaction"
      params do
        requires :collection_id, type: Integer, desc: "Collection id"
        optional :name, type: String
124
125
126
127
128
129
130
131
132
133
134
        optional :description, type: String
        optional :timestamp_start, type: String
        optional :timestamp_stop, type: String
        optional :observation, type: String
        optional :purification, type: Array, default: []
        optional :dangerous_products, type: Array, default: []
        optional :solvents, type: String
        optional :tlc_description, type: String
        optional :rf_value, type: String
        optional :temperature, type: String
        optional :status, type: String
Marco Sehrer's avatar
Marco Sehrer committed
135
        optional :reaction_svg_file, type: String
136

137
138
139
140
        requires :materials, type: Hash
      end

      # before do
141
      #   error!('401 Unauthorized', 401) unless ElementPolicy.new(@current_user, Reaction).create?
142
143
144
      # end

      post do
145
146
147
        attributes = declared(params, include_missing: false).symbolize_keys
        materials = attributes.delete(:materials)
        collection_id = attributes.delete(:collection_id)
148

149
150
        collection = Collection.find(collection_id)
        reaction = Reaction.create(attributes)
151

152
        CollectionsReaction.create(reaction: reaction, collection: collection)
153
        if reaction
154
          ReactionUpdator.update_materials_for_reaction(reaction, materials)
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
          reaction.reload
          reaction
        end
      end
    end


  end
end


module ReactionUpdator

  def self.update_materials_for_reaction(reaction, material_attributes)
    #todo: is this correct to set it to the first collection?
    collection_id = reaction.collections.first.id

    materials = OpenStruct.new(material_attributes)

    materials = {
      starting_material: Array(material_attributes['starting_materials']).map{|m| OpenStruct.new(m)},
      reactant: Array(material_attributes['reactants']).map{|m| OpenStruct.new(m)},
      product: Array(material_attributes['products']).map{|m| OpenStruct.new(m)}
    }

    ActiveRecord::Base.transaction do
181
      included_sample_ids = []
182
183
184
185
      materials.each do |material_group, samples|
        reaction_samples_association = reaction.public_send("reactions_#{material_group}_samples")
        samples.each do |sample|

186
          #create new subsample
187
188
189
190
191
192
193
194
195
196
197
198
          if sample.is_new && sample.parent_id

            parent_sample = Sample.find(sample.parent_id)

            subsample = parent_sample.dup
            subsample.parent = parent_sample

            subsample.name = sample.name
            subsample.amount_value = sample.amount_value
            subsample.amount_unit = sample.amount_unit

            subsample.save
199
200
            subsample.reload
            included_sample_ids << subsample.id
201
202
203
204
205
206
207
208
209

            #assign subsample to current collection
            CollectionsSample.create(collection_id: collection_id, sample_id: subsample.id)

            reaction_samples_association.create(
              sample_id: subsample.id,
              equivalent: sample.equivalent,
              reference: sample.reference
            )
210
211

          #update the existing sample
212
213
214
215
216
217
          else
            existing_sample = Sample.find(sample.id)

            existing_sample.amount_value = sample.amount_value
            existing_sample.amount_unit = sample.amount_unit
            existing_sample.save
218
            included_sample_ids << existing_sample.id
219
220
221
222
223
224
225
226
227

            existing_association = reaction_samples_association.find_by(sample_id: sample.id)

            #update existing associations
            if existing_association.present?
              existing_association.update_attributes(
                equivalent: sample.equivalent,
                reference: sample.reference
              )
228
            #sample was moved to other materialgroup
229
230
231
232
233
234
235
236
237
238
239
240
241
242
            else
              #clear existing associations
              reaction.reactions_starting_material_samples.find_by(sample_id: sample.id).try(:destroy)
              reaction.reactions_reactant_samples.find_by(sample_id: sample.id).try(:destroy)
              reaction.reactions_product_samples.find_by(sample_id: sample.id).try(:destroy)

              #create a new association
              reaction_samples_association.create(
                sample_id: sample.id,
                equivalent: sample.equivalent,
                reference: sample.reference
              )
            end
          end
243

244
245
        end
      end
246
247
248
249
250
251
252
253
254
255
256
257

      #delete all samples not anymore in one of the groups

      current_sample_ids = [
        reaction.reactions_starting_material_samples.pluck(:sample_id),
        reaction.reactions_reactant_samples.pluck(:sample_id),
        reaction.reactions_product_samples.pluck(:sample_id)
      ].flatten.uniq

      deleted_sample_ids = current_sample_ids - included_sample_ids
      Sample.where(id: deleted_sample_ids).destroy_all

258
259
      #for testing
      #raise ActiveRecord::Rollback
Marco Sehrer's avatar
Marco Sehrer committed
260
    end
Marco Sehrer's avatar
Marco Sehrer committed
261
262
263
264

    # to update the SVG
    reaction.reload
    reaction.save
Marco Sehrer's avatar
Marco Sehrer committed
265
266
  end
end