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

pulibrary / pdc_describe / 25744549-a0ce-4a94-be72-211b26360e47

04 Apr 2024 02:57PM UTC coverage: 95.316% (-0.5%) from 95.842%
25744549-a0ce-4a94-be72-211b26360e47

Pull #1739

circleci

JaymeeH
changing routes so that the next button on additional metadata goes to the readme
figuring out how to show the second pane by default for the additional metadata
Pull Request #1739: 1684 additional step

10 of 10 new or added lines in 1 file covered. (100.0%)

20 existing lines in 4 files now uncovered.

3215 of 3373 relevant lines covered (95.32%)

187.21 hits per line

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

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

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

6
# Currently this controller supports Multiple ways to create a work, wizard mode, create dataset, and migrate
7
# The goal is to eventually break some of these workflows into separate contorllers.
8
# For the moment I'm documenting which methods get called by each workflow below.
9
# Note: new, edit and update get called by both the migrate and Non wizard workflows
10
#
11
# Normal mode
12
#  new & file_list -> create -> show & file_list
13
#
14
#  Clicking Edit puts you in wizard mode for some reason :(
15
#
16
# migrate
17
#
18
#  new & file_list -> create -> show & file_list
19
#
20
#  Clicking edit
21
#   edit & file_list -> update -> show & file_list
22
#
23

24
# rubocop:disable Metrics/ClassLength
25
class WorksController < ApplicationController
1✔
26
  include ERB::Util
1✔
27
  around_action :rescue_aasm_error, only: [:approve, :withdraw, :resubmit, :validate, :create]
1✔
28

29
  skip_before_action :authenticate_user!
1✔
30
  before_action :authenticate_user!, unless: :public_request?
1✔
31

32
  def index
1✔
33
    @works = Work.all
4✔
34
    respond_to do |format|
4✔
35
      format.html
4✔
36
      format.rss { render layout: false }
6✔
37
    end
38
  end
39

40
  # only non wizard mode
41
  def new
1✔
42
    group = Group.find_by(code: params[:group_code]) || current_user.default_group
76✔
43
    @work = Work.new(created_by_user_id: current_user.id, group:)
76✔
44
    @work_decorator = WorkDecorator.new(@work, current_user)
76✔
45
    @form_resource_decorator = FormResourceDecorator.new(@work, current_user)
76✔
46
  end
47

48
  # only non wizard mode
49
  def create
1✔
50
    @work = Work.new(created_by_user_id: current_user.id, group_id: params_group_id, user_entered_doi: params["doi"].present?)
4✔
51
    @work.resource = FormToResourceService.convert(params, @work)
4✔
52
    @work.resource.migrated = migrated?
4✔
53
    if @work.valid?
4✔
54
      @work.draft!(current_user)
4✔
55
      upload_service = WorkUploadsEditService.new(@work, current_user)
4✔
56
      upload_service.update_precurated_file_list(added_files_param, deleted_files_param)
4✔
57
      redirect_to work_url(@work), notice: "Work was successfully created."
4✔
58
    else
UNCOV
59
      @work_decorator = WorkDecorator.new(@work, current_user)
×
UNCOV
60
      @form_resource_decorator = FormResourceDecorator.new(@work, current_user)
×
UNCOV
61
      render :new, status: :unprocessable_entity
×
62
    end
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])
82✔
70
    UpdateSnapshotJob.perform_later(work_id: @work.id, last_snapshot_id: work.upload_snapshots.first&.id)
82✔
71
    @work_decorator = WorkDecorator.new(@work, current_user)
82✔
72

73
    respond_to do |format|
82✔
74
      format.html do
82✔
75
        # Ensure that the Work belongs to a Group
76
        group = @work_decorator.group
79✔
77
        raise(Work::InvalidGroupError, "The Work #{@work.id} does not belong to any Group") unless group
79✔
78

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

86
  # only non wizard mode
