Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prototyping an initial implementation for the disk storage quota usage component in the Project Show View #1146

Merged
merged 4 commits into from
Jan 24, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 93 additions & 0 deletions app/assets/stylesheets/_projects.scss
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,99 @@
.date-and-data-info li:first-child:after {
content: "|";
padding: 0 0.5em;

float: right;
border-radius: 0.5em;
padding: 0.5em 0.75em 0.5em 0.5em;
text-transform: capitalize;
font-weight: bold;
}

.status::before {
content: "";
width: 20px;
height: 20px;
}

.active {
background: url("status_active.svg") no-repeat;
background-color: $status-info;
background-position: left;
padding-left: 20px;
color: $blue-dark;
}

.pending {
background: url("status_pending.svg") no-repeat;
background-color: $status-warning;
background-position: left;
padding-left: 20px;
color: $yellow-dark;
}

.approved {
background: url("status_approved.svg") no-repeat;
background-color: $status-success;
background-position: left;
padding-left: 20px;
color: $green-dark;
}

.rejected {
background: url("status_rejected.svg") no-repeat;
background-color: $status-error;
background-position: left;
padding-left: 20px;
color: $red-darker;
}

#project-description {
color: $black;
font-family: "Libre Franklin";
font-size: 1em;
font-style: normal;
font-weight: 400;
line-height: 1.5em;
}

.truncate {
overflow: hidden;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}

a#show-more-less-link {
color: $gray-60;
}

.label {
font-weight: bold;
}

.date-and-data-info ul {
padding: 0;
}

.date-and-data-info li {
float: left;
list-style-type: none;
line-height: 1em;
}

.date-and-data-info li:first-child:after {
content: "|";
padding: 0 0.5em;
}

.storage-quota {
border-radius: 0.5rem;
&-total {
font-weight: bold;
}
@include media-breakpoint-up(lg) {
max-width: 75%;
}
}
}

Expand Down
3 changes: 3 additions & 0 deletions app/assets/stylesheets/_variables.scss
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ $status-warning: #fff6df;
$status-info: #e6eef8;

$libre-franklin: "Libre Franklin", sans-serif;

$progress-bar-bg: $princeton-orange;
$progress-height: 2rem;
4 changes: 3 additions & 1 deletion app/javascript/entrypoints/pulDataTables.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@ export function setupTable(tableId) {

// If we have a table initialize it with DataTables
if (table) {
table.dataTable(datasetOptions);
if (table.dataTable) {
table.dataTable(datasetOptions);
}
}
}

Expand Down
6 changes: 5 additions & 1 deletion app/models/project.rb
Original file line number Diff line number Diff line change
Expand Up @@ -212,8 +212,12 @@ def asset_count(session_id:)
values.fetch(:total_file_count, 0)
end

def self.default_storage_unit
"KB"
end

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

def storage_usage(session_id:)
Expand Down
65 changes: 64 additions & 1 deletion app/presenters/project_show_presenter.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# frozen_string_literal: true
class ProjectShowPresenter
delegate "id", "in_mediaflux?", "mediaflux_id", "pending?", "status", "title", to: :project
delegate "description", "project_id", "storage_capacity", "storage_performance_expectations", "project_purpose", to: :project_metadata
delegate "description", "project_id", "storage_performance_expectations", "project_purpose", to: :project_metadata

attr_reader :project, :project_metadata

Expand Down Expand Up @@ -30,4 +30,67 @@ def data_manager
def project_directory
project.project_directory.gsub(Mediaflux::Connection.hidden_root, "")
end

# This assumed that the storage usage is recorded in the same units as the units specified in the StorageCapacity metadata
def storage_usage(session_id:)
persisted = project.storage_usage_raw(session_id: session_id)
value = persisted.to_f

value*default_usage_divisor
end

def formatted_storage_usage(session_id:)
value = storage_usage(session_id: session_id)
format("%.3f", value)
end

def storage_capacity(session_id: nil)
return project_metadata.storage_capacity if session_id.nil?

persisted = project.storage_capacity_raw(session_id: session_id)
value = persisted.to_f

value*default_capacity_divisor
end

def formatted_storage_capacity(session_id:)
value = storage_capacity(session_id: session_id)
format("%.3f", value)
end

def formatted_storage_remaining(session_id:)
value = storage_remaining(session_id:)
format("%.3f", value)
end

def storage_usage_percent(session_id:)
capacity = storage_capacity(session_id: session_id)
return 0.0 if capacity.zero?

usage = storage_usage(session_id: session_id)

value = (usage/(capacity/default_capacity_divisor))*100.0
format("%.1f", value)
end

private

def default_usage_divisor
1.0/(1000.0**1)
end

# Capacity is in bytes
def default_capacity_divisor
1.0/(1000.0**3)
end

def storage_remaining(session_id:)
capacity = storage_capacity(session_id: session_id)
return 0.0 if capacity.zero?

usage = storage_usage(session_id: session_id)

