mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-03-06 12:43:06 +08:00
Merge branch 'master' into features/templates
This commit is contained in:
commit
d03ef2cd2a
22 changed files with 428 additions and 96 deletions
|
@ -36,6 +36,8 @@
|
|||
//= require i18n.js
|
||||
//= require i18n/translations
|
||||
//= require users/settings/teams/invite_users_modal
|
||||
//= require select2.min
|
||||
//= require select2_customization
|
||||
//= require turbolinks
|
||||
|
||||
|
||||
|
|
92
app/assets/javascripts/select2_customization.js
Normal file
92
app/assets/javascripts/select2_customization.js
Normal file
|
@ -0,0 +1,92 @@
|
|||
|
||||
$.fn.extend({
|
||||
select2Multiple: function(config = {}) {
|
||||
// Adding ID to each block
|
||||
var templateSelection = (state) => {
|
||||
return $('<span class="select2-block-body" data-select-id="' + state.id + '">'
|
||||
+ (config.customSelection !== undefined ? config.customSelection(state) : state.text)
|
||||
+ '</span>');
|
||||
};
|
||||
var select2 = this.select2({
|
||||
closeOnSelect: false,
|
||||
multiple: true,
|
||||
ajax: config.ajax,
|
||||
templateSelection: templateSelection
|
||||
});
|
||||
// select all check
|
||||
this[0].dataset.singleDisplay = config.singleDisplay || false;
|
||||
if (this[0].dataset.selectAll === 'true') {
|
||||
$.each($(this).find('option'), (index, e) => { e.selected = true; });
|
||||
this.trigger('change');
|
||||
}
|
||||
if (config.singleDisplay) {
|
||||
$(this).updateSingleName();
|
||||
}
|
||||
return select2
|
||||
// Adding select all button
|
||||
.on('select2:open', function() {
|
||||
var selectElement = this;
|
||||
$('.select2-selection').scrollTo(0);
|
||||
$('.select2_select_all').remove();
|
||||
if (selectElement.dataset.selectAllButton !== undefined) {
|
||||
$('<div class="select2_select_all btn btn-default"><strong>' + selectElement.dataset.selectAllButton + '</strong></div>').prependTo('.select2-dropdown').on('click', function() {
|
||||
var elementsToSelect = $.map($(selectElement).find('option'), e => e.value);
|
||||
if ($(selectElement).find('option:selected').length === elementsToSelect.length) elementsToSelect = [];
|
||||
$(selectElement).val(elementsToSelect).trigger('change');
|
||||
$(selectElement).select2('close');
|
||||
$(selectElement).select2('open');
|
||||
});
|
||||
}
|
||||
})
|
||||
// Prevent shake bug with multiple select
|
||||
.on('select2:open select2:close', function() {
|
||||
if ($(this).val() != null && $(this).val().length > 3) {
|
||||
$(this).next().find('.select2-search__field')[0].disabled = true;
|
||||
} else {
|
||||
$(this).next().find('.select2-search__field')[0].disabled = false;
|
||||
}
|
||||
$('.select2-selection').scrollTo(0);
|
||||
})
|
||||
// Prevent opening window when deleteing selection
|
||||
.on('select2:unselect', function() {
|
||||
var resultWindow = $('.select2-container--open');
|
||||
if (resultWindow.length === 0) {
|
||||
$(this).select2('open');
|
||||
}
|
||||
})
|
||||
// Fxied scroll bug
|
||||
.on('select2:selecting select2:unselecting', function(e) {
|
||||
$(e.currentTarget).data('scrolltop', $('.select2-results__options').scrollTop());
|
||||
$('.select2-selection').scrollTo(0);
|
||||
})
|
||||
// Fxied scroll bug
|
||||
.on('select2:select select2:unselect change', function(e) {
|
||||
$('.select2-selection').scrollTo(0);
|
||||
$('.select2-results__options').scrollTop($(e.currentTarget).data('scrolltop'));
|
||||
if (this.dataset.singleDisplay === 'true') {
|
||||
$(this).updateSingleName();
|
||||
}
|
||||
});
|
||||
},
|
||||
select2MultipleClearAll: function() {
|
||||
$(this).val([]).trigger('change');
|
||||
},
|
||||
// Create from multiple blocks single one with counter
|
||||
updateSingleName: function() {
|
||||
var template = '';
|
||||
var selectElement = this;
|
||||
var selectedOptions = selectElement.next().find('.select2-selection__choice');
|
||||
var optionsCounter = selectedOptions.length;
|
||||
var allOptionsSelected = this.find('option').length === optionsCounter;
|
||||
var optionText = allOptionsSelected ? this[0].dataset.selectMultipleAllSelected : optionsCounter + ' ' + this[0].dataset.selectMultipleName;
|
||||
if (optionsCounter > 1) {
|
||||
selectedOptions.remove();
|
||||
template = '<li class="select2-selection__choice">'
|
||||
+ '<span class="select2-selection__choice__remove" role="presentation">×</span>'
|
||||
+ optionText
|
||||
+ '</li>';
|
||||
$(template).prependTo(selectElement.next().find('.select2-selection__rendered')).find('.select2-selection__choice__remove')
|
||||
.click(function() { selectElement.select2MultipleClearAll(); });
|
||||
}
|
||||
}
|
||||
});
|
89
app/assets/stylesheets/select2_customizations.scss
Normal file
89
app/assets/stylesheets/select2_customizations.scss
Normal file
|
@ -0,0 +1,89 @@
|
|||
@import "constants";
|
||||
@import "mixins";
|
||||
|
||||
.select2-container {
|
||||
|
||||
.select2-selection {
|
||||
max-height: 23px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
|
||||
&.select2-selection--multiple {
|
||||
border: 1px solid $color-alto;
|
||||
}
|
||||
|
||||
.select2-selection__rendered {
|
||||
width: 200%;
|
||||
}
|
||||
|
||||
.select2-selection__choice {
|
||||
background: $color-concrete;
|
||||
border: 1px solid $color-alto;
|
||||
|
||||
.select2-selection__choice__remove {
|
||||
float: right;
|
||||
margin: 0;
|
||||
margin-left: 5px;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.select2-dropdown {
|
||||
border: 1px solid $color-alto;
|
||||
|
||||
.select2_select_all {
|
||||
border: 0;
|
||||
border-bottom: 1px solid $color-alto;
|
||||
border-radius: 0;
|
||||
cursor: pointer;
|
||||
line-height: 20px;
|
||||
padding: 5px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.select2-results__option[role="group"] {
|
||||
strong {
|
||||
font-size: $font-size-base;
|
||||
}
|
||||
}
|
||||
|
||||
.select2-results__option[role="treeitem"] {
|
||||
background: $color-white;
|
||||
line-height: 18px;
|
||||
padding-left: 30px;
|
||||
position: relative;
|
||||
|
||||
&::before {
|
||||
background: $color-white;
|
||||
border: 1px solid $color-emperor;
|
||||
border-radius: 6px;
|
||||
content: "";
|
||||
height: 12px;
|
||||
left: 0;
|
||||
margin: 9px;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
transition: .3s;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
&.select2-results__option--highlighted {
|
||||
background: inherit;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
&[aria-selected="true"] {
|
||||
background: $color-concrete;
|
||||
|
||||
&::before {
|
||||
background: $color-emperor;
|
||||
}
|
||||
}
|
||||
|
||||
&:hover {
|
||||
background: $color-alto;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -25,6 +25,7 @@
|
|||
|
||||
&:hover {
|
||||
background: $color-concrete;
|
||||
color: $color-emperor;
|
||||
opacity: .7;
|
||||
}
|
||||
|
||||
|
@ -101,8 +102,13 @@
|
|||
}
|
||||
}
|
||||
|
||||
.no-notification-meessage {
|
||||
padding: 20px 0;
|
||||
.system-notifications-container {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.no-notification-message {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
|
@ -124,10 +130,13 @@
|
|||
margin-top: 10px;
|
||||
}
|
||||
}
|
||||
|
||||
#manage-module-system-notification-modal {
|
||||
|
||||
.modal-title {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.modal-body {
|
||||
margin: 20px;
|
||||
}
|
||||
|
|
|
@ -395,7 +395,8 @@
|
|||
word-wrap: break-word;
|
||||
|
||||
.system-notifications-no-recent {
|
||||
padding: 0 0 10px 10px;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.system-notifications-dropdown-header {
|
||||
|
|
|
@ -248,14 +248,14 @@ class MyModule < ApplicationRecord
|
|||
end
|
||||
|
||||
def is_overdue?(datetime = DateTime.current)
|
||||
due_date.present? and datetime.utc > due_date.utc
|
||||
due_date.present? && datetime.utc > due_date.end_of_day.utc
|
||||
end
|
||||
|
||||
def overdue_for_days(datetime = DateTime.current)
|
||||
if due_date.blank? or due_date.utc > datetime.utc
|
||||
return 0
|
||||
if due_date.blank? || due_date.end_of_day.utc > datetime.utc
|
||||
0
|
||||
else
|
||||
return ((datetime.utc.to_i - due_date.utc.to_i) / (60*60*24).to_f).ceil
|
||||
((datetime.utc.to_i - due_date.end_of_day.utc.to_i) / 1.day.to_f).ceil
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -264,7 +264,9 @@ class MyModule < ApplicationRecord
|
|||
end
|
||||
|
||||
def is_due_in?(datetime, diff)
|
||||
due_date.present? and datetime.utc < due_date.utc and datetime.utc > (due_date.utc - diff)
|
||||
due_date.present? &&
|
||||
datetime.utc < due_date.end_of_day.utc &&
|
||||
datetime.utc > (due_date.end_of_day.utc - diff)
|
||||
end
|
||||
|
||||
def space_taken
|
||||
|
|
|
@ -488,7 +488,8 @@ class User < ApplicationRecord
|
|||
|
||||
# This method must be overwriten for addons that will be installed
|
||||
def show_login_system_notification?
|
||||
user_system_notifications.show_on_login.present?
|
||||
user_system_notifications.show_on_login.present? &&
|
||||
(ENV['ENABLE_TUTORIAL'] != 'true' || settings['tutorial_completed'])
|
||||
end
|
||||
|
||||
# json friendly attributes
|
||||
|
|
|
@ -13,6 +13,7 @@ class UserProject < ApplicationRecord
|
|||
belongs_to :project, inverse_of: :user_projects, touch: true, optional: true
|
||||
|
||||
before_destroy :destroy_associations
|
||||
validates_uniqueness_of :user_id, scope: :project_id
|
||||
|
||||
def role_str
|
||||
I18n.t("user_projects.enums.role.#{role.to_s}")
|
||||
|
|
|
@ -14,6 +14,7 @@ class UserTeam < ApplicationRecord
|
|||
|
||||
before_destroy :destroy_associations
|
||||
after_create :create_samples_table_state
|
||||
validates_uniqueness_of :user_id, scope: :team_id
|
||||
|
||||
def role_str
|
||||
I18n.t("user_teams.enums.role.#{role}")
|
||||
|
|
|
@ -93,7 +93,7 @@ module Experiments
|
|||
user: @user,
|
||||
message: I18n.t(
|
||||
'activities.clone_experiment',
|
||||
user: @user,
|
||||
user: @user.full_name,
|
||||
experiment_new: @c_exp.name,
|
||||
experiment_original: @exp.name
|
||||
)
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
module FirstTimeDataGenerator
|
||||
# Default inventory repository
|
||||
REPO_SAMPLES_NAME = 'Samples'.freeze
|
||||
|
||||
# Create data for demo for new users
|
||||
def seed_demo_data(user, team)
|
||||
def seed_demo_data(user, team, asset_queue = :demo)
|
||||
@user = user
|
||||
|
||||
# If private private team does not exist,
|
||||
|
@ -8,28 +11,49 @@ module FirstTimeDataGenerator
|
|||
# Do nothing
|
||||
return unless team
|
||||
|
||||
# create custom repository samples
|
||||
repository = Repository.create(
|
||||
name: 'Samples',
|
||||
team: team,
|
||||
created_by: user
|
||||
)
|
||||
# check if samples repo already exist, then create custom repository samples
|
||||
repository = Repository.where(team: team).where(name: REPO_SAMPLES_NAME)
|
||||
repository =
|
||||
if repository.blank?
|
||||
if team.repositories.count < Rails.configuration.x.repositories_limit
|
||||
Repository.create(
|
||||
name: REPO_SAMPLES_NAME,
|
||||
team: team,
|
||||
created_by: user
|
||||
)
|
||||
else
|
||||
# User first repo just as a placeholder, this call will fail anyhow
|
||||
Repository.create(
|
||||
name: team.repositories.first.name,
|
||||
team: team,
|
||||
created_by: user
|
||||
)
|
||||
end
|
||||
else
|
||||
repository.first
|
||||
end
|
||||
|
||||
# create list value column for sample types
|
||||
repository_column_sample_types = RepositoryColumn.create(
|
||||
repository: repository,
|
||||
created_by: user,
|
||||
data_type: :RepositoryListValue,
|
||||
name: 'Sample Types'
|
||||
)
|
||||
repo_columns = []
|
||||
['Sample Types', 'Sample Groups'].each do |repo_name|
|
||||
repo_column = repository.repository_columns.where(name: repo_name)
|
||||
|
||||
# create list value column for sample groups
|
||||
repository_column_sample_groups = RepositoryColumn.create(
|
||||
repository: repository,
|
||||
created_by: user,
|
||||
data_type: :RepositoryListValue,
|
||||
name: 'Sample Groups'
|
||||
)
|
||||
repo_columns <<
|
||||
if repo_column.blank?
|
||||
RepositoryColumn.create(
|
||||
repository: repository,
|
||||
created_by: user,
|
||||
data_type: :RepositoryListValue,
|
||||
name: repo_name
|
||||
)
|
||||
else
|
||||
repo_column.first
|
||||
end
|
||||
end
|
||||
|
||||
# Maintain old names
|
||||
repository_column_sample_types, repository_column_sample_groups =
|
||||
repo_columns
|
||||
|
||||
# create few list items for sample types
|
||||
repository_items_sample_types = []
|
||||
|
@ -41,7 +65,15 @@ module FirstTimeDataGenerator
|
|||
repository_column: repository_column_sample_types,
|
||||
repository: repository
|
||||
)
|
||||
repository_items_sample_types << item
|
||||
|
||||
# Check if it already exists
|
||||
if item.persisted?
|
||||
repository_items_sample_types << item
|
||||
else
|
||||
repository_items_sample_types << repository_column_sample_types
|
||||
.repository_list_items
|
||||
.where(data: name).first
|
||||
end
|
||||
end
|
||||
|
||||
# create few list items for sample groups
|
||||
|
@ -54,7 +86,15 @@ module FirstTimeDataGenerator
|
|||
repository_column: repository_column_sample_groups,
|
||||
repository: repository
|
||||
)
|
||||
repository_items_sample_groups << item
|
||||
|
||||
# Check if it already exists
|
||||
if item.persisted?
|
||||
repository_items_sample_groups << item
|
||||
else
|
||||
repository_items_sample_groups << repository_column_sample_groups
|
||||
.repository_list_items
|
||||
.where(data: name).first
|
||||
end
|
||||
end
|
||||
|
||||
repository_rows_to_assign = []
|
||||
|
@ -153,6 +193,7 @@ module FirstTimeDataGenerator
|
|||
end
|
||||
|
||||
name = 'Demo project'
|
||||
name = '[NEW] Demo project by SciNote'
|
||||
exp_name = 'Polymerase chain reaction'
|
||||
# If there is an existing demo project, archive and rename it
|
||||
if team.projects.where(name: name).present?
|
||||
|
@ -516,19 +557,22 @@ module FirstTimeDataGenerator
|
|||
'Collection of potatoes'
|
||||
]
|
||||
|
||||
second_rep_item = smart_annotate_rep_item(repository_rows_to_assign.second)
|
||||
third_rep_item = smart_annotate_rep_item(repository_rows_to_assign.third)
|
||||
fifth_rep_item = smart_annotate_rep_item(repository_rows_to_assign.fifth)
|
||||
module_step_descriptions = [
|
||||
'<html>
|
||||
<body>
|
||||
<p>50% of samples should be mock inoculated
|
||||
<span class=\"atwho-inserted\"contenteditable=\"false\"
|
||||
data-atwho-at-query=\"#\">[#' + sample_name + '3~rep_item~3]</span>
|
||||
data-atwho-at-query=\"#\">[#' + third_rep_item + ']</span>
|
||||
<span class=\"atwho-inserted\" contenteditable=\"false\"
|
||||
data-atwho-at-query=\"#\">[#' + sample_name + '5~rep_item~5]</span>
|
||||
data-atwho-at-query=\"#\">[#' + fifth_rep_item + ']</span>
|
||||
while other 50% with PVY NTN virus
|
||||
<span class=\"atwho-inserted\" contenteditable=\"false\"
|
||||
data-atwho-at-query=\"#\">[#' + sample_name + '3~rep_item~3]</span>
|
||||
data-atwho-at-query=\"#\">[#' + third_rep_item + ']</span>
|
||||
<span class=\"atwho-inserted\" contenteditable=\"false\"
|
||||
data-atwho-at-query=\"#\">[#' + sample_name + '5~rep_item~5]</span>.
|
||||
data-atwho-at-query=\"#\">[#' + fifth_rep_item + ']</span>.
|
||||
</p>
|
||||
</body>
|
||||
</html>',
|
||||
|
@ -542,6 +586,9 @@ module FirstTimeDataGenerator
|
|||
module_step_names,
|
||||
module_step_descriptions)
|
||||
|
||||
# Delete repository items, if we went over the limit
|
||||
repository_rows_to_assign.map(&:destroy) unless repository.id
|
||||
|
||||
# Add table to existig step
|
||||
step = my_modules[1].protocol.steps.where('position = 0').take
|
||||
Table.create(
|
||||
|
@ -551,19 +598,18 @@ module FirstTimeDataGenerator
|
|||
contents: tab_content['module2']['samples_table']
|
||||
)
|
||||
# Add file to existig step
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[1].protocol.steps.where('position = 0').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
file_name: 'PVY-inoculated_plant_symptoms.JPG'
|
||||
)
|
||||
# Add comment to step 1
|
||||
user_annotation = '[@' + user.name + '~' + user.id.to_s + ']'
|
||||
user_annotation = user.name
|
||||
generate_step_comment(
|
||||
step,
|
||||
user,
|
||||
user_annotation + ' I have used different sample [#' + sample_name +
|
||||
'2~rep_item~2]'
|
||||
"#{user_annotation} I have used different sample [##{second_rep_item}]"
|
||||
)
|
||||
# Add comment to step 3
|
||||
step = my_modules[1].protocol.steps.where('position = 2').take
|
||||
|
@ -573,7 +619,7 @@ module FirstTimeDataGenerator
|
|||
user_annotation + ' Please complete this by Monday.'
|
||||
)
|
||||
# Results
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[1],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -582,7 +628,7 @@ module FirstTimeDataGenerator
|
|||
file_name: 'mock-inoculated-plant.JPG'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[1],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -713,7 +759,7 @@ module FirstTimeDataGenerator
|
|||
).sneaky_save
|
||||
|
||||
# Second result
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[2],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -810,7 +856,7 @@ module FirstTimeDataGenerator
|
|||
generate_module_steps(my_modules[3], module_step_names, module_step_descriptions)
|
||||
|
||||
# Add file to existig step 1
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[3].protocol.steps.where('position = 0').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -852,8 +898,8 @@ module FirstTimeDataGenerator
|
|||
'Perform all centrifugation steps at 20–25°C in a standard ' \
|
||||
'microcentrifuge. Ensure that the centrifuge does not cool below 20°C.'
|
||||
]
|
||||
module_checklist_items.each do |item|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item)
|
||||
module_checklist_items.each_with_index do |item, ind|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item, position: ind)
|
||||
end
|
||||
checklist.save
|
||||
|
||||
|
@ -877,8 +923,8 @@ module FirstTimeDataGenerator
|
|||
'If performing optional on-column DNase digestion, prepare DNase I ' \
|
||||
'stock solution as described in Appendix D (page 67).'
|
||||
]
|
||||
module_checklist_items.each do |item|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item)
|
||||
module_checklist_items.each_with_index do |item, ind|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item, position: ind)
|
||||
end
|
||||
checklist.save
|
||||
|
||||
|
@ -903,13 +949,13 @@ module FirstTimeDataGenerator
|
|||
'genomic DNA contamination”, page 21), follow steps D1–D4 (page 67) ' \
|
||||
'after performing this step.'
|
||||
]
|
||||
module_checklist_items.each do |item|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item)
|
||||
module_checklist_items.each_with_index do |item, ind|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item, position: ind)
|
||||
end
|
||||
checklist.save
|
||||
|
||||
# Results
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[3],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -962,8 +1008,8 @@ module FirstTimeDataGenerator
|
|||
step: step
|
||||
)
|
||||
|
||||
module_checklist_items.each do |item|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item)
|
||||
module_checklist_items.each_with_index do |item, ind|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item, position: ind)
|
||||
end
|
||||
checklist.save
|
||||
|
||||
|
@ -1025,41 +1071,41 @@ module FirstTimeDataGenerator
|
|||
'Clean surfaces with 70% ethanol or RNA remover',
|
||||
'Turn on the UV light'
|
||||
]
|
||||
module_checklist_items.each do |item|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item)
|
||||
module_checklist_items.each_with_index do |item, ind|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item, position: ind)
|
||||
end
|
||||
checklist.save
|
||||
|
||||
# Add file to existig steps
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[5].protocol.steps.where('position = 0').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
file_name: 'Mixes_Templats.xlsx'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[5].protocol.steps.where('position = 1').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
file_name: 'qPCR_template.jpg'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[5].protocol.steps.where('position = 1').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
file_name: '96plate.docx'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[5].protocol.steps.where('position = 2').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
file_name: 'cycling_conditions.JPG'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[5].protocol.steps.where('position = 2').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1102,7 +1148,7 @@ module FirstTimeDataGenerator
|
|||
).sneaky_save
|
||||
|
||||
# Results
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[5],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1111,7 +1157,7 @@ module FirstTimeDataGenerator
|
|||
file_name: '1505745387970-1058053257.jpg'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[5],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1120,7 +1166,7 @@ module FirstTimeDataGenerator
|
|||
file_name: 'chromatogram.png'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[5],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1129,7 +1175,7 @@ module FirstTimeDataGenerator
|
|||
file_name: 'curves.JPG'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[5],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1138,7 +1184,7 @@ module FirstTimeDataGenerator
|
|||
file_name: 'Bacterial_colonies.jpg'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[5],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1289,21 +1335,21 @@ module FirstTimeDataGenerator
|
|||
module_step_descriptions)
|
||||
|
||||
# Add file to existig steps
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[6].protocol.steps.where('position = 0').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
file_name: 'Native_SDS-PAGE_for_complex_analysis.jpg'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[6].protocol.steps.where('position = 4').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
file_name: 'Native-PAGE-Nature_protocols.pdf'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[6].protocol.steps.where('position = 5').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1321,8 +1367,8 @@ module FirstTimeDataGenerator
|
|||
'Check stock of reagents & order new stock if needed',
|
||||
'Use gloves at all times'
|
||||
]
|
||||
module_checklist_items.each do |item|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item)
|
||||
module_checklist_items.each_with_index do |item, ind|
|
||||
checklist.checklist_items << ChecklistItem.new(text: item, position: ind)
|
||||
end
|
||||
checklist.save
|
||||
|
||||
|
@ -1354,14 +1400,14 @@ module FirstTimeDataGenerator
|
|||
module_step_descriptions)
|
||||
|
||||
# Add file to existig step
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[7].protocol.steps.where('position = 0').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
file_name: 'ddCq-quantification_diagnostics-template.xls'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).add_step_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).add_step_asset(
|
||||
step: my_modules[7].protocol.steps.where('position = 0').take,
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1377,7 +1423,7 @@ module FirstTimeDataGenerator
|
|||
)
|
||||
|
||||
# Add result
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[7],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1386,7 +1432,7 @@ module FirstTimeDataGenerator
|
|||
file_name: 'ddCq-quantification_diagnostics-results.xls'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[7],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1395,7 +1441,7 @@ module FirstTimeDataGenerator
|
|||
file_name: 'dilution_curve-efficiency.JPG'
|
||||
)
|
||||
|
||||
DelayedUploaderDemo.delay(queue: :demo).generate_result_asset(
|
||||
DelayedUploaderDemo.delay(queue: asset_queue).generate_result_asset(
|
||||
my_module: my_modules[7],
|
||||
current_user: user,
|
||||
current_team: team,
|
||||
|
@ -1408,6 +1454,23 @@ module FirstTimeDataGenerator
|
|||
experiment.delay.generate_workflow_img
|
||||
end
|
||||
|
||||
# Used for delayed jobs
|
||||
def self.seed_demo_data_with_id(user_id, team_id)
|
||||
extend self
|
||||
user = User.find(user_id)
|
||||
team = Team.find(team_id)
|
||||
|
||||
unless user || team
|
||||
Rails.logger.warning("Could not retrieve user or team in " \
|
||||
"seed_demo_data_with_id. " \
|
||||
"User #{user_id} was mapped to #{user.inspect}." \
|
||||
"Team #{team_id} was mapped to #{team.inspect}.")
|
||||
return
|
||||
end
|
||||
|
||||
seed_demo_data(user, team, :new_demo_project)
|
||||
end
|
||||
|
||||
# WARNING: This only works on PostgreSQL
|
||||
def pluck_random(scope)
|
||||
scope.order('RANDOM()').first
|
||||
|
@ -1580,4 +1643,8 @@ module FirstTimeDataGenerator
|
|||
step_name: step.name)
|
||||
).sneaky_save
|
||||
end
|
||||
|
||||
def smart_annotate_rep_item(item)
|
||||
"#{item.name}~rep_item~#{Base62.encode(item.id)}"
|
||||
end
|
||||
end
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
<a href="#"
|
||||
id="system-notifications-dropdown"
|
||||
class="dropdown-toggle"
|
||||
title="System notifications"
|
||||
title="<%= t('system_notifications.navbar.tooltip') %>"
|
||||
data-toggle="dropdown"
|
||||
role="button"
|
||||
aria-haspopup="true"
|
||||
|
|
|
@ -20,16 +20,16 @@
|
|||
<div class="system-notifications-container">
|
||||
<%= render partial: "list", locals: { notifications: @system_notifications[:notifications] } %>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<% if @system_notifications[:more_notifications_url] && @system_notifications[:notifications].present? %>
|
||||
<a class="btn btn-default btn-more-notifications"
|
||||
<div class="text-center">
|
||||
<% if @system_notifications[:more_notifications_url] && @system_notifications[:notifications].present? %>
|
||||
<a class="btn btn-default btn-more-notifications"
|
||||
href="<%= @system_notifications[:more_notifications_url] %>"
|
||||
data-remote="true">
|
||||
<%= t("system_notifications.index.more_notifications") %></a>
|
||||
<% else %>
|
||||
<span class="no_notification_meessage"><%= t("system_notifications.index.no_notifications") %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
<% else %>
|
||||
<span class="no-notification-message"><%= t("system_notifications.index.no_notifications") %></span>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<%= javascript_include_tag("system_notifications/index") %>
|
||||
|
|
|
@ -152,7 +152,7 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-2">
|
||||
<span class="system-message hidden-sm"><i class="fas fa-chess-rook" aria-hidden="true"></i></span>
|
||||
<span class="system-message hidden-sm"><i class="fas fa-gift" aria-hidden="true"></i></span>
|
||||
</div>
|
||||
<div class="col-sm-10">
|
||||
<strong><%=t 'notifications.form.system_message' %></strong>
|
||||
|
|
|
@ -1292,6 +1292,8 @@ en:
|
|||
create:
|
||||
success_flash: "Successfully added sample group <strong>%{sample_group}</strong> to team <strong>%{team}</strong>."
|
||||
system_notifications:
|
||||
navbar:
|
||||
tooltip: 'What’s new notifications'
|
||||
emails:
|
||||
subject: "You've received a What's new notification"
|
||||
intro_paragraph: "Hi %{user_name}, you've received What's new notification in SciNote:"
|
||||
|
@ -1872,8 +1874,8 @@ en:
|
|||
assignments_description: 'Assignment notifications appear whenever you get assigned to a team, project, task.'
|
||||
recent_notification: 'Recent changes'
|
||||
recent_notification_description: 'Recent changes notifications appear whenever there is a change on a task you are assigned to.'
|
||||
system_message: 'System message'
|
||||
system_message_description: 'System message notifications are specifically sent by site maintainers to notify all users about a system update.'
|
||||
system_message: "What's New in SciNote"
|
||||
system_message_description: 'You will be notified about new SciNote features, releases and improvements you can benefit from.'
|
||||
deliver:
|
||||
download_link: "Download link:"
|
||||
download_text: "Click the link to download the file."
|
||||
|
|
16
db/migrate/20190227125306_add_unique_index_on_user_teams.rb
Normal file
16
db/migrate/20190227125306_add_unique_index_on_user_teams.rb
Normal file
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUniqueIndexOnUserTeams < ActiveRecord::Migration[5.1]
|
||||
def up
|
||||
# firstly delete the duplicates
|
||||
execute 'WITH uniq AS
|
||||
(SELECT DISTINCT ON (user_id, team_id) * FROM user_teams)
|
||||
DELETE FROM user_teams WHERE user_teams.id NOT IN
|
||||
(SELECT id FROM uniq)'
|
||||
add_index :user_teams, %i(user_id team_id), unique: true
|
||||
end
|
||||
|
||||
def down
|
||||
remove_index :user_teams, column: %i(user_id team_id)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,16 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class AddUniqueIndexOnUserProjects < ActiveRecord::Migration[5.1]
|
||||
def up
|
||||
# firstly delete the duplicates
|
||||
execute 'WITH uniq AS
|
||||
(SELECT DISTINCT ON (user_id, project_id) * FROM user_projects)
|
||||
DELETE FROM user_projects WHERE user_projects.id NOT IN
|
||||
(SELECT id FROM uniq)'
|
||||
add_index :user_projects, %i(user_id project_id), unique: true
|
||||
end
|
||||
|
||||
def down
|
||||
remove_index :user_projects, columns: %i(user_id project_id)
|
||||
end
|
||||
end
|
|
@ -10,7 +10,7 @@
|
|||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema.define(version: 20190125123107) do
|
||||
ActiveRecord::Schema.define(version: 20190227125352) do
|
||||
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
@ -796,6 +796,7 @@ ActiveRecord::Schema.define(version: 20190125123107) do
|
|||
t.bigint "assigned_by_id"
|
||||
t.index ["assigned_by_id"], name: "index_user_projects_on_assigned_by_id"
|
||||
t.index ["project_id"], name: "index_user_projects_on_project_id"
|
||||
t.index ["user_id", "project_id"], name: "index_user_projects_on_user_id_and_project_id", unique: true
|
||||
t.index ["user_id"], name: "index_user_projects_on_user_id"
|
||||
end
|
||||
|
||||
|
@ -821,6 +822,7 @@ ActiveRecord::Schema.define(version: 20190125123107) do
|
|||
t.bigint "assigned_by_id"
|
||||
t.index ["assigned_by_id"], name: "index_user_teams_on_assigned_by_id"
|
||||
t.index ["team_id"], name: "index_user_teams_on_team_id"
|
||||
t.index ["user_id", "team_id"], name: "index_user_teams_on_user_id_and_team_id", unique: true
|
||||
t.index ["user_id"], name: "index_user_teams_on_user_id"
|
||||
end
|
||||
|
||||
|
|
|
@ -131,23 +131,53 @@ namespace :data do
|
|||
end
|
||||
|
||||
desc 'Update all templates projects'
|
||||
task update_all_templates: :environment do
|
||||
task :update_all_templates,
|
||||
%i(slice_size) => [:environment] do |_, args|
|
||||
args.with_defaults(slice_size: 800)
|
||||
|
||||
Rails.logger.info('Templates, syncing all templates projects')
|
||||
updated, total = TemplatesService.new.update_all_templates
|
||||
Rails.logger.info(
|
||||
"Templates, total number of updated projects: #{updated} out of #{total}}"
|
||||
)
|
||||
Team.all.order(updated_at: :desc)
|
||||
.each_slice(args[:slice_size].to_i).with_index do |teams, i|
|
||||
Rails.logger.info("Processing slice with index #{i}. " \
|
||||
"First team: #{teams.first.id}, " \
|
||||
"Last team: #{teams.last.id}.")
|
||||
|
||||
teams.each do |team|
|
||||
TemplatesService.new.delay(
|
||||
run_at: i.hours.from_now,
|
||||
queue: :templates,
|
||||
priority: 5
|
||||
).update_team(team)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
desc 'Create demo project on existing users'
|
||||
task create_demo_project_on_existing_users: :environment do
|
||||
task :create_demo_project_on_existing_users,
|
||||
%i(slice_size) => [:environment] do |_, args|
|
||||
args.with_defaults(slice_size: 800)
|
||||
|
||||
require "#{Rails.root}/app/utilities/first_time_data_generator"
|
||||
include FirstTimeDataGenerator
|
||||
|
||||
Rails.logger.info('Creating demo project on existing users')
|
||||
|
||||
User.find_each do |user|
|
||||
seed_demo_data(user, user.teams.first)
|
||||
Team.all.order(updated_at: :desc)
|
||||
.each_slice(args[:slice_size]).with_index do |teams, i|
|
||||
Rails.logger.info("Processing slice with index #{i}. " \
|
||||
"First team: #{teams.first.id}, " \
|
||||
"Last team: #{teams.last.id}.")
|
||||
|
||||
teams.each do |team|
|
||||
owner_ut = team.user_teams.where(role: 2).first
|
||||
next unless owner_ut
|
||||
|
||||
FirstTimeDataGenerator.delay(
|
||||
run_at: i.hours.from_now,
|
||||
queue: :new_demo_project,
|
||||
priority: 10
|
||||
).seed_demo_data_with_id(owner_ut.user.id, team.id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -68,7 +68,6 @@ describe ClientApi::UserTeamService do
|
|||
|
||||
describe '#update_role!' do
|
||||
it 'should raise ClientApi::CustomUserTeamError if no role is set' do
|
||||
create :user_team, team: team_one, user: user_one
|
||||
ut_service = ClientApi::UserTeamService.new(
|
||||
user: user_one,
|
||||
team_id: team_one.id,
|
||||
|
|
1
vendor/assets/javascripts/select2.min.js
vendored
Normal file
1
vendor/assets/javascripts/select2.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
vendor/assets/stylesheets/select2.min.css
vendored
Normal file
1
vendor/assets/stylesheets/select2.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue