• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

pulibrary / pdc_describe / 10e67590-c335-4eef-8035-ba8ad42eff83

pending completion
10e67590-c335-4eef-8035-ba8ad42eff83

Pull #1060

circleci

jrgriffiniii
wip
Pull Request #1060: Integrating Support for Upload Snapshots

155 of 155 new or added lines in 8 files covered. (100.0%)

2095 of 2149 relevant lines covered (97.49%)

174.83 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.07
/app/controllers/works_controller.rb
1
# frozen_string_literal: true
2

3
require "nokogiri"
1✔
4
require "open-uri"
1✔
5

6
# rubocop:disable Metrics/ClassLength
7
class WorksController < ApplicationController
1✔
8
  include ERB::Util
1✔
9
  around_action :rescue_aasm_error, only: [:approve, :withdraw, :resubmit, :validate, :create]
1✔
10

11
  skip_before_action :authenticate_user!
1✔
12
  before_action :authenticate_user!, unless: :public_request?
1✔
13

14
  ##
15
  # Public requests are requests that do not require authentication.
16
  # This is to enable PDC Discovery to index approved content via the RSS feed and
17
  # .json calls to individual works without needing to log in as a user.
18
  # Note that only approved works can be fetched for indexing.
19
  def public_request?
1✔
20
    return true if action_name == "index" && request.format.symbol == :rss
486✔
21
    return true if action_name == "show" && request.format.symbol == :json && Work.find(params[:id]).state == "approved"
484✔
22
    false
483✔
23
  end
24

25
  def index
1✔
26
    @works = Work.all
4✔
27
    respond_to do |format|
4✔
28
      format.html
4✔
29
      format.rss { render layout: false }
6✔
30
    end
31
  end
32

33
  # Renders the "step 0" information page before creating a new dataset
34
  def new
1✔
35
    if wizard_mode?
30✔
36
      render "new_submission"
8✔
37
    else
38
      @work = Work.new(created_by_user_id: current_user.id, collection: current_user.default_collection)
22✔
39
    end
40
  end
41

42
  def create
1✔
43
    # TODO: We need to process files submitted by the user in this method.
44
    # We currently do not and therefore there are not saved to the work.
45
    # See https://github.com/pulibrary/pdc_describe/issues/1041
46
    @work = Work.new(created_by_user_id: current_user.id, collection_id: params_collection_id, user_entered_doi: params["doi"].present?)
25✔
47
    @work.resource = FormToResourceService.convert(params, @work)
25✔
48
    if @work.valid?
25✔
49
      @work.draft!(current_user)
23✔
50
      redirect_to work_url(@work), notice: "Work was successfully created."
23✔
51
    else
52
      render :new, status: :unprocessable_entity
2✔
53
    end
54
  end
55

56
  # Creates the new dataset
57
  def new_submission
1✔
58
    default_collection_id = current_user.default_collection.id
6✔
59
    work = Work.new(created_by_user_id: current_user.id, collection_id: default_collection_id)
6✔
60
    work.resource = FormToResourceService.convert(params, work)
6✔
61
    work.draft!(current_user)
6✔
62
    redirect_to edit_work_path(work, wizard: true)
6✔
63
  end
64

65
  ##
66
  # Show the information for the dataset with the given id
67
  # When requested as .json, return the internal json resource
68
  def show
1✔
69
    @work = Work.find(params[:id])
88✔
70
    @work.reload_snapshots
88✔
71
    @changes = WorkActivity.changes_for_work(@work.id)
88✔
72
    @messages = WorkActivity.messages_for_work(@work.id)
88✔
73

74
    respond_to do |format|
88✔
75
      format.html do
88✔
76
        # Ensure that the Work belongs to a Collection
77
        @collection = @work.collection
86✔
78
        raise(Work::InvalidCollectionError, "The Work #{@work.id} does not belong to any Collection") unless @collection
86✔
79

80
        @can_curate = current_user.can_admin?(@collection)
85✔
81
        @work.mark_new_notifications_as_read(current_user.id)
85✔
82
      end
83
      format.json { render json: @work.to_json }
90✔
84
    end
85
  end
86

87
  def file_list
1✔
88
    if params[:id] == "NONE"