remaining = (capacity/default_capacity_divisor) - usage
remaining*default_capacity_divisor
end
end
106 changes: 68 additions & 38 deletions app/views/projects/_project_details_heading.html.erb
Original file line number Diff line number Diff line change
@@ -1,49 +1,79 @@
<div id="project-details-heading">

<div class="row">
<div class="col-12 col-md-8">
<h1><%= @project.title %></h1>
</div>
<div class="col-6 col-md-4">
<div class="status <%= @project.status.downcase %>">
<%= @project.status %>
<div class="col-6 col-md-8">
<h1><%= @project.title %></h1>
<div class="heading-decoration"></div>
</div>
</div>
</div>


<div class="heading-decoration"></div>

<div id="project-description">
<div id="description-text" class="truncate">
<%= @project.description %>
<div class="col-6 col-md-4">
<div class="status <%= @project.status.downcase %>">
<%= @project.status %>
</div>
</div>
<a id="show-more-less-link" class="show-more" href="#"><span>more...</span></a>
</div>
</div><!-- .row -->

<div class="row">
<div class="col-12">
<div class="date-and-data-info">
<ul>
<li><span class="label">Date Created:</span> <%= @project.created %></li>
<li><span class="label">Last updated:</span> <%= @project.updated %></li>
</ul>
</div>
</div>
</div>
<div class="row">
<div class="col-lg-6"><!-- .col -->

<div id="project-description">
<div id="description-text" class="truncate">
<%= @project.description %>
</div>
<a id="show-more-less-link" class="show-more" href="#"><span>more...</span></a>
</div><!-- #project-description -->

<div class="row">
<div class="col-12">
<div class="date-and-data-info">
<ul>
<li><span class="label">Data Sponsor:</span> <span class="netid-large"><%= @project.data_sponsor %></span></li>
<li><span class="label">Data Manager:</span> <span class="netid-large"><%= @project.data_manager %></span></li>
</ul>
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-12">
<div class="date-and-data-info">
<ul>
<li><span class="label">Date Created:</span> <%= @project.created %></li>
<li><span class="label">Last updated:</span> <%= @project.updated %></li>
</ul>
</div>
</div>
</div><!-- .row -->

<div class="row">
<div class="col-12">
<div class="date-and-data-info">
<ul>
<li><span class="label">Data Sponsor:</span> <span class="netid-large"><%= @project.data_sponsor %></span></li>
<li><span class="label">Data Manager:</span> <span class="netid-large"><%= @project.data_manager %></span></li>
</ul>
</div>
</div><!-- .col-12 -->
</div><!-- .row -->
</div><!-- .col -->

<div class="col-lg-6">
<div class="row mt-4">
<div class="storage-quota container-fluid px-3 py-2 border border-secondary me-0">
<div class="row mt-2 mb-4">
<div class="col-6">
<b>Storage</b> <span class="storage-quota-total text-secondary">(<%= @project.formatted_storage_capacity(session_id: current_user.mediaflux_session) %> GB)</span>
</div>
<div class="col-6 text-end">
<%= link_to "Request More", "mailto:[email protected]" %>
</div>
</div>
<div class="row my-2">
<div class="progress" role="progressbar" aria-label="Storage quota usage" aria-valuenow="<%= @project.storage_usage_percent(session_id: current_user.mediaflux_session) %>" aria-valuemin="0" aria-valuemax="100">
<div class="progress-bar" style="width: <%= @project.storage_usage_percent(session_id: current_user.mediaflux_session) %>%"></div>
</div>
</div>
<div class="row my-2">
<div class="col-6">
<%= @project.formatted_storage_usage(session_id: current_user.mediaflux_session) %> KB Used
</div>
<div class="col-6 text-secondary text-end">
<%= @project.formatted_storage_remaining(session_id: current_user.mediaflux_session) %> GB Free
</div>
</div>
</div>
</div><!-- .row -->
</div><!-- .col -->
</div><!-- .row -->
</div><!-- #project-details-heading -->

<script type="module">

Expand Down
12 changes: 12 additions & 0 deletions spec/models/project_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -414,4 +414,16 @@
expect(project.metadata_model.status).to_not eq Project::ACTIVE_STATUS
end
end

describe ".default_storage_unit" do
it "returns the default storage unit of KB" do
expect(described_class.default_storage_unit).to eq("KB")
end
end

describe ".default_storage_usage" do
it "returns the default storage usage of 0 units" do
expect(described_class.default_storage_usage).to eq("0 KB")
end
end
end
8 changes: 8 additions & 0 deletions spec/system/project_details_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,14 @@
Mediaflux::AssetDestroyRequest.new(session_token: sponsor_user.mediaflux_session, collection: project.mediaflux_id, members: true).resolve
end

it "renders the storage quota usage component" do
sign_in sponsor_user

visit "/projects/#{project.id}/details"
expect(page).to have_selector(:link_or_button, "Content Preview")
click_on("Content Preview")
end

it "Contents page has collection summary data" do
# sign in and be able to view the file count for the collection
sign_in sponsor_user
Expand Down
Loading