Class: Project

Inherits:
ApplicationRecord show all
Defined in:
app/models/project.rb

Constant Summary collapse

APPROVED_STATUS =

Valid project status described in ADR 7 See ‘architecture-decisions/0007-valid-project-statuses.md`

"approved"
ACTIVE_STATUS =
"active"

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.all_projects(user, aql_query = "xpath(tigerdata:project/ProjectID) has value") ⇒ Object

Returns the projects that the current user has access in Mediaflux given their credentials



110
111
112
113
114
115
116
117
118
119
120
# File 'app/models/project.rb', line 110

def self.all_projects(user, aql_query = "xpath(tigerdata:project/ProjectID) has value")
  request = Mediaflux::ProjectListRequest.new(session_token: user.mediaflux_session, aql_query:)
  request.resolve
  if request.error?
    Rails.logger.error("Error fetching project list for user #{user&.uid}: #{request.response_error[:message]}")
    Honeybadger.notify("Error fetching project list for user #{user&.uid}: #{request.response_error[:message]}")
    []
  else
    request.results
  end
end

.default_storage_capacityObject



163
164
165
# File 'app/models/project.rb', line 163

def self.default_storage_capacity
  "0 GB"
end

.default_storage_unitObject



145
146
147
# File 'app/models/project.rb', line 145

def self.default_storage_unit
  "KB"
end

.default_storage_usageObject



149
150
151
# File 'app/models/project.rb', line 149

def self.default_storage_usage
  "0 #{default_storage_unit}"
end

.users_projects(user) ⇒ Object

This method narrows the list down returned by ‘all_projects` to only those projects where the user has been given a role (e.g. sponsor, manager, or data user.) For most users `all_projects` and `user_projects` are identical, but for administrators the lists can be very different since they are not part of most projects even though they have access to them in Mediaflux.



103
104
105
106
107
# File 'app/models/project.rb', line 103

def self.users_projects(user)
  all_projects(user).select do |project|
    project[:data_manager] == user.uid || project[:data_sponsor] == user.uid || project[:data_users].include?(user.uid)
  end
end

Instance Method Details

#activate(current_user:) ⇒ Object

Raises:

  • (StandardError)


35
36
37
38
39
40
41
42
43
44
45
46
# File 'app/models/project.rb', line 35

def activate(current_user:)
  raise StandardError.new("Only approved projects can be activated") if self.status != Project::APPROVED_STATUS
   = Mediaflux::AssetMetadataRequest.new(session_token: current_user.mediaflux_session, id: self.mediaflux_id)
  .resolve
  raise .response_error if .error?
  if self.title == .[:title]
    self..status = Project::ACTIVE_STATUS
    self.save!
  else
    raise StandardError.new("Title mismatch: #{self.title} != #{.[:title]}")
  end
end

#asset_count(session_id:) ⇒ Object



140
141
142
143
# File 'app/models/project.rb', line 140

def asset_count(session_id:)
  values = (session_id:)
  values.fetch(:total_file_count, 0)
end

#create!(initial_metadata:, user:) ⇒ Object



18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# File 'app/models/project.rb', line 18

def create!(initial_metadata:, user:)
  self. = 
  if self.valid?
    if .project_id == ProjectMetadata::DOI_NOT_MINTED
      self.draft_doi(user: user)
      self.save!
      ProvenanceEvent.generate_submission_events(project: self, user: user)
    else
      self.save!
    end
    # return doi
    self..project_id
  else
    nil
  end
end

#created_by_userObject



122
123
124
# File 'app/models/project.rb', line 122

def created_by_user
  User.find_by(uid: .created_by)
end

#departmentsObject



78
79
80
81
# File 'app/models/project.rb', line 78

def departments
  unsorted = .departments || []
  unsorted.sort
end

#draft_doi(user: nil) ⇒ Object



48
49
50
51
# File 'app/models/project.rb', line 48

def draft_doi(user: nil)
  puldatacite = PULDatacite.new
  self..project_id = puldatacite.draft_doi
end

#file_list(session_id:, size: 10) ⇒ Object

Fetches the first n files



184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
# File 'app/models/project.rb', line 184

def file_list(session_id:, size: 10)
  return { files: [] } if mediaflux_id.nil?

  query_req = Mediaflux::QueryRequest.new(session_token: session_id, collection: mediaflux_id, deep_search: true, aql_query: "type!='application/arc-asset-collection'")
  iterator_id = query_req.result

  iterator_req = Mediaflux::IteratorRequest.new(session_token: session_id, iterator: iterator_id, size: size)
  results = iterator_req.result

  # Destroy _after_ fetching the first set of results from iterator_req.
  # This call is required since it possible that we have read less assets than
  # what the collection has but we are done with the iterator.
  Mediaflux::IteratorDestroyRequest.new(session_token: session_id, iterator: iterator_id).resolve

  results
end

#file_list_to_file(session_id:, filename:) ⇒ Object

Fetches the entire file list to a file



202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'app/models/project.rb', line 202