127✔
89
      # This is a special case when we render the file list for a work being created
90
      # (i.e. it does not have an id just yet)
91
      render json: []
23✔
92
    else
93
      @work = Work.find(params[:id])
104✔
94
      render json: @work.uploads
104✔
95
    end
96
  end
97

98
  def resolve_doi
1✔
99
    @work = Work.find_by_doi(params[:doi])
3✔
100
    redirect_to @work
2✔
101
  end
102

103
  def resolve_ark
1✔
104
    @work = Work.find_by_ark(params[:ark])
3✔
105
    redirect_to @work
2✔
106
  end
107

108
  # GET /works/1/edit
109
  def edit
1✔
110
    @work = Work.find(params[:id])
43✔
111
    if current_user && @work.editable_by?(current_user)
43✔
112
      if @work.approved? && !@work.administered_by?(current_user)
40✔
113
        Honeybadger.notify("Can not edit work: #{@work.id} is approved but #{current_user} is not admin")
1✔
114
        redirect_to root_path, notice: I18n.t("works.uneditable.approved")
1✔
115
      else
116
        @uploads = @work.uploads
39✔
117
        @wizard_mode = wizard_mode?
39✔
118
        render "edit"
39✔
119
      end
120
    else
121
      Honeybadger.notify("Can not edit work: #{@work.id} is not editable by #{current_user}")
3✔
122
      redirect_to root_path, notice: I18n.t("works.uneditable.privs")
3✔
123
    end
124
  end
125

126
  def update
1✔
127
    @work = Work.find(params[:id])
47✔
128
    if current_user.blank? || !@work.editable_by?(current_user)
47✔
129
      Honeybadger.notify("Can not update work: #{@work.id} is not editable by #{current_user}")
1✔
130
      redirect_to root_path, notice: I18n.t("works.uneditable.privs")
1✔
131
    elsif !@work.editable_in_current_state?(current_user)
46✔
132
      Honeybadger.notify("Can not update work: #{@work.id} is not editable in current state by #{current_user}")
1✔
133
      redirect_to root_path, notice: I18n.t("works.uneditable.approved")
1✔
134
    else
135
      update_work
45✔
136
    end
137
  end
138

139
  # Prompt to select how to submit their files
140
  def attachment_select
1✔
141
    @work = Work.find(params[:id])
7✔
142
    @wizard_mode = true
7✔
143
  end
144

145
  # User selected a specific way to submit their files
146
  def attachment_selected
1✔
147
    @work = Work.find(params[:id])
8✔
148
    @wizard_mode = true
8✔
149
    @work.files_location = params["attachment_type"]
8✔
150
    @work.save!
8✔
151

152
    # create a directory for the work if the curator will need to move files by hand
153
    @work.s3_query_service.create_directory if @work.files_location != "file_upload"
8✔
154

155
    next_url = case @work.files_location
8✔
156
               when "file_upload"
157
                 work_file_upload_url(@work)
2✔
158
               when "file_cluster"
159
                 work_file_cluster_url(@work)
1✔
160
               else
161
                 work_file_other_url(@work)
5✔
162
               end
163
    redirect_to next_url
8✔
164
  end
165

166
  # Allow user to upload files directly
167
  def file_upload
1✔
168
    @work = Work.find(params[:id])
3✔
169
  end
170

171
  def file_uploaded
1✔
172
    @work = Work.find(params[:id])
4✔
173
    if pre_curation_uploads_param
4✔
174
      @work.pre_curation_uploads.attach(pre_curation_uploads_param)
×
175
      @work.save!
×
176
      @work.reload_snapshots
×
177
    end
178

179
    redirect_to(work_review_path)
4✔
180
  rescue StandardError => active_storage_error
181
    Rails.logger.error("Failed to attach the file uploads for the work #{@work.doi}: #{active_storage_error}")
×
182
    flash[:notice] = "Failed to attach the file uploads for the work #{@work.doi}: #{active_storage_error}. Please contact rdss@princeton.edu for assistance."
×
183

184
    redirect_to work_file_upload_path(@work)
×
185
  end
186

187
  # Allow user to indicate where their files are located in the PUL Research Cluster
188
  def file_cluster
