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

feat: user storage space #2274

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
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
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -33,3 +33,4 @@ DATA_CITE_DEVICE_PREFIX=DEVICE-
DATA_CITE_RESEARCH_PLAN_PREFIX=RP-
DATA_CITE_DEVICE_PUBLISHER=chemotion.net
DATA_CITE_DEVICE_CREATOR=chemotion.net
CRON_CONFIG_DISK_USAGE='0 0 * * *'
32 changes: 25 additions & 7 deletions app/api/chemotion/admin_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,19 @@ class AdminAPI < Grape::API
{ percent_used: stat.percent_used.round(2), mb_available: mb_available }
end

namespace :usersDefault do
get do
default = User.default_disk_space / 1024 / 1024
{ allocated_user_space: default }
end
put do
params do
require :allocatedUserSpace, type: Integer, desc: 'users default allocated space'
end
User.default_disk_space = params[:allocatedUserSpace]
end
end

namespace :listLocalCollector do
desc 'List all local collectors'
get 'all' do
Expand Down Expand Up @@ -103,14 +116,19 @@ class AdminAPI < Grape::API
obj = Group.find(params[:id]) if %w[Group].include?(params[:rootType])
obj = Device.find(params[:id]) if %w[Device].include?(params[:rootType])
if %w[Person Group].include?(params[:actionType])
new_users = (params[:add_users] || []).map(&:to_i) - obj.users.pluck(:id)
new_user_ids = (params[:add_users] || []).map(&:to_i) - obj.users.pluck(:id)
new_user_ids.each do |uid|
user = Person.find(uid)
user.allocated_space = [user.allocated_space, obj.allocated_space].max
user.save!
end
end
if %w[Device].include?(params[:actionType])
new_users = (params[:add_users] || []).map(&:to_i) - obj.devices.pluck(:id)
new_user_ids = (params[:add_users] || []).map(&:to_i) - obj.devices.pluck(:id)
end
obj.users << Person.where(id: new_users) if %w[Person].include?(params[:actionType])
obj.users << Group.where(id: new_users) if %w[Group].include?(params[:actionType])
obj.devices << Device.where(id: new_users) if %w[Device].include?(params[:actionType])
obj.users << Person.where(id: new_user_ids) if %w[Person].include?(params[:actionType])
obj.users << Group.where(id: new_user_ids) if %w[Group].include?(params[:actionType])
obj.devices << Device.where(id: new_user_ids) if %w[Device].include?(params[:actionType])
obj.save!

