Skip to content

Commit 5ae692e

Browse files
authored
Organize the submission provenance for a project (#2250)
ref #2187
1 parent 37c3aca commit 5ae692e

File tree

9 files changed

+173
-38
lines changed

9 files changed

+173
-38
lines changed

app/assets/stylesheets/_project.scss

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -297,7 +297,18 @@ copy-project-path-label-normal {
297297
font-size: 1rem;
298298
font-style: normal;
299299
font-weight: 600;
300-
line-height: 1.5rem;
300+
margin-bottom: 1.5rem;
301+
margin-right: 1.25rem;
302+
line-height: 1.5rem; /* 150% */
303+
width: 11.5rem;
304+
}
305+
.provenance-info {
306+
font-weight: 200;
307+
}
308+
.provenance-value {
309+
font-size: 1rem;
310+
font-weight: 200;
311+
line-height: 1.5rem; /* 150% */
301312
}
302313

303314
.departments-group {
@@ -322,6 +333,12 @@ copy-project-path-label-normal {
322333
margin-left: 1em;
323334
}
324335

336+
.lux-badge.lux-badge-grey {
337+
background: #eee;
338+
color: black;
339+
margin: 0.15rem 0.25rem 0.25rem 0;
340+
}
341+
325342
.data-users-section {
326343
/* frame-218 in Figma */
327344
display: inline-flex;

app/operations/project_create.rb

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,12 @@ def persist_in_mediaflux(project, current_user)
3939
Failure debug_output
4040
else
4141
ProvenanceEvent.generate_approval_events(project: project, user: current_user, debug_output: mediaflux_request.debug_output.to_s)
42+
# Save the submission provenance
43+
# TODO: Should we update the metadata_model or just the metadata hash directly?
44+
project.metadata_model.submission["approved_by"] = current_user.uid
45+
project.metadata_model.submission["approved_on"] = project.provenance_events.where(event_type: "Approved").first.created_at
46+
project.save!
47+
4248
Success(mediaflux_id)
4349
end
4450
# TODO: What kind of error are we expecting here? This will capture the session errors, but maybe we should not be doing this.

app/presenters/project_show_presenter.rb

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,46 @@ def departments
111111
@project_mf[:departments] || []
112112
end
113113

114+
def submission_provenance
115+
@project.metadata_json["submission"]
116+
end
117+
118+
def requested_by
119+
user_name_id = {}
120+
uid = submission_provenance["requested_by"]
121+
return "N/A" if uid.nil?
122+
123+
user = User.find_by(uid: uid)
124+
user_name_id["#{user.given_name} #{user.family_name}"] = uid
125+
user_name_id
126+
end
127+
128+
def requested_on
129+
date_time = {}
130+
date = @project.metadata_json["submission"]["request_date_time"].to_datetime.strftime("%B %d, %Y")
131+
time = @project.metadata_json["submission"]["request_date_time"].to_datetime.strftime("%I:%M %p")
132+
date_time["#{date}"] = time
133+
date_time
134+
end
135+
136+
def approved_by
137+
user_name_id = {}
138+
uid = submission_provenance["approved_by"]
139+
return "N/A" if uid.nil?
140+
141+
user = User.find_by(uid: uid)
142+
user_name_id["#{user.given_name} #{user.family_name}"] = uid
143+
user_name_id
144+
end
145+
146+
def approved_on
147+
date_time = {}
148+
date = @project.metadata_json["submission"]["approved_on"].to_datetime.strftime("%B %d, %Y")
149+
time = @project.metadata_json["submission"]["approved_on"].to_datetime.strftime("%I:%M %p")
150+
date_time["#{date}"] = time
151+
date_time
152+
end
153+
114154
def department_codes
115155
@dep_with_codes = {}
116156
departments_list = departments.nil? ? [] : departments.first.split(", ")

app/services/request_project_metadata.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ def convert(request)
1717
project_directory: project_directory(request),
1818
storage_capacity: storage_capacity(request),
1919
storage_performance_expectations: { requested: "Standard", approved: "Standard" },
20-
created_by: nil,
21-
created_on: request[:created_at],
20+
created_by: request[:requested_by],
21+
created_on: request[:created_at], # This is redundant and counterintuitive. Insinuates that the project was created when the request was created.
2222
project_id: ProjectMetadata::DOI_NOT_MINTED,
2323
number_of_files: request[:number_of_files],
2424
hpc: request[:hpc]&.downcase == "yes",

app/views/projects/details.html.erb

Lines changed: 52 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@
6666
<% end %>
6767
</p>
6868
</div>
69+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1" viewBox="0 0 1200 1" fill="none">
70+
<path d="M6.36558e-08 0.5L1200 0.500105" stroke="#EEEEEE"/>
71+
</svg>
6972
</ul>
7073
<ul class="details basic-details-fields">
7174
<div class="detail-heading">
@@ -92,6 +95,9 @@
9295
<% end %>
9396
</p>
9497
</div>
98+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1" viewBox="0 0 1200 1" fill="none">
99+
<path d="M6.36558e-08 0.5L1200 0.500105" stroke="#EEEEEE"/>
100+
</svg>
95101
</ul>
96102
<ul class="details basic-details-fields">
97103
<div class="detail-heading">
@@ -135,38 +141,55 @@
135141
</div>
136142
</div>
137143
<ul class="sub-details basic-details-fields">
138-
<% if @project.in_mediaflux? && (current_user.developer? || current_user.eligible_sysadmin?) %>
139-
<div class="detail-pair">
140-
<li class="detail-subheading">Mediaflux ID</li>
141-
<p class="detail-value"><%= @project.mediaflux_id %></p>
142-
</div>
143-
<% elsif [email protected]_mediaflux? %>
144-
<li class="detail-subheading">Mediaflux ID</li>
145-
<p class="detail-value">This project has not been saved to Mediaflux</p>
144+
<% if @project.in_mediaflux? && (current_user.developer? || current_user.eligible_sysadmin?) %>
145+
<div class="detail-pair">
146+
<li class="detail-subheading mediaflux-id">Mediaflux ID</li>
147+
<p class="detail-value"><%= @project.mediaflux_id %></p>
148+
</div>
149+
<% elsif [email protected]_mediaflux? %>
150+
<li class="detail-subheading mediaflux-id">Mediaflux ID</li>
151+
<p class="detail-value">This project has not been saved to Mediaflux</p>
152+
<% end %>
153+
146154
</ul>
147-
</div>
148-
<% end %>
149-
</ul>
150-
<ul class="details basic-details-fields">
151-
<div class="detail-heading">
152-
<div class="heading-text" id="provenance-section">Provenance</div>
153-
</div>
154-
<ul class="sub-details basic-details-fields">
155-
<div class="detail-sub-heading">
156-
<div class="heading-text">Submission</div>
155+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1" viewBox="0 0 1200 1" fill="none">
156+
<path d="M6.36558e-08 0.5L1200 0.500105" stroke="#EEEEEE"/>
157+
</svg>
158+
159+
<ul class="details basic-details-fields">
160+
<div class="detail-heading">
161+
<div class="heading-text" id="provenance-section">Provenance</div>
157162
</div>
158-
<% @provenance_events.each do |event| %>
159-
<li class="provenance">
160-
<%= event.event_details %>, <%=event.created_at.to_time.in_time_zone("America/New_York").iso8601%>
161-
<% if current_user.eligible_sysadmin? && event.event_type = ProvenanceEvent::DEBUG_OUTPUT_TYPE && event.event_note %>
162-
<blockquote>
163-
<%= event.event_note %>
164-
</blockquote>
165-
<% end %>
166-
<% end %>
163+
<ul class="sub-details basic-details-fields">
164+
<div class="detail-sub-heading">Submission</div>
165+
<div class="detail-pair">
166+
<div class="detail-sub-heading provenance-info"> Requested by</div>
167+
<div class="provenance-value"> <%= @presenter.requested_by.keys.first %> </div>
168+
<lux-badge class="lux-badge lux-badge-grey">
169+
<div class="badge-text"><%= @presenter.requested_by.values.first %></div>
170+
</lux-badge>
171+
</div>
172+
<div class="detail-pair">
173+
<div class="detail-sub-heading provenance-info"> Requested Date–Time </div>
174+
<div class="provenance-value"> <%= @presenter.requested_on.keys.first %> <%= @presenter.requested_on.values.first %> </div>
175+
</div>
176+
<div class="detail-pair">
177+
<div class="detail-sub-heading provenance-info">Approved by</div>
178+
<div class="provenance-value"> <%= @presenter.approved_by.keys.first %> </div>
179+
<lux-badge class="lux-badge lux-badge-grey">
180+
<div class="badge-text"><%= @presenter.approved_by.values.first %></div>
181+
</lux-badge>
182+
</div>
183+
<div class="detail-pair">
184+
<div class="detail-sub-heading provenance-info"> Approval Date–Time</div>
185+
<div class="provenance-value"> <%= @presenter.approved_on.keys.first %> <%= @presenter.approved_on.values.first %></div>
186+
</div>
187+
</ul>
188+
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="1" viewBox="0 0 1200 1" fill="none">
189+
<path d="M6.36558e-08 0.5L1200 0.500105" stroke="#EEEEEE"/>
190+
</svg>
167191
</ul>
168-
</ul>
169-
</div>
192+
</div>
170193
</div>
171194
172195
<script type="module">

spec/factories/request.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,5 +24,6 @@
2424
departments { [{ name: "RDSS" }, { name: "RC" }] }
2525
description { "a random description" }
2626
project_folder { random_project_directory }
27+
requested_by { "tigerdatatester" }
2728
end
2829
end

spec/models/project_show_presenter_spec.rb

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,4 +95,37 @@
9595
expect(presenter.department_codes.keys).to eq(project.metadata_model.departments)
9696
end
9797
end
98+
99+
describe "#submission_provenance" do
100+
it "returns the hash containing the project submission and approval provenance" do
101+
expect(presenter.submission_provenance).to be_a(Hash)
102+
expect(presenter.submission_provenance).to eq(project.metadata_json["submission"])
103+
end
104+
end
105+
106+
describe "#requested_by" do
107+
it "generates the string-serialized XML for the Project XML Document" do
108+
expect(presenter.requested_by).to be_a(Hash)
109+
expect(presenter.requested_by.values.first).to eq(project.metadata_json["submission"]["requested_by"])
110+
end
111+
end
112+
113+
describe "#requested_on" do
114+
it "generates the string-serialized XML for the Project XML Document" do
115+
expect(presenter.requested_on).to be_a(Hash)
116+
end
117+
end
118+
119+
describe "#approved_by" do
120+
it "generates the string-serialized XML for the Project XML Document" do
121+
expect(presenter.approved_by).to be_a(Hash)
122+
expect(presenter.approved_by.values.first).to eq(project.metadata_json["submission"]["approved_by"])
123+
end
124+
end
125+
126+
describe "#approved_on" do
127+
it "generates the string-serialized XML for the Project XML Document" do
128+
expect(presenter.approved_on).to be_a(Hash)
129+
end
130+
end
98131
end

spec/presenters/dashboard_presenter_spec.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,10 @@
77
let(:current_user) { FactoryBot.create(:sponsor_and_data_manager, uid: "tigerdatatester", mediaflux_session: SystemUser.mediaflux_session) }
88
let(:other_user) { FactoryBot.create :user, uid: "kl37" }
99

10-
let(:request1) { FactoryBot.create :request_project, data_manager: current_user.uid, data_sponsor: other_user.uid }
11-
let(:request2) { FactoryBot.create :request_project, data_manager: other_user.uid, data_sponsor: current_user.uid }
12-
let(:request3) { FactoryBot.create :request_project, data_manager: other_user.uid, data_sponsor: other_user.uid, user_roles: [{"uid" => current_user.uid, "read_only" => true}] }
13-
let(:request4) { FactoryBot.create :request_project, data_manager: other_user.uid, data_sponsor: other_user.uid }
10+
let(:request1) { FactoryBot.create :request_project, data_manager: current_user.uid, data_sponsor: other_user.uid, requested_by: other_user.uid }
11+
let(:request2) { FactoryBot.create :request_project, data_manager: other_user.uid, data_sponsor: current_user.uid, requested_by: other_user.uid }
12+
let(:request3) { FactoryBot.create :request_project, data_manager: other_user.uid, data_sponsor: other_user.uid, user_roles: [{"uid" => current_user.uid, "read_only" => true}], requested_by: other_user.uid }
13+
let(:request4) { FactoryBot.create :request_project, data_manager: other_user.uid, data_sponsor: other_user.uid, requested_by: other_user.uid }
1414

1515
let!(:project1) { request1.approve(current_user) }
1616
let!(:project2) {

spec/system/project_details_spec.rb

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,11 +150,26 @@
150150
let(:request) { FactoryBot.create :request_project, project_title: "project 111", data_sponsor: sponsor_user.uid }
151151
let!(:project) { request.approve(sponsor_and_data_manager_user) }
152152
let(:submission_event) { FactoryBot.create(:submission_event, project: project) }
153-
it "shows provenance events" do
153+
let(:approval_event) { FactoryBot.create(:approval_event, project: project) }
154+
it "shows submission events" do
154155
submission_event
155156
sign_in sponsor_user
156157
visit "/projects/#{project.id}/details"
157-
expect(page).to have_content "#{submission_event.event_details}, #{submission_event.created_at.to_time.in_time_zone('America/New_York').iso8601}"
158+
expect(page).to have_content("Requested by")
159+
expect(page).to have_css(".provenance-value", count: 4)
160+
expect(page).to have_content "Requested Date–Time"
161+
expect(page).to have_content "#{submission_event.created_at.to_datetime.strftime("%B %d, %Y")}"
162+
expect(page).to have_content "#{submission_event.created_at.to_datetime.strftime("%I:%M %p")}"
163+
end
164+
it "shows approval events" do
165+
approval_event
166+
sign_in sponsor_user
167+
visit "/projects/#{project.id}/details"
168+
expect(page).to have_content("Approved by")
169+
expect(page).to have_css(".provenance-value", count: 4)
170+
expect(page).to have_content "Approval Date–Time"
171+
expect(page).to have_content "#{approval_event.created_at.to_datetime.strftime("%B %d, %Y")}"
172+
expect(page).to have_content "#{approval_event.created_at.to_datetime.strftime("%I:%M %p")}"
158173
end
159174
it "shows the project status under the provenance section" do
160175
submission_event

0 commit comments

Comments
 (0)