87
  def file_list
1✔
88
    if params[:id] == "NONE"
207✔
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: []
75✔
92
    else
93
      @work = Work.find(params[:id])
132✔
94
      render json: @work.file_list
132✔
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
  # only non wizard mode
110
  def edit
1✔
111
    @new_uploader = params[:new_uploader] == "true" || Rails.env.staging?
73✔
112

113
    @work = Work.find(params[:id])
73✔
114
    @work_decorator = WorkDecorator.new(@work, current_user)
73✔
115
    if validate_modification_permissions(work: @work,
73✔
116
                                         uneditable_message: "Can not update work: #{@work.id} is not editable by #{current_user.uid}",
117
                                         current_state_message: "Can not update work: #{@work.id} is not editable in current state by #{current_user.uid}")
118
      @uploads = @work.uploads
69✔
119
      @form_resource_decorator = FormResourceDecorator.new(@work, current_user)
69✔
120
    end
121
  end
122

123
  # PATCH /works/1
124
  # only non wizard mode
125
  def update
1✔
126
    @work = Work.find(params[:id])
41✔
127
    if validate_modification_permissions(work: @work, uneditable_message: "Can not update work: #{@work.id} is not editable by #{current_user.uid}",
41✔
128
                                         current_state_message: "Can not update work: #{@work.id} is not editable in current state by #{current_user.uid}")
129
      update_work
39✔
130
    end
131
  end
132

133
  def approve
1✔
134
    @work = Work.find(params[:id])
14✔
135
    @work.approve!(current_user)
14✔
136
    flash[:notice] = "Your files are being moved to the post-curation bucket in the background. Depending on the file sizes this may take some time."
6✔
137
    redirect_to work_path(@work)
6✔
138
  end
139

140
  def withdraw
1✔
141
    @work = Work.find(params[:id])
2✔
142
    @work.withdraw!(current_user)
2✔
143
    redirect_to work_path(@work)
1✔
144
  end
145

146
  def resubmit
1✔
147
    @work = Work.find(params[:id])
2✔
148
    @work.resubmit!(current_user)
2✔
149
    redirect_to work_path(@work)
1✔
150
  end
151

152
  def assign_curator
1✔
153
    work = Work.find(params[:id])
5✔
154
    work.change_curator(params[:uid], current_user)
5✔
155
    if work.errors.count > 0
4✔
156
      render json: { errors: work.errors.map(&:type) }, status: :bad_request
1✔
157
    else
158
      render json: {}
3✔
159
    end
160
  rescue => ex
161
    Rails.logger.error("Error changing curator for work: #{work.id}. Exception: #{ex.message}")
1✔
162
    render json: { errors: ["Cannot save dataset"] }, status: :bad_request
1✔
163
  end
164

165
  def add_message
1✔
166
    work = Work.find(params[:id])
6✔
167
    if params["new-message"].present?
6✔
168
      new_message_param = params["new-message"]
6✔
169
      sanitized_new_message = html_escape(new_message_param)
6✔
170

171
      work.add_message(sanitized_new_message, current_user.id)
6✔
172
    end
173
    redirect_to work_path(id: params[:id])
6✔
174
  end
175

176
  def add_provenance_note
1✔
177
    work = Work.find(params[:id])
2✔
178
    if params["new-provenance-note"].present?
2✔
179
      new_date = params["new-provenance-date"]
2✔
180
      new_label = params["change_label"]
2✔
181
      new_note = html_escape(params["new-provenance-note"])
2✔
182

183
      work.add_provenance_note(new_date, new_note, current_user.id, new_label)
2✔
184
    end
185
    redirect_to work_path(id: params[:id])
2✔
186
  end
187

188
  # Outputs the Datacite XML representation of the work
189
  def datacite
1✔
190
    work = Work.find(params[:id])
2✔
191
    render xml: work.to_xml
2✔
192
  end
193

194
  def datacite_validate