if obj.is_a?(Device)
Expand Down Expand Up @@ -164,7 +182,7 @@ class AdminAPI < Grape::API
# rewrite edited json file
result = Entities::OlsTermEntity.represent(
OlsTerm.where(owl_name: params[:owl_name], is_enabled: true).select(
<<~SQL
<<~SQL.squish,
id, owl_name, term_id, label, synonym, synonyms, 'desc' as desc,
case when (ancestry is null) then null else
(select array_to_string(array(
Expand Down Expand Up @@ -248,7 +266,7 @@ class AdminAPI < Grape::API
end

put do
Delayed::Job.find(params[:id]).update_columns(run_at: 1.minutes.from_now, failed_at: nil)
Delayed::Job.find(params[:id]).update_columns(run_at: 1.minute.from_now, failed_at: nil)
PiTrem marked this conversation as resolved.
Show resolved Hide resolved

{} # FE does not use the result
end
Expand Down
3 changes: 2 additions & 1 deletion app/api/chemotion/admin_user_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,7 @@ class AdminUserAPI < Grape::API
optional :last_name, type: String, desc: 'user last_name'
optional :name_abbreviation, type: String, desc: 'user name name_abbreviation'
optional :type, type: String, desc: 'user type'
optional :allocated_space, type: Integer, desc: 'user allocated_space'
optional :account_active, type: Boolean, desc: '(in)activate or activate user account'
# special params
optional :enable, type: Boolean, desc: '(un)lock user account'
Expand Down Expand Up @@ -222,7 +223,7 @@ class AdminUserAPI < Grape::API
resource :matrix do
desc 'Find all matrices'
get do
present Matrice.all.order('id'), with: Entities::MatriceEntity, root: 'matrices'
present Matrice.order('id'), with: Entities::MatriceEntity, root: 'matrices'
end

desc 'update matrice'
Expand Down
8 changes: 5 additions & 3 deletions app/api/chemotion/attachable_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,11 @@ module Chemotion
class AttachableAPI < Grape::API
resource :attachable do
params do
optional :files, type: Array[File], desc: 'files', default: []
optional :files, type: [File], desc: 'files', default: []
optional :attachable_type, type: String, desc: 'attachable_type'
optional :attachable_id, type: Integer, desc: 'attachable id'
optional :attfilesIdentifier, type: Array[String], desc: 'file identifier'
optional :del_files, type: Array[Integer], desc: 'del file id', default: []
optional :attfilesIdentifier, type: [String], desc: 'file identifier'
optional :del_files, type: [Integer], desc: 'del file id', default: []
end
after_validation do
case params[:attachable_type]
Expand Down Expand Up @@ -51,6 +51,8 @@ class AttachableAPI < Grape::API
if a.attachable_type.in?(%w[ResearchPlan Wellplate DeviceDescription Labimotion::Element])
rp_attach_ary.push(a.id)
end
rescue StandardError
status 413
ensure
tempfile.close
tempfile.unlink
Expand Down
14 changes: 8 additions & 6 deletions app/api/chemotion/attachment_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ def remove_duplicated(att)
desc 'Upload files to Inbox as unsorted'
post 'upload_to_inbox' do
attach_ary = []
params.each do |_file_id, file|
params.each_value do |file|
next unless tempfile = file[:tempfile] # rubocop:disable Lint/AssignmentInCondition

attach = Attachment.new(
Expand All @@ -274,6 +274,8 @@ def remove_duplicated(att)
begin
attach.save!
attach_ary.push(attach.id)
rescue StandardError
status 413
ensure
tempfile.close
tempfile.unlink
Expand Down Expand Up @@ -417,7 +419,7 @@ def remove_duplicated(att)

desc 'Return Base64 encoded thumbnails'
params do
requires :ids, type: Array[Integer]
requires :ids, type: [Integer]
end
post 'thumbnails' do
thumbnails = params[:ids].map do |a_id|
Expand All @@ -434,7 +436,7 @@ def remove_duplicated(att)

desc 'Return Base64 encoded files'
params do
requires :ids, type: Array[Integer]
requires :ids, type: [Integer]
end
post 'files' do
files = params[:ids].map do |a_id|
Expand All @@ -451,8 +453,8 @@ def remove_duplicated(att)

desc 'Regenerate spectra'
params do
requires :original, type: Array[Integer]
requires :generated, type: Array[Integer]
requires :original, type: [Integer]
requires :generated, type: [Integer]
end
post 'regenerate_spectrum' do
pm = to_rails_snake_case(params)
Expand All @@ -475,7 +477,7 @@ def remove_duplicated(att)

desc 'Regenerate edited spectra'
params do
requires :edited, type: Array[Integer]
requires :edited, type: [Integer]
optional :molfile, type: String
end
post 'regenerate_edited_spectrum' do
Expand Down
44 changes: 24 additions & 20 deletions app/api/chemotion/report_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ def hashize(inputs)
end

def time_now
Time.now.strftime('%Y-%m-%dT%H-%M-%S')
Time.zone.now.strftime('%Y-%m-%dT%H-%M-%S')
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Metrics/ClassLength: Class has too many lines. [237/200]

end

def is_int?
Expand All @@ -36,7 +36,7 @@ def is_int?
env['api.format'] = :binary
header(
'Content-Disposition',
"attachment; filename*=UTF-8''#{CGI.escape(filename)}"
"attachment; filename*=UTF-8''#{CGI.escape(filename)}",
)
docx
end
Expand Down Expand Up @@ -107,8 +107,8 @@ def is_int?

results = reaction_smiles_hash(
real_coll_id,
p_t[:checkedAll] && p_t[:uncheckedIds] || p_t[:checkedIds],
p_t[:checkedAll]
(p_t[:checkedAll] && p_t[:uncheckedIds]) || p_t[:checkedIds],
p_t[:checkedAll],
) || {}
smiles_construct = "r_smiles_#{params[:exportType]}"
results.map { |_, v| send(smiles_construct, v) }.join("\r\n")
Expand All @@ -123,7 +123,7 @@ def is_int?
header(
'Content-Disposition',
"attachment; filename*=UTF-8''#{CGI.escape("Wellplate_#{params[:id]}_\
Samples Excel.xlsx")}"
Samples Excel.xlsx")}",
)
export = Export::ExportExcel.new
column_query = build_column_query(default_columns_wellplate, current_user.id)
Expand All @@ -145,7 +145,7 @@ def is_int?
header(
'Content-Disposition',
"attachment; filename*=UTF-8''#{CGI.escape("Reaction_#{params[:id]}_\
Samples Excel.xlsx")}"
Samples Excel.xlsx")}",
)
export = Export::ExportExcel.new
column_query = build_column_query(default_columns_reaction, current_user.id)
Expand Down Expand Up @@ -174,7 +174,7 @@ def is_int?

desc 'return reports which can be downloaded now'
params do
requires :ids, type: Array[Integer]
requires :ids, type: [Integer]
end
post :downloadable do
reports = current_user.reports.where(id: params[:ids]).where.not(generated_at: nil)
Expand All @@ -198,15 +198,15 @@ def is_int?

desc 'returns a created report'
params do
requires :objTags, type: Array[Hash]
requires :splSettings, type: Array[Hash]
requires :rxnSettings, type: Array[Hash]
requires :siRxnSettings, type: Array[Hash]
requires :configs, type: Array[Hash]
requires :molSerials, type: Array[Hash]
requires :prdAtts, type: Array[Hash]
requires :objTags, type: [Hash]
requires :splSettings, type: [Hash]
requires :rxnSettings, type: [Hash]
requires :siRxnSettings, type: [Hash]
requires :configs, type: [Hash]
requires :molSerials, type: [Hash]
requires :prdAtts, type: [Hash]
requires :imgFormat, type: String, default: 'png', values: %w[png eps emf]
requires :fileName, type: String, default: 'ELN_Report_' + Time.now.strftime('%Y-%m-%dT%H-%M-%S')
requires :fileName, type: String, default: "ELN_Report_#{Time.zone.now.strftime('%Y-%m-%dT%H-%M-%S')}"
requires :templateId, type: String
optional :templateType, type: String, default: 'standard', values: ReportTemplate::REPORT_TYPES
optional :fileDescription
Expand All @@ -229,13 +229,17 @@ def is_int?
objects: params[:objTags],
img_format: params[:imgFormat],
template: params[:templateType],
report_templates_id: !!/\A\d+\z/.match(params[:templateId]) ? params[:templateId].to_i : nil,
author_id: current_user.id
report_templates_id: /\A\d+\z/.match(params[:templateId]).nil? ? nil : params[:templateId].to_i,
author_id: current_user.id,
}

report = Report.create(attributes)
current_user.reports << report
report.create_docx
begin
report.create_docx
rescue StandardError => e
report.file_description = "Report could not be generated: #{e.message}"
end

present report, with: Entities::ReportEntity, root: :report
end
Expand All @@ -249,7 +253,7 @@ def is_int?
end

get :file do
ext = params[:ext]
params[:ext]
report = current_user.reports.find(params[:id])

if report
Expand All @@ -262,7 +266,7 @@ def is_int?
env['api.format'] = :binary
header(
'Content-Disposition',
"attachment; filename*=UTF-8''#{CGI.escape(att.filename)}"
"attachment; filename*=UTF-8''#{CGI.escape(att.filename)}",
)
att.read_file
end
Expand Down
2 changes: 2 additions & 0 deletions app/api/entities/user_entity.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ class UserEntity < Grape::Entity
expose :first_name, documentation: { type: 'String', desc: "User's name" }
expose :last_name, documentation: { type: 'String', desc: "User's name" }
expose :initials, documentation: { type: 'String', desc: 'initials' }
expose :used_space, documentation: { type: 'Integer', desc: "User's used storage space" }
expose :allocated_space, documentation: { type: 'Integer', desc: "User's allocated storage space (0=infinite)" }
expose :samples_count, documentation: { type: 'Integer', desc: 'Sample count' }
expose :reactions_count, documentation: { type: 'Integer', desc: 'Reactions count' }
expose :cell_lines_count, documentation: { type: 'Integer', desc: 'Cellline Samples count' }
Expand Down
78 changes: 56 additions & 22 deletions app/javascript/src/apps/admin/AdminDashboard.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import React from 'react';
import { InputGroup, Card, Form } from 'react-bootstrap';
import {
InputGroup, Card, Form, Button
} from 'react-bootstrap';
import AdminFetcher from 'src/fetchers/AdminFetcher';

export default class AdminDashboard extends React.Component {
Expand All @@ -8,13 +10,16 @@ export default class AdminDashboard extends React.Component {
this.state = {
diskAvailable: 0,
diskPercentUsed: 0,
allocatedUserSpace: 0,
showDiskInfo: false,
};
this.handleDiskspace = this.handleDiskspace.bind(this);
this.handleSaveBtn = this.handleSaveBtn.bind(this);
}

componentDidMount() {
this.handleDiskspace();
this.getAllocatedUserSpace();
}

handleDiskspace() {
Expand All @@ -28,30 +33,59 @@ export default class AdminDashboard extends React.Component {
});
}

handleSaveBtn() {
const { allocatedUserSpace } = this.state;
AdminFetcher.setAllocatedUserSpace(Math.round(allocatedUserSpace) * 1024 * 1024);
}

getAllocatedUserSpace() {
AdminFetcher.getAllocatedUserSpace()
.then((result) => {
this.setState({
allocatedUserSpace: result.allocated_user_space,
});
});
}

renderDiskInfo() {
const { diskAvailable, diskPercentUsed } = this.state;
let className = diskPercentUsed > 80 ? 'text-danger' : '';
const {
diskAvailable, diskPercentUsed, allocatedUserSpace
} = this.state;
const className = diskPercentUsed > 80 ? 'text-danger' : '';

return (
<Card>
<Card.Body className='p-0'>
<InputGroup >
<InputGroup.Text >Disk Available (MB)</InputGroup.Text>
<Form.Control
type="text"
defaultValue={diskAvailable || ''}
readOnly
/>
<InputGroup.Text >Disk Percent Used (%)</InputGroup.Text>
<Form.Control
type="text"
className={className}
defaultValue={`${diskPercentUsed}%` || ''}
readOnly
/>
</InputGroup>
</Card.Body>
</Card>
<Card style={{ width: '30rem' }}>
<Card.Body className="p-0">
<InputGroup.Text>Disk Available (MB)</InputGroup.Text>
<Form.Control
type="text"
defaultValue={diskAvailable || ''}
readOnly
/>
<InputGroup.Text>Disk Percent Used (%)</InputGroup.Text>
<Form.Control
type="text"
className={className}
defaultValue={`${diskPercentUsed}%` || ''}
readOnly
/>
<InputGroup.Text>Default User Allocated Space (MB)</InputGroup.Text>
<InputGroup>
<Form.Control
type="number"
min="0"
defaultValue={allocatedUserSpace || ''}
onChange={(event) => this.setState({ allocatedUserSpace: event.target.value })}
/>
<Button
variant="warning"
onClick={() => this.handleSaveBtn()}
>
Save
</Button>
</InputGroup>
</Card.Body>
</Card>
);
}

Expand Down
Loading