def file_list_to_file(session_id:, filename:)
  return { files: [] } if mediaflux_id.nil?

  query_req = Mediaflux::QueryRequest.new(session_token: session_id, collection: mediaflux_id, deep_search: true,  aql_query: "type!='application/arc-asset-collection'")
  iterator_id = query_req.result

  start_time = Time.zone.now
  prefix = "file_list_to_file #{session_id[0..7]} #{self..project_id}"
  log_elapsed(start_time, prefix, "STARTED")

  File.open(filename, "w") do |file|
    page_number = 0
    # file header
    file.write("ID, PATH, NAME, COLLECTION?, LAST_MODIFIED, SIZE\r\n")
    loop do
      iterator_start_time = Time.zone.now
      page_number += 1
      iterator_req = Mediaflux::IteratorRequest.new(session_token: session_id, iterator: iterator_id, size: 1000)
      iterator_resp = iterator_req.result
      log_elapsed(iterator_start_time, prefix, "FETCHED page #{page_number} from iterator")
      lines = files_from_iterator(iterator_resp)
      file.write(lines.join("\r\n") + "\r\n")
      break if iterator_resp[:complete] || iterator_req.error?
    end
    log_elapsed(start_time, prefix, "ENDED")
  end

  # Destroy _after_ fetching the results from iterator_req
  # This call is technically not necessary since Mediaflux automatically deletes the iterator
  # once we have ran through it and by now we have. But it does not hurt either.
  Mediaflux::IteratorDestroyRequest.new(session_token: session_id, iterator: iterator_id).resolve
end

#in_mediaflux?Boolean

Returns:

  • (Boolean)


95
96
97
# File 'app/models/project.rb', line 95

def in_mediaflux?
  mediaflux_id.present?
end

#mediaflux_meta_xml(user:) ⇒ String

Returns XML representation of the <meta> element.

Returns:

  • (String)

    XML representation of the <meta> element



127
128
129
130
# File 'app/models/project.rb', line 127

def mediaflux_meta_xml(user:)
  doc = ProjectMediaflux.document(project: self, user: user)
  doc.xpath("/response/reply/result/asset/meta").to_s
end

#mediaflux_metadata(session_id:) ⇒ Object



132
133
134
135
136
137
138
# File 'app/models/project.rb', line 132

def (session_id:)
  @mediaflux_metadata ||= begin
     = Mediaflux::AssetMetadataRequest.new(session_token: session_id, id: mediaflux_id)
    .
  end
  @mediaflux_metadata
end

#metadataObject

Ideally this method should return a ProjectMetadata object (like ‘metadata_model` does) but we’ll keep them both while we are refactoring the code so that we don’t break everything at once since ‘metadata` is used everywhere.



56
57
58
# File 'app/models/project.rb', line 56

def 
  @metadata_hash = ( || {}).with_indifferent_access
end

#metadata=(metadata_model) ⇒ Object



68
69
70
71
72
# File 'app/models/project.rb', line 68

def metadata=()
  # Convert our metadata to a hash so it can be saved on our JSONB field
   = JSON.parse(.to_json)
  self. = 
end

#metadata_modelObject



60
61
62
# File 'app/models/project.rb', line 60

def 
  @metadata_model ||= ProjectMetadata.new_from_hash(self.)
end

#metadata_model=(new_metadata_model) ⇒ Object



64
65
66
# File 'app/models/project.rb', line 64

def ()
  @metadata_model = 
end

#project_directoryObject



83
84
85
# File 'app/models/project.rb', line 83

def project_directory
  .project_directory || ""
end

#project_directory_shortObject



87
88
89
# File 'app/models/project.rb', line 87

def project_directory_short
  project_directory
end

#statusObject



91
92
93
# File 'app/models/project.rb', line 91

def status
  .status
end

#storage_capacity(session_id:) ⇒ Object



167
168
169
170
171
172
173
174
175
# File 'app/models/project.rb', line 167

def storage_capacity(session_id:)
  values = (session_id:)
  quota_value = values.fetch(:quota_allocation, '') #if quota does not exist, set value to an empty string
  if quota_value.blank?
    return self.class.default_storage_capacity
  else
    return quota_value
  end
end

#storage_capacity_raw(session_id:) ⇒ Object



177
178
179
180
181
# File 'app/models/project.rb', line 177

def storage_capacity_raw(session_id:)
  values = (session_id:)
  quota_value = values.fetch(:quota_allocation_raw, 0) #if quota does not exist, set value to 0
  quota_value
end

#storage_usage(session_id:) ⇒ Object



153
154
155
156
# File 'app/models/project.rb', line 153

def storage_usage(session_id:)
  values = (session_id:)
  values.fetch(:quota_used, self.class.default_storage_usage) # if the storage is empty use the default
end

#storage_usage_raw(session_id:) ⇒ Object



158
159
160
161
# File 'app/models/project.rb', line 158

def storage_usage_raw(session_id:)
  values = (session_id:)
  values.fetch(:quota_used_raw, 0) # if the storage raw is empty use zero
end

#titleObject



74
75
76
# File 'app/models/project.rb', line 74

def title
  self..title
end