1✔
195
    @errors = []
3✔
196
    @work = Work.find(params[:id])
3✔
197
    validator = WorkValidator.new(@work)
3✔
198
    unless validator.valid_datacite?
3✔
199
      @errors = @work.errors.full_messages
2✔
200
    end
201
  end
202

203
  def migrating?
1✔
204
    return @work.resource.migrated if @work&.resource && !params.key?(:migrate)
150✔
205

206
    params[:migrate]
126✔
207
  end
208
  helper_method :migrating?
1✔
209

210
  # Returns the raw BibTex citation information
211
  def bibtex
1✔
212
    work = Work.find(params[:id])
1✔
213
    creators = work.resource.creators.map { |creator| "#{creator.family_name}, #{creator.given_name}" }
15✔
214
    citation = DatasetCitation.new(creators, [work.resource.publication_year], work.resource.titles.first.title, work.resource.resource_type, work.resource.publisher, work.resource.doi)
1✔
215
    bibtex = citation.bibtex
1✔
216
    send_data bibtex, filename: "#{citation.bibtex_id}.bibtex", type: "text/plain", disposition: "attachment"
1✔
217
  end
218

219
  def upload_files
1✔
220
    @work = Work.find(params[:id])
×
221
    upload_service = WorkUploadsEditService.new(@work, current_user)
×
222
    upload_service.update_precurated_file_list(params["files"], [])
×
223
  end
224

225
  private
1✔
226

227
    # Extract the Work ID parameter
228
    # @return [String]
229
    def work_id_param
1✔
230
      params[:id]
86✔
231
    end
232

233
    # Find the Work requested by ID
234
    # @return [Work]
235
    def work
1✔
236
      Work.find(work_id_param)
86✔
237
    end
238

239
    # Determine whether or not the request is for the :index action in the RSS
240
    # response format
241
    # This is to enable PDC Discovery to index approved content via the RSS feed
242
    def rss_index_request?
1✔
243
      action_name == "index" && request.format.symbol == :rss
536✔
244
    end
245

246
    # Determine whether or not the request is for the :show action in the JSON
247
    # response format
248
    # @return [Boolean]
249
    def json_show_request?
1✔
250
      action_name == "show" && request.format.symbol == :json
534✔
251
    end
252

253
    # Determine whether or not the requested Work has been approved
254
    # @return [Boolean]
255
    def work_approved?
1✔
256
      work&.state == "approved"
4✔
257
    end
258

259
    ##
260
    # Public requests are requests that do not require authentication.
261
    # This is to enable PDC Discovery to index approved content via the RSS feed
262
    # and .json calls to individual works without needing to log in as a user.
263
    # Note that only approved works can be fetched for indexing.
264
    def public_request?
1✔
265
      return true if rss_index_request?
536✔
266
      return true if json_show_request? && work_approved?
534✔
267
      false
532✔
268
    end
269

270
    def work_params
1✔
271
      params[:work] || {}
90✔
272
    end
273

274
    def patch_params
1✔
275
      return {} unless params.key?(:patch)
×
276

277
      params[:patch]
×
278
    end
279

280
    def pre_curation_uploads_param
1✔
281
      return if patch_params.nil?
×
282

283
      patch_params[:pre_curation_uploads]
×
284
    end
285

286
    def readme_file_param
1✔
287
      return if patch_params.nil?
×
288

289
      patch_params[:readme_file]
×
290
    end
291

292
    def rescue_aasm_error
1✔
293
      super
22✔
294
    rescue StandardError => generic_error
UNCOV
295
      if action_name == "create"
×
UNCOV
296
        handle_error_for_create(generic_error)
×
297
      else
298
        redirect_to root_url, notice: "We apologize, an error was encountered: #{generic_error.message}. Please contact the PDC Describe administrators."
×
299
      end
300
    end
301

302
    def handle_error_for_create(generic_error)