1✔
189
    @work = Work.find(params[:id])
1✔
190
  end
191

192
  # Allow user to indicate where their files are located in the WWW
193
  def file_other
1✔
194
    @work = Work.find(params[:id])
5✔
195
  end
196

197
  def review
1✔
198
    @work = Work.find(params[:id])
6✔
199
    if request.method == "POST"
6✔
200
      @work.location_notes = params["location_notes"]
5✔
201
      @work.save!
5✔
202
    end
203
  end
204

205
  def validate
1✔
206
    @work = Work.find(params[:id])
28✔
207
    @work.submission_notes = params["submission_notes"]
28✔
208
    @uploads = @work.uploads
28✔
209
    @wizard_mode = true
28✔
210
    @work.complete_submission!(current_user)
28✔
211
    redirect_to user_url(current_user)
26✔
212
  end
213

214
  def approve
1✔
215
    @work = Work.find(params[:id])
10✔
216
    @work.approve!(current_user)
10✔
217
    redirect_to work_path(@work)
6✔
218
  end
219

220
  def withdraw
1✔
221
    @work = Work.find(params[:id])
2✔
222
    @work.withdraw!(current_user)
2✔
223
    redirect_to work_path(@work)
1✔
224
  end
225

226
  def resubmit
1✔
227
    @work = Work.find(params[:id])
2✔
228
    @work.resubmit!(current_user)
2✔
229
    redirect_to work_path(@work)
1✔
230
  end
231

232
  def assign_curator
1✔
233
    work = Work.find(params[:id])
4✔
234
    work.change_curator(params[:uid], current_user)
4✔
235
    if work.errors.count > 0
3✔
236
      render json: { errors: work.errors.map(&:type) }, status: :bad_request
1✔
237
    else
238
      render json: {}
2✔
239
    end
240
  rescue => ex
241
    Rails.logger.error("Error changing curator for work: #{work.id}. Exception: #{ex.message}")
1✔
242
    render json: { errors: ["Cannot save dataset"] }, status: :bad_request
1✔
243
  end
244

245
  def add_message
1✔
246
    work = Work.find(params[:id])
6✔
247
    if params["new-message"].present?
6✔
248
      new_message_param = params["new-message"]
6✔
249
      sanitized_new_message = html_escape(new_message_param)
6✔
250

251
      work.add_message(sanitized_new_message, current_user.id)
6✔
252
    end
253
    redirect_to work_path(id: params[:id])
6✔
254
  end
255

256
  def add_provenance_note
1✔
257
    work = Work.find(params[:id])
1✔
258
    if params["new-provenance-note"].present?
1✔
259
      new_date = params["new-provenance-date"]
1✔
260
      new_note = html_escape(params["new-provenance-note"])
1✔
261

262
      work.add_provenance_note(new_date, new_note, current_user.id)
1✔
263
    end
264
    redirect_to work_path(id: params[:id])
1✔
265
  end
266

267
  # Outputs the Datacite XML representation of the work
268
  def datacite
1✔
269
    work = Work.find(params[:id])
2✔
270
    render xml: work.to_xml
2✔
271
  end
272

273
  def datacite_validate
1✔
274
    @errors = []
1✔
275
    @work = Work.find(params[:id])
1✔
276
    datacite_xml = Nokogiri::XML(@work.to_xml)
1✔
277
    schema_location = Rails.root.join("config", "schema")
1✔
278
    Dir.chdir(schema_location) do
1✔
279
      xsd = Nokogiri::XML::Schema(File.read("datacite_4_4.xsd"))
1✔
280
      xsd.validate(datacite_xml).each do |error|
1✔
281
        @errors << error
1✔
282
      end
283
    end
284
  end
285

286
  def readme_select
1✔
287
    @work = Work.find(params[:id])
8✔
288
    readme = Readme.new(@work)
8✔
289
    @readme = readme.file_name
8✔
290
    @wizard = true
8✔
291
  end
292

293
  def readme_uploaded
1✔
294
    @work = Work.find(params[:id])
8✔
295
    @wizard = true
8✔
296
    readme = Readme.new(@work)
8✔
297
    readme_error = readme.attach(readme_file_param)