1✔
UNCOV
303
      if @work.persisted?
×
304
        Honeybadger.notify("Failed to create the new Dataset #{@work.id}: #{generic_error.message}")
×
305
        @form_resource_decorator = FormResourceDecorator.new(@work, current_user)
×
306
        redirect_to edit_work_url(id: @work.id), notice: "Failed to create the new Dataset #{@work.id}: #{generic_error.message}", params:
×
307
      else
UNCOV
308
        Honeybadger.notify("Failed to create a new Dataset #{@work.id}: #{generic_error.message}")
×
UNCOV
309
        new_params = {}
×
UNCOV
310
        new_params[:wizard] = wizard_mode? if wizard_mode?
×
UNCOV
311
        new_params[:migrate] = migrating? if migrating?
×
UNCOV
312
        @form_resource_decorator = FormResourceDecorator.new(@work, current_user)
×
UNCOV
313
        redirect_to new_work_url(params: new_params), notice: "Failed to create a new Dataset: #{generic_error.message}", params: new_params
×
314
      end
315
    end
316

317
    def redirect_aasm_error(transition_error_message)
1✔
318
      if @work.persisted?
10✔
319
        redirect_to edit_work_url(id: @work.id), notice: transition_error_message, params:
10✔
320
      else
321
        new_params = {}
×
322
        new_params[:wizard] = wizard_mode? if wizard_mode?
×
323
        new_params[:migrate] = migrating? if migrating?
×
324
        @form_resource_decorator = FormResourceDecorator.new(@work, current_user)
×
325
        redirect_to new_work_url(params: new_params), notice: transition_error_message, params: new_params
×
326
      end
327
    end
328

329
    def error_action
1✔
330
      @form_resource_decorator = FormResourceDecorator.new(@work, current_user)
×
331
      if action_name == "create"
×
332
        :new
×
333
      elsif action_name == "validate"
×
334
        :edit
×
335
      elsif action_name == "new_submission"
×
336
        :new_submission
×
337
      else
338
        @work_decorator = WorkDecorator.new(@work, current_user)
×
339
        :show
×
340
      end
341
    end
342

343
    def wizard_mode?
1✔
UNCOV
344
      params[:wizard] == "true"
×
345
    end
346

347
    def update_work
1✔
348
      upload_service = WorkUploadsEditService.new(@work, current_user)
39✔
349
      if @work.approved?
39✔
350
        upload_keys = deleted_files_param || []
5✔
351
        deleted_uploads = upload_service.find_post_curation_uploads(upload_keys:)
5✔
352

353
        return head(:forbidden) unless deleted_uploads.empty?
5✔
354
      else
355
        @work = upload_service.update_precurated_file_list(added_files_param, deleted_files_param)
34✔
356
      end
357

358
      process_updates
38✔
359
    end
360

361
    def added_files_param
1✔
362
      Array(work_params[:pre_curation_uploads_added])
38✔
363
    end
364

365
    def deleted_files_param
1✔
366
      deleted_count = (work_params["deleted_files_count"] || "0").to_i
43✔
367
      (1..deleted_count).map { |i| work_params["deleted_file_#{i}"] }.select(&:present?)
52✔
368
    end
369

370
    def process_updates
1✔
371
      if WorkCompareService.update_work(work: @work, update_params:, current_user:)
38✔
372
        redirect_to work_url(@work), notice: "Work was successfully updated."
36✔
373
      else
374
        # This is needed for rendering HTML views with validation errors
375
        @uploads = @work.uploads
2✔
376
        @form_resource_decorator = FormResourceDecorator.new(@work, current_user)
2✔
377

378
        render :edit, status: :unprocessable_entity
2✔
379
      end
380
    end
381

382
    def migrated?
1✔
383
      return false unless params.key?(:submit)
4✔
384

UNCOV
385
      params[:submit] == "Migrate"
×
386
    end
387
end
388
# 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