8✔
298
    if readme_error.nil?
8✔
299
      redirect_to work_attachment_select_url(@work)
7✔
300
    else
301
      flash[:notice] = readme_error
1✔
302
      redirect_to work_readme_select_url(@work)
1✔
303
    end
304
  end
305

306
  private
1✔
307

308
    def work_params
1✔
309
      params[:work] || params
90✔
310
    end
311

312
    def patch_params
1✔
313
      return {} unless params.key?(:patch)
24✔
314

315
      params[:patch]
16✔
316
    end
317

318
    def pre_curation_uploads_param
1✔
319
      return if patch_params.nil?
4✔
320

321
      patch_params[:pre_curation_uploads]
4✔
322
    end
323

324
    def readme_file_param
1✔
325
      return if patch_params.nil?
8✔
326

327
      patch_params[:readme_file]
8✔
328
    end
329

330
    def rescue_aasm_error
1✔
331
      yield
67✔
332
    rescue AASM::InvalidTransition => error
333
      message = error.message
8✔
334
      if @work.errors.count > 0
8✔
335
        message = @work.errors.to_a.join(", ")
5✔
336
      end
337
      Honeybadger.notify("Invalid #{@work.current_transition}: #{error.message} errors: #{message}")
8✔
338
      @errors = ["Cannot #{@work.current_transition}: #{message}"]
8✔
339
      render error_action, status: :unprocessable_entity
8✔
340
    end
341

342
    def error_action
1✔
343
      if action_name == "create"
8✔
344
        :new
×
345
      elsif action_name == "validate"
8✔
346
        :edit
2✔
347
      else
348
        :show
6✔
349
      end
350
    end
351

352
    def wizard_mode?
1✔
353
      params[:wizard] == "true"
114✔
354
    end
355

356
    def update_work
1✔
357
      @wizard_mode = wizard_mode?
45✔
358
      upload_service = WorkUploadsEditService.new(@work, current_user)
45✔
359
      if @work.approved?
45✔
360
        upload_keys = work_params[:deleted_uploads] || []
5✔
361
        deleted_uploads = upload_service.find_post_curation_uploads(upload_keys: upload_keys)
5✔
362

363
        return head(:forbidden) unless deleted_uploads.empty?
5✔
364
      else
365
        @work = upload_service.update_precurated_file_list(added_files_param, deleted_files_param)
40✔
366
      end
367

368
      process_updates
44✔
369
    end
370

371
    def update_params
1✔
372
      {
373
        collection_id: params_collection_id,
86✔
374
        resource: FormToResourceService.convert(params, @work)
375
      }
376
    end
377

378
    def added_files_param
1✔
379
      Array(work_params[:pre_curation_uploads_added])
40✔
380
    end
381

382
    def deleted_files_param
1✔
383
      deleted_count = (work_params["deleted_files_count"] || "0").to_i
40✔
384
      (1..deleted_count).map { |i| work_params["deleted_file_#{i}"] }.select(&:present?)
45✔
385
    end
386

387
    def process_updates
1✔
388
      resource_before = @work.resource
44✔
389
      if @work.update(update_params)
44✔
390

391
        resource_compare = ResourceCompareService.new(resource_before, update_params[:resource])
42✔
392
        @work.log_changes(resource_compare, current_user.id)
42✔
393

394
        if @wizard_mode
42✔
395
          redirect_to work_readme_select_url(@work)
7✔
396
        else
397
          redirect_to work_url(@work), notice: "Work was successfully updated."
35✔
398
        end
399
      else
400
        @uploads = @work.uploads
2✔
401
        render :edit, status: :unprocessable_entity
2✔
402
      end
403
    end
404

405
    def params_collection_id
1✔
406
      # Do not allow a nil for the collection id
407
      @params_collection_id ||= begin
111✔
408
        collection_id = params[:collection_id]
67✔
409
        if collection_id.blank?
67✔
410
          collection_id = current_user.default_collection.id
4✔
411
          Honeybadger.notify("We got a nil collection as part of the parameters #{params} #{request}")
4✔
412
        end
413
        collection_id
67✔
414
      end
415
    end
416
end
417
# rubocop:enable Metrics/ClassLength
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc