Merge branch 'master' into ml-sci-2940

This commit is contained in:
mlorb 2019-02-13 14:35:03 +01:00 committed by GitHub
commit 71e4cc4dbd
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 1223 additions and 443 deletions

View file

@ -350,6 +350,9 @@ Style/WhenThen:
Metrics/AbcSize:
Enabled: false
Metrics/BlockLength:
ExcludedMethods: ['describe', 'context']
Metrics/ClassLength:
Enabled: false

View file

@ -2,10 +2,12 @@ FROM ruby:2.4.5
MAINTAINER BioSistemika <info@biosistemika.com>
# additional dependecies
# libSSL-1.0 is required by wkhtmltopdf binary
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
apt-get update -qq && \
apt-get install -y \
libjemalloc1 \
libssl1.0-dev \
nodejs \
postgresql-client \
default-jre-headless \

View file

@ -2,10 +2,12 @@ FROM ruby:2.4.5
MAINTAINER BioSistemika <info@biosistemika.com>
# additional dependecies
# libSSL-1.0 is required by wkhtmltopdf binary
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && \
apt-get update -qq && \
apt-get install -y \
libjemalloc1 \
libssl1.0-dev \
nodejs \
groff-base \
awscli \

View file

@ -35,6 +35,7 @@
//= require underscore
//= require i18n.js
//= require i18n/translations
//= require users/settings/teams/invite_users_modal
//= require turbolinks

View file

@ -112,22 +112,34 @@
}
// Initialize no description edit link
function initEditNoDescription(){
var modal = "#edit-experiment-modal-";
$.each($(".experiment-no-description a"), function(){
var modal = '#edit-experiment-modal-';
$.each($('.experiment-no-description'), function() {
var id = modal + $(this).data("id");
initializeModal($(this), id);
});
}
// Bind modal to new-experiment action
initializeModal($("#new-experiment"), '#new-experiment-modal');
// Bind modal to big-plus new experiment actions
initializeModal('.big-plus', '#new-experiment-modal');
$(document).on('turbolinks:load', function() {
// Bind modal to new-experiment action
initializeModal($('#new-experiment'), '#new-experiment-modal');
// Bind modal to all actions listed on dropdown accesible from experiment
// panel
initializeDropdownActions();
// Bind modal to big-plus new experiment actions
initializeModal('.big-plus', '#new-experiment-modal');
// init
initEditNoDescription();
// Bind modal to new-exp-title action
initializeModal('.new-exp-title', '#new-experiment-modal');
// Bind modals to all clone-experiment actions
$.each($('.clone-experiment'), function() {
var id = $(this).closest('.experiment-panel').data('id');
initializeModal($(this), '#clone-experiment-modal-' + id);
});
// Bind modal to all actions listed on dropdown accesible from experiment
// panel
initializeDropdownActions();
// init
initEditNoDescription();
});
})();

View file

@ -11,7 +11,7 @@
I18n setupSidebarTree */
//= require comments
(function() {
(function(global) {
var newProjectModal = null;
var newProjectModalForm = null;
var newProjectModalBody = null;
@ -203,7 +203,7 @@
}
// Initialize users editing modal remote loading.
function initUsersEditLink($el) {
global.initUsersEditLink = function($el) {
$el.find('.manage-users-link')
.on('ajax:before', function() {
var projectId = $(this).closest('.panel-default').attr('id');
@ -771,4 +771,4 @@
initProjectsViewModeSwitch();
initSorting();
loadCardsView();
}());
}(window));

View file

@ -14,7 +14,7 @@
var timestamp = container.data("timestamp");
var img_url = container.data('updated-img');
animateSpinner(container, true);
animateSpinner(container, true, { color: '#555', top: '60%', zIndex: '100' });
checkUpdatedImg(img_url, url, timestamp, container);
}
@ -54,6 +54,7 @@
type: "GET",
dataType: "json",
success: function (data) {
el.children('img').remove();
el.append(data.workflowimg);
},
error: function (ev) {

View file

@ -270,7 +270,7 @@ function importProtocolFromFile(
element.getAttribute('fileref'));
var image_tag = "<img style='max-width:300px; max-height:300px;' src='data:" + element.children[1].innerHTML + ";base64," + assetBytes + "' />"
description = description.replace(match, image_tag); // replace the token with image
}).bind(this);
});
// I know is crazy but is the only way I found to pass valid HTML
return $('<div></div>').html(
description.replace('<!--[CDATA[ ', '')

View file

@ -1284,6 +1284,7 @@ var RepositoryDatatable = (function(global) {
});
reorderer.order(listIds, false);
loadColumnsNames();
initRowSelection();
}
});
$('.sorting').on('click', checkAvailableColumns);

View file

@ -1,6 +1,16 @@
(function() {
'use strict';
function reloadRecaptchaIfExists() {
if (typeof grecaptcha !== 'undefined') {
grecaptcha.reset();
}
}
window.recaptchaCallback = function recaptchaCallback(response) {
$('#recaptcha-invite-modal').val(response);
};
function initializeModal(modal) {
var modalDialog = modal.find('.modal-dialog');
var type = modal.attr('data-type');
@ -14,6 +24,9 @@
var teamSelectorDropdown = modal.find('[data-role=team-selector-dropdown]');
var teamSelectorDropdown2 = $();
var tagsInput = modal.find('[data-role=tags-input]');
var recaptchaInput = modal.find('#recaptcha-invite-modal');
var recaptchaErrorMsgDiv = modal.find('#recaptcha-error-msg');
var recaptchaErrorText = modal.find('#recaptcha-error-msg>span');
// Set max tags
tagsInput.tagsinput({
@ -85,7 +98,8 @@
// Click action
modal.find('[data-action=invite]').off('click').on('click', function() {
var data = {
emails: tagsInput.val()
emails: tagsInput.val(),
'g-recaptcha-response': recaptchaInput.val()
};
animateSpinner(modalDialog);
@ -130,16 +144,23 @@
// Add 'data-invited="true"' status to modal element
modal.attr('data-invited', 'true');
},
error: function() {
error: function(jqXHR) {
// ReCaptcha fails
if (jqXHR.status === 422) {
recaptchaErrorMsgDiv.removeClass('hidden');
recaptchaErrorText.text(jqXHR.responseJSON.recaptcha_error);
} else {
modal.modal('hide');
alert('Error inviting users.');
}
reloadRecaptchaIfExists();
animateSpinner(modalDialog, false);
modal.modal('hide');
alert('Error inviting users.');
}
});
});
}).on('shown.bs.modal', function() {
tagsInput.tagsinput('focus');
recaptchaErrorMsgDiv.addClass('hidden');
// Remove 'data-invited="true"' status
modal.removeAttr('data-invited');
}).on('hide.bs.modal', function() {
@ -150,6 +171,7 @@
inviteWithRoleBtn.attr('disabled', 'disabled');
teamSelectorDropdown2.addClass('disabled');
animateSpinner(modalDialog, false);
recaptchaErrorMsgDiv.addClass('hidden');
// Unbind event listeners
teamSelectorCheckbox.off('change');
@ -160,6 +182,8 @@
stepResultsDiv.html('');
stepResults.hide();
stepForm.show();
reloadRecaptchaIfExists();
recaptchaInput.val('');
});
}

View file

@ -1,4 +1,4 @@
@import url(https://fonts.googleapis.com/css?family=Open+Sans:400,600,700,400italic&subset=latin,latin-ext);
@import url(https://fonts.googleapis.com/css?family=Lato:400,400i,700&subset=latin-ext);
//==============================================================================
// Colors
@ -58,7 +58,7 @@ $link-hover-color: darken($link-color, 15%);
$link-hover-decoration: underline;
// Typography
$font-family-lato: lato, "Open Sans", Arial, Helvetica, sans-serif;
$font-family-lato: Lato, "Open Sans", Arial, Helvetica, sans-serif;
$font-family-sans-serif: "Open Sans", Arial, Helvetica, sans-serif;
$font-family-serif: Georgia, "Times New Roman", Times, serif;
$font-family-monospace: Menlo, Monaco, Consolas, "Courier New", monospace;

View file

@ -463,69 +463,110 @@ li.module-hover {
}
}
#project-show {
@media (min-width: 1400px) {
.col-md-6 {
width: 33.33%; // fallback if needed
width: calc(100% / 3);
}
}
}
// EXPERIMENT PANEL
.experiment-panel {
@include box-shadow(0 4px 8px 0 $color-dove-gray);
height: 550px;
display: flex;
flex-direction: column;
height: 400px;
margin: 0 auto;
margin-bottom: 35px;
margin-top: 45px;
max-width: 700px;
display: flex;
flex-direction: column;
.panel-title {
margin-bottom: 14px;
}
.panel-heading .clone-experiment,
.panel-heading .dropdown {
display: none;
}
&:hover .clone-experiment,
&:hover .dropdown {
display: block;
}
.panel-date {
color: $color-silver-chalice;
}
.panel-heading > .clone-experiment {
color: $color-silver-chalice;
padding-left: 4px;
padding-top: 2px;
}
.dropdown {
color: $color-silver-chalice;
button {
padding-left: 6px;
}
}
.panel-body {
display: flex;
flex: 1 1 auto;
flex-direction: column;
height: 100%;
a {
height: 100%;
}
}
.experiment-description {
border: 1px solid $color-alto;
border-radius: 4px;
height: 100px;
margin-top: 10px;
max-height: 86px;
overflow-x: hidden;
overflow-y: scroll;
padding: 10px;
p {
margin: 0;
}
}
.experiment-no-description {
color: $color-alto;
display: block;
font-size: $font-size-h4;
font-size: $font-size-large;
font-weight: bold;
margin-top: 10px;
text-align: center;
a {
color: $color-alto;
text-decoration: none;
}
}
.no-workflowimg {
color: $color-alto;
display: block;
font-size: 26px;
font-size: 22px;
font-weight: bold;
height: 300px;
margin: 15px 0;
padding-top: 150px;
max-height: 200px;
padding-bottom: 70px;
padding-top: 50px;
text-align: center;
a {
color: $color-alto;
text-decoration: none;
}
}
}
.workflowimg-container {
height: 300px;
margin: 15px 0;
align-items: center;
display: flex;
height: 100%;
justify-content: center;
img {
max-height: 300px;
max-height: 190px;
max-width: 100%;
}
}
@ -540,7 +581,7 @@ li.module-hover {
.big-plus {
color: $color-gainsboro;
display: block;
font-size: 250px;
font-size: 180px;
margin: 20px 0;
text-align: center;

View file

@ -652,7 +652,7 @@ ul.double-line > li {
}
.panel-default > .panel-heading {
background-color: $color-gainsboro;
background-color: $color-white;
&>.panel-title {
overflow: hidden;
@ -783,7 +783,9 @@ ul.double-line > li {
}
.manage-users-link {
display: block;
padding-left: 15px;
margin-bottom: 10px;
}
}
@ -1403,6 +1405,10 @@ table.dataTable {
margin: 0;
}
}
.g-recaptcha {
margin-top: 20px;
}
}
// Image preview modal

View file

@ -75,8 +75,10 @@ class ApplicationController < ActionController::Base
private
def update_current_team
if current_user.current_team_id.blank? &&
current_user.teams.count > 0
current_team = Team.find_by_id(current_user.current_team_id)
if (current_team.nil? || !current_user.is_member_of_team?(current_team)) &&
current_user.teams.count.positive?
current_user.update(
current_team_id: current_user.teams.first.id
)

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class ExperimentsController < ApplicationController
include SampleActions
include TeamsHelper
@ -183,37 +185,15 @@ class ExperimentsController < ApplicationController
# POST: clone_experiment(id)
def clone
project = Project.find_by_id(params[:experiment].try(:[], :project_id))
# Try to clone the experiment
success = true
if @experiment.projects_with_role_above_user(current_user).include?(project)
cloned_experiment = @experiment.deep_clone_to_project(current_user,
project)
success = cloned_experiment.valid?
# Create workflow image
cloned_experiment.delay.generate_workflow_img if success
else
success = false
end
if success
Activity.create(
type_of: :clone_experiment,
project: project,
experiment: @experiment,
user: current_user,
message: I18n.t(
'activities.clone_experiment',
user: current_user.full_name,
experiment_new: cloned_experiment.name,
experiment_original: @experiment.name
)
)
service = Experiments::CopyExperimentAsTemplateService
.call(experiment_id: @experiment.id,
project_id: move_experiment_param,
user_id: current_user.id)
if service.succeed?
flash[:success] = t('experiments.clone.success_flash',
experiment: @experiment.name)
redirect_to canvas_experiment_path(cloned_experiment)
redirect_to canvas_experiment_path(service.cloned_experiment)
else
flash[:error] = t('experiments.clone.error_flash',
experiment: @experiment.name)
@ -237,49 +217,23 @@ class ExperimentsController < ApplicationController
# POST: move_experiment(id)
def move
project = Project.find_by_id(params[:experiment].try(:[], :project_id))
old_project = @experiment.project
# Try to move the experiment
success = true
if @experiment.moveable_projects(current_user).include?(project)
success = @experiment.move_to_project(project)
else
success = false
end
if success
Activity.create(
type_of: :move_experiment,
project: project,
experiment: @experiment,
user: current_user,
message: I18n.t(
'activities.move_experiment',
user: current_user.full_name,
experiment: @experiment.name,
project_new: project.name,
project_original: old_project.name
)
)
service = Experiments::MoveToProjectService
.call(experiment_id: @experiment.id,
project_id: move_experiment_param,
user_id: current_user.id)
if service.succeed?
flash[:success] = t('experiments.move.success_flash',
experiment: @experiment.name)
respond_to do |format|
format.json do
render json: { path: canvas_experiment_url(@experiment) }, status: :ok
end
end
path = canvas_experiment_url(@experiment)
status = :ok
else
respond_to do |format|
format.json do
render json: { message: t('experiments.move.error_flash',
experiment:
escape_input(@experiment.name)) },
status: :unprocessable_entity
end
end
message = t('experiments.move.error_flash',
experiment: escape_input(@experiment.name))
status = :unprocessable_entity
end
render json: { message: message, path: path }, status: status
end
def module_archive
@ -348,6 +302,10 @@ class ExperimentsController < ApplicationController
params.require(:experiment).permit(:name, :description, :archived)
end
def move_experiment_param
params.require(:experiment).require(:project_id)
end
def load_projects_tree
# Switch to correct team
current_team_switch(@experiment.project.team) unless @experiment.project.nil?

View file

@ -574,8 +574,10 @@ class ProtocolsController < ApplicationController
transaction_error = false
Protocol.transaction do
begin
protocol = import_new_protocol(@protocol_json, @team, @type, current_user)
rescue Exception
protocol =
import_new_protocol(@protocol_json, @team, @type, current_user)
rescue StandardError => ex
Rails.logger.error ex.message
transaction_error = true
raise ActiveRecord:: Rollback
end

View file

@ -229,8 +229,7 @@ class TeamsController < ApplicationController
def export_projects
unless export_proj_requests_exceeded?
current_user.export_vars['num_of_export_all_last_24_hours'] += 1
current_user.save
current_user.increase_daily_exports_counter!
generate_export_projects_zip

View file

@ -5,6 +5,8 @@ module Users
prepend_before_action :check_captcha, only: [:update]
prepend_before_action :check_captcha_for_invite, only: [:invite_users]
before_action :check_invite_users_permission, only: :invite_users
before_action :update_sanitized_params, only: :update
@ -188,6 +190,15 @@ module Users
end
end
def check_captcha_for_invite
if Rails.configuration.x.enable_recaptcha
unless verify_recaptcha
render json: { recaptcha_error: t('invite_users.errors.recaptcha') },
status: :unprocessable_entity
end
end
end
def check_invite_users_permission
@user = current_user
@emails = params[:emails].map(&:downcase)

View file

@ -242,12 +242,12 @@ class Asset < ApplicationRecord
file_path = fa.path
end
unless Yomu.class_eval('@@server_pid')
Yomu.server(:text, nil)
sleep(5)
# Start Tika as a server
if !ENV['NO_TIKA_SERVER'] && Yomu.class_variable_get(:@@server_pid).nil?
Yomu.server(:text)
end
y = Yomu.new file_path
y = Yomu.new file_path
text_data = y.text
if asset.asset_text_datum.present?

View file

@ -225,54 +225,6 @@ class Experiment < ApplicationRecord
Experiments::GenerateWorkflowImageService.call(experiment_id: id)
end
# Clone this experiment to given project
def deep_clone_to_project(current_user, project)
# First we have to find unique name for our little experiment
experiment_names = project.experiments.map(&:name)
format = 'Clone %d - %s'
i = 1
i += 1 while experiment_names.include?(format(format, i, name))
clone = Experiment.new(
name: format(format, i, name).truncate(Constants::NAME_MAX_LENGTH),
description: description,
created_by: current_user,
last_modified_by: current_user,
project: project
)
# Copy all workflows
my_module_groups.each do |g|
clone.my_module_groups << g.deep_clone_to_experiment(current_user, clone)
end
# Copy modules without group
clone.my_modules << my_modules.without_group.map do |m|
m.deep_clone_to_experiment(current_user, clone)
end
clone.save
clone
end
def move_to_project(project)
self.project = project
my_modules.each do |m|
new_tags = []
m.tags.each do |t|
new_tags << t.deep_clone_to_project(project)
end
m.my_module_tags.destroy_all
project.tags << new_tags
m.tags << new_tags
end
result = save
touch(:workflowimg_updated_at) if result
result
end
# Get projects where user is either owner or user in the same team
# as this experiment
def projects_with_role_above_user(current_user)

View file

@ -246,6 +246,8 @@ class Project < ApplicationRecord
parsed_page_html = Nokogiri::HTML(page_html_string)
parsed_html = parsed_page_html.at_css('#report-content')
# Style tables (mimick frontend processing)
tables = parsed_html.css('.hot-table-contents')
.zip(parsed_html.css('.hot-table-container'))
tables.each do |table_input, table_container|

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
class Tag < ApplicationRecord
include SearchableModel
@ -48,7 +50,10 @@ class Tag < ApplicationRecord
end
end
def deep_clone_to_project(project)
def clone_to_project_or_return_existing(project)
tag = Tag.find_by(project: project, name: name, color: color)
return tag if tag
Tag.create(
name: name,
color: color,

View file

@ -129,6 +129,18 @@ class TeamZipExport < ZipExport
)
file = FileUtils.touch("#{project_path}/#{html_name}").first
File.open(file, 'wb') { |f| f.write(project_report_pdf) }
# Add Handsontable and dependent JS files (mimick frontend formula
# processing).
required_js = %w(handsontable.full.min.js lodash.js numeral.js numeric.js
md5.js jstat.js formula.js parser.js ruleJS.js
handsontable.formula.js big.min.js)
required_js.each do |filename|
filepath = File.join(Rails.root,
"vendor/assets/javascripts/#{filename}/")
dest_folder = "#{project_path}/"
FileUtils.cp(filepath, dest_folder)
end
end
# Change current dir outside tmp_dir, since tmp_dir will be deleted

View file

@ -56,7 +56,8 @@ class User < ApplicationRecord
default_variables(
export_vars: {
num_of_export_all_last_24_hours: 0
num_of_export_all_last_24_hours: 0,
last_export_timestamp: Date.today.to_time.to_i
}
)
@ -505,6 +506,16 @@ class User < ApplicationRecord
end
end
def increase_daily_exports_counter!
if Time.at(export_vars['last_export_timestamp'] || 0).to_date == Date.today
export_vars['num_of_export_all_last_24_hours'] += 1
else
export_vars['last_export_timestamp'] = Date.today.to_time.to_i
export_vars['num_of_export_all_last_24_hours'] = 1
end
save
end
protected
def confirmation_required?

View file

@ -1,6 +1,6 @@
class JsonbHashSerializer
def self.dump(hash)
hash.to_json
hash.nil? ? '{}' : hash.to_json
end
def self.load(hash)

View file

@ -0,0 +1,103 @@
# frozen_string_literal: true
module Experiments
class CopyExperimentAsTemplateService
extend Service
attr_reader :errors, :c_exp
alias cloned_experiment c_exp
def initialize(experiment_id:, project_id:, user_id:)
@exp = Experiment.find experiment_id
@project = Project.find project_id
@user = User.find user_id
@original_project = @exp&.project
@c_exp = nil
@errors = {}
end
def call
return self unless valid?
ActiveRecord::Base.transaction do
@c_exp = Experiment.new(
name: find_uniq_name,
description: @exp.description,
created_by: @user,
last_modified_by: @user,
project: @project
)
# Copy all signle taskas
@c_exp.my_modules << @exp.my_modules.without_group.map do |m|
m.deep_clone_to_experiment(@user, @c_exp)
end
# Copy all grouped tasks
@exp.my_module_groups.each do |g|
@c_exp.my_module_groups << g.deep_clone_to_experiment(@user, @c_exp)
end
raise ActiveRecord::Rollback unless @c_exp.save
end
@errors.merge!(@c_exp.errors.to_hash) unless @c_exp.valid?
@c_exp = nil unless succeed?
@c_exp.delay.generate_workflow_img if succeed?
track_activity if succeed?
self
end
def succeed?
@errors.none?
end
private
def find_uniq_name
experiment_names = @project.experiments.map(&:name)
format = 'Clone %d - %s'
free_index = 1
free_index += 1 while experiment_names
.include?(format(format, free_index, @exp.name))
format(format, free_index, @exp.name).truncate(Constants::NAME_MAX_LENGTH)
end
def valid?
unless @exp && @project && @user
@errors[:invalid_arguments] =
{ 'experiment': @exp,
'project': @project,
'user': @user }
.map do |key, value|
"Can't find #{key.capitalize}" if value.nil?
end.compact
return false
end
if @exp.projects_with_role_above_user(@user).include?(@project)
true
else
@errors[:user_without_permissions] =
['You are not allowed to copy this experiment to this project']
false
end
end
def track_activity
Activity.create(
type_of: :clone_experiment,
project: @project,
experiment: @exp,
user: @user,
message: I18n.t(
'activities.clone_experiment',
user: @user,
experiment_new: @c_exp.name,
experiment_original: @exp.name
)
)
end
end
end

View file

@ -0,0 +1,91 @@
# frozen_string_literal: true
module Experiments
class MoveToProjectService
extend Service
attr_reader :errors
def initialize(experiment_id:, project_id:, user_id:)
@exp = Experiment.find experiment_id
@project = Project.find project_id
@user = User.find user_id
@original_project = @exp&.project
@errors = {}
end
def call
return self unless valid?
ActiveRecord::Base.transaction do
@exp.project = @project
@exp.my_modules.each do |m|
new_tags = m.tags.map do |t|
t.clone_to_project_or_return_existing(@project)
end
m.my_module_tags.delete_all
m.tags = new_tags
end
raise ActiveRecord::Rollback unless @exp.save
# To pass the ExperimentsController#updated_img check
@exp.update(workflowimg_updated_at: @exp.updated_at)
end
@errors.merge!(@exp.errors.to_hash) unless @exp.valid?
track_activity if succeed?
self
end
def succeed?
@errors.none?
end
private
def valid?
unless @exp && @project && @user
@errors[:invalid_arguments] =
{ 'experiment': @exp,
'project': @project,
'user': @user }
.map do |key, value|
"Can't find #{key.capitalize}" if value.nil?
end.compact
return false
end
e = Experiment.find_by(name: @exp.name, project: @project)
if e
@errors[:project_with_exp] =
['Project already contains experiment with this name']
false
elsif !@exp.moveable_projects(@user).include?(@project)
@errors[:target_project_not_valid] =
['Experiment cannot be moved to this project']
false
else
true
end
end
def track_activity
Activity.create(
type_of: :move_experiment,
project: @project,
experiment: @exp,
user: @user,
message: I18n.t(
'activities.move_experiment',
user: @user,
experiment: @exp.name,
project_new: @project.name,
project_original: @original_project.name
)
)
end
end
end

View file

@ -1,5 +1,5 @@
<div class="panel panel-default module-large
<%= get_task_alert_color(my_module) %>
<%= get_task_alert_color(my_module) %>"
id="<%= my_module.id %>"
data-module-id="<%= my_module.id %>"
data-module-name="<%= my_module.name %>"
@ -9,6 +9,7 @@
data-module-x="<%= my_module.x %>"
data-module-y="<%= my_module.y %>"
data-module-conns="<%= construct_module_connections(my_module) %>"
data-module-users-tab-url="<%= my_module_user_my_modules_url(my_module_id: my_module.id, format: :json) %>"
data-module-tags-url="<%= my_module_tags_experiment_path(my_module.experiment, format: :json) %>">
<% if can_manage_module?(my_module) %>

View file

@ -31,7 +31,7 @@
tooltipcontent: I18n.t('tooltips.text.experiment.move')
} %></li>
<% end %>
<% if can_manage_experiment?(experiment) %>
<% if can_archive_experiment?(experiment) %>
<li><%= link_to t('experiments.archive.label_title'),
archive_experiment_url(experiment),
type: 'button',

View file

@ -44,8 +44,6 @@
<% @project.sorted_active_experiments(@current_sort).each_with_index do |experiment, index| %>
<%= render partial: 'projects/show/experiment',
locals: { experiment: experiment } %>
<%= content_tag(:div, '', class: 'clearfix visible-lg-block') if (index + 1) % 2 == 0 %>
<% end %>
<% if can_create_experiments?(@project) %>
<%= render 'projects/show/new_experiment' %>

View file

@ -17,50 +17,68 @@
<%= render partial: 'experiments/dropdown_actions.html.erb',
locals: { project: @project, experiment: experiment } %>
</div>
<% end %>
<h3 class="panel-title"><%= link_to experiment.name, canvas_experiment_path(experiment) %></h3>
</div>
<div class="panel-body">
<% if experiment.active_modules.length > 0 %>
<div class="workflowimg-container"
data-check-img="<%= updated_img_experiment_url(experiment) %>"
data-updated-img="<%= fetch_workflow_img_experiment_url(experiment) %>"
data-timestamp="<%= experiment.updated_at %>" >
</div>
<% else %>
<div class="no-workflowimg">
<% if can_manage_experiment?(experiment) %>
<p><%= link_to( t('experiments.edit.add_task'),
canvas_experiment_path(experiment,
editMode: true)) %></p>
<% else %>
<p><%= t('experiments.edit.no_workflowimg') %></p>
<% if can_clone_experiment?(experiment) %>
<%= link_to clone_modal_experiment_url(experiment),
remote: true, type: 'button',
class: 'clone-experiment help_tooltips pull-right',
data: {
tooltiplink: I18n.t('tooltips.link.experiment.copy'),
tooltipcontent: I18n.t('tooltips.text.experiment.copy')
} do %>
<span class="fas fa-copy"></span>&nbsp;
<%= t('experiments.clone.label_title') %>
<% end %>
</div>
<% end %>
<% end %>
<span class="help_tooltips" data-tooltiplink="<%= I18n.t('tooltips.link.experiment.dates') %>" data-tooltipcontent="<%= I18n.t('tooltips.text.experiment.dates') %>">
<div class="panel-title"><%= link_to experiment.name, canvas_experiment_path(experiment) %></div>
<span class="help_tooltips panel-date" data-tooltiplink="<%= I18n.t('tooltips.link.experiment.dates') %>" data-tooltipcontent="<%= I18n.t('tooltips.text.experiment.dates') %>">
<span class="fas fa-calendar-alt" aria-hidden="true"></span>
<%= l(experiment.created_at, format: :full_date) %> - <%= l(experiment.updated_at, format: :full_date) %>
</span>
<div data-hook="experiment-card-body">
<div data-hook="experiment-card-description">
<% if experiment.description? %>
<div class='experiment-description'>
<%= custom_auto_link(experiment.description, team: current_team) %>
</div>
<% else %>
<span class='experiment-no-description'>
<% if can_manage_experiment?(experiment) %>
<%= link_to t('experiments.edit.add-description'),
edit_project_experiment_url(@project, experiment),
remote: true,
data: { id: experiment.id } %>
<% else %>
<p><%= t('experiments.edit.no-description') %></p>
<% end %>
</span>
<% if can_manage_experiment?(experiment) %>
<%= link_to t('experiments.edit.add-description'),
edit_project_experiment_url(@project, experiment),
remote: true,
data: { id: experiment.id },
class: 'experiment-no-description' %>
<% else %>
<p class='experiment-no-description'><%= t('experiments.edit.no-description') %></p>
<% end %>
<% end %>
</div>
</div>
<div class="panel-body">
<% if experiment.active_modules.length > 0 %>
<%= link_to canvas_experiment_path(experiment) do %>
<div class="workflowimg-container"
data-check-img="<%= updated_img_experiment_url(experiment) %>"
data-updated-img="<%= fetch_workflow_img_experiment_url(experiment) %>"
data-timestamp="<%= experiment.updated_at %>" >
</div>
<% end %>
<% else %>
<% if can_manage_experiment?(experiment) %>
<%= link_to( t('experiments.edit.add_task'),
canvas_experiment_path(experiment, editMode: true),
class: 'no-workflowimg') %>
<% else %>
<div class="no-workflowimg">
<p><%= t('experiments.edit.no_workflowimg') %></p>
</div>
<% end %>
<% end %>
</div>
</div>
</div>

View file

@ -1,9 +1,10 @@
<div class="col-md-6">
<div class="panel panel-default experiment-panel">
<div class="panel-heading">
<h3 class="panel-title"><%= t('experiments.new.modal_title') %></h3>
<%= link_to t('experiments.new.modal_title'),
new_project_experiment_url(@project),
remote: true,
class: 'panel-title new-exp-title' %>
</div>
<div class="panel-body">
<%= link_to new_project_experiment_url(@project),

View file

@ -1,5 +1,4 @@
<%= link_to image_tag(@experiment.workflowimg.expiring_url(
<%= image_tag(@experiment.workflowimg.expiring_url(
Constants::URL_SHORT_EXPIRE_TIME
),
class: 'img-responsive center-block'),
canvas_experiment_path(@experiment) %>
class: 'img-responsive center-block') %>

View file

@ -99,6 +99,16 @@ invite_to_team = type.in?(%w(invite_to_team invite_to_team_with_role))
</div>
<% end %>
<% end %>
<% if ENV['ENABLE_RECAPTCHA'] %>
<div id="recaptcha-service" class="g-recaptcha"
data-callback="recaptchaCallback"
data-sitekey=<%= ENV['RECAPTCHA_SITE_KEY'] %>></div>
<script type="text/javascript" src="https://www.google.com/recaptcha/api.js?hl=en"></script>
<input type="hidden" id="recaptcha-invite-modal" value="">
<div class="form-group has-error hidden" id="recaptcha-error-msg" >
<span class="has-error help-block"></span>
</div>
<% end %>
</div>
<div class="results-container" data-role="step-results" data-clear="true">
</div>

View file

@ -10,6 +10,41 @@
.force_encoding(Encoding::UTF_8)
.html_safe %>
</style>
<script src="handsontable.full.min.js"></script>
<!-- Libraries for formulas -->
<script src="lodash.js"></script>
<script src="numeral.js"></script>
<script src="numeric.js"></script>
<script src="md5.js"></script>
<script src="jstat.js"></script>
<script src="formula.js"></script>
<script src="parser.js"></script>
<script src="ruleJS.js"></script>
<script src="handsontable.formula.js"></script>
<script src="big.min.js"></script>
<!-- Init Handsontables -->
<script>
window.onload = function() {
var tables = document.getElementsByClassName('hot-table-container');
var tableVals = document.getElementsByClassName('hot-table-contents');
for (i = 0; i < tables.length; i++) {
tables[i].innerHTML=''
new Handsontable(tables[i], {
data: JSON.parse(tableVals[i].value).data,
rowHeaders: true,
colHeaders: true,
filters: true,
dropdownMenu: true,
formulas: true
});
}
};
</script>
<title><%= title %></title>
</head>
<body class='print-report-body'>
@ -17,4 +52,4 @@
<%= content.force_encoding(Encoding::UTF_8).html_safe %>
</div>
</body>
</html>
</html>

View file

@ -1,6 +1,6 @@
<h5 class="text-center"><%= t("projects.index.users_tab") %></h5>
<hr>
<ul class="no-style content-users">
<ul class="no-style content-users" data-hook="project-users-tab-list">
<% if @users.size == 0 then %>
<li><em><%= t 'projects.index.no_users' %></em></li>
<% else %>

View file

@ -137,5 +137,4 @@
<%= render partial: 'users/settings/user_teams/destroy_user_team_modal.html.erb' %>
<%= stylesheet_link_tag 'datatables' %>
<%= javascript_include_tag 'users/settings/teams/show' %>
<%= javascript_include_tag 'users/settings/teams/invite_users_modal' %>
<span data-hook="team-bottom"></span>

View file

@ -769,7 +769,7 @@ en:
label_title: 'Archive'
clone:
modal_title: 'Copy experiment %{experiment} as template'
label_title: 'Copy as template (only Protocols steps copied)'
label_title: 'Copy as template'
modal_submit: 'Copy'
success_flash: 'Successfully copied experiment %{experiment} as template.'
error_flash: 'Could not copy the experiment as template.'
@ -1831,6 +1831,8 @@ en:
invite_guest: "As Guests"
invite_user: "As Normal Users"
invite_admin: "As Administrators"
errors:
recaptcha: "reCAPTCHA verification failed, please try again"
results:
heading: "Invitation results:"
user_exists: "User is already a member of SciNote."

View file

@ -0,0 +1,195 @@
# frozen_string_literal: true
class ChangeIndicesFromIntToBigint < ActiveRecord::Migration[5.1]
def up
drop_view :datatables_teams
change_column :activities, :id, :bigint
change_column :assets, :id, :bigint
change_column :asset_text_data, :id, :bigint
change_column :checklist_items, :id, :bigint
change_column :checklists, :id, :bigint
change_column :comments, :id, :bigint
change_column :connections, :id, :bigint
change_column :custom_fields, :id, :bigint
change_column :delayed_jobs, :id, :bigint
change_column :experiments, :id, :bigint
change_column :my_module_groups, :id, :bigint
change_column :my_module_repository_rows, :id, :bigint
change_column :my_modules, :id, :bigint
change_column :my_module_tags, :id, :bigint
change_column :notifications, :id, :bigint
change_column :projects, :id, :bigint
change_column :protocol_keywords, :id, :bigint
change_column :protocol_protocol_keywords, :id, :bigint
change_column :protocols, :id, :bigint
change_column :report_elements, :id, :bigint
change_column :reports, :id, :bigint
change_column :repositories, :id, :bigint
change_column :repository_table_states, :id, :bigint
change_column :result_assets, :id, :bigint
change_column :results, :id, :bigint
change_column :result_tables, :id, :bigint
change_column :result_texts, :id, :bigint
change_column :sample_custom_fields, :id, :bigint
change_column :sample_groups, :id, :bigint
change_column :sample_my_modules, :id, :bigint
change_column :samples, :id, :bigint
change_column :samples_tables, :id, :bigint
change_column :sample_types, :id, :bigint
change_column :settings, :id, :bigint
change_column :step_assets, :id, :bigint
change_column :steps, :id, :bigint
change_column :step_tables, :id, :bigint
change_column :tables, :id, :bigint
change_column :tags, :id, :bigint
change_column :teams, :id, :bigint
change_column :temp_files, :id, :bigint
change_column :tiny_mce_assets, :id, :bigint
change_column :tokens, :id, :bigint
change_column :user_identities, :id, :bigint
change_column :user_my_modules, :id, :bigint
change_column :user_notifications, :id, :bigint
change_column :user_projects, :id, :bigint
change_column :users, :id, :bigint
change_column :user_teams, :id, :bigint
change_column :wopi_actions, :id, :bigint
change_column :wopi_apps, :id, :bigint
change_column :wopi_discoveries, :id, :bigint
change_column :zip_exports, :id, :bigint
change_column :activities, :user_id, :bigint
change_column :activities, :my_module_id, :bigint
change_column :activities, :project_id, :bigint
change_column :activities, :experiment_id, :bigint
change_column :assets, :created_by_id, :bigint
change_column :assets, :last_modified_by_id, :bigint
change_column :asset_text_data, :asset_id, :bigint
change_column :checklist_items, :checklist_id, :bigint
change_column :checklist_items, :last_modified_by_id, :bigint
change_column :checklist_items, :created_by_id, :bigint
change_column :checklists, :step_id, :bigint
change_column :checklists, :created_by_id, :bigint
change_column :checklists, :last_modified_by_id, :bigint
change_column :comments, :last_modified_by_id, :bigint
change_column :comments, :user_id, :bigint
change_column :connections, :output_id, :bigint
change_column :connections, :input_id, :bigint
change_column :custom_fields, :team_id, :bigint
change_column :custom_fields, :user_id, :bigint
change_column :custom_fields, :last_modified_by_id, :bigint
change_column :experiments, :created_by_id, :bigint
change_column :experiments, :restored_by_id, :bigint
change_column :experiments, :archived_by_id, :bigint
change_column :experiments, :last_modified_by_id, :bigint
change_column :my_module_groups, :experiment_id, :bigint
change_column :my_module_groups, :created_by_id, :bigint
change_column :my_module_repository_rows, :assigned_by_id, :bigint
change_column :my_modules, :last_modified_by_id, :bigint
change_column :my_modules, :created_by_id, :bigint
change_column :my_modules, :experiment_id, :bigint
change_column :my_modules, :my_module_group_id, :bigint
change_column :my_modules, :archived_by_id, :bigint
change_column :my_modules, :restored_by_id, :bigint
change_column :my_module_tags, :created_by_id, :bigint
change_column :notifications, :generator_user_id, :bigint
change_column :oauth_access_grants, :resource_owner_id, :bigint
change_column :oauth_access_tokens, :resource_owner_id, :bigint
change_column :projects, :team_id, :bigint
change_column :projects, :archived_by_id, :bigint
change_column :projects, :restored_by_id, :bigint
change_column :projects, :created_by_id, :bigint
change_column :projects, :last_modified_by_id, :bigint
change_column :protocol_keywords, :team_id, :bigint
change_column :protocol_protocol_keywords, :protocol_keyword_id, :bigint
change_column :protocol_protocol_keywords, :protocol_id, :bigint
change_column :protocols, :team_id, :bigint
change_column :protocols, :added_by_id, :bigint
change_column :protocols, :parent_id, :bigint
change_column :protocols, :restored_by_id, :bigint
change_column :protocols, :archived_by_id, :bigint
change_column :protocols, :my_module_id, :bigint
change_column :report_elements, :report_id, :bigint
change_column :report_elements, :experiment_id, :bigint
change_column :report_elements, :table_id, :bigint
change_column :report_elements, :asset_id, :bigint
change_column :report_elements, :checklist_id, :bigint
change_column :report_elements, :result_id, :bigint
change_column :report_elements, :step_id, :bigint
change_column :report_elements, :my_module_id, :bigint
change_column :report_elements, :project_id, :bigint
change_column :reports, :user_id, :bigint
change_column :reports, :last_modified_by_id, :bigint
change_column :reports, :project_id, :bigint
change_column :repositories, :created_by_id, :bigint
change_column :repository_columns, :created_by_id, :bigint
change_column :repository_date_values, :last_modified_by_id, :bigint
change_column :repository_date_values, :created_by_id, :bigint
change_column :repository_rows, :last_modified_by_id, :bigint
change_column :repository_rows, :created_by_id, :bigint
change_column :repository_text_values, :created_by_id, :bigint
change_column :repository_text_values, :last_modified_by_id, :bigint
change_column :result_assets, :result_id, :bigint
change_column :result_assets, :asset_id, :bigint
change_column :results, :archived_by_id, :bigint
change_column :results, :user_id, :bigint
change_column :results, :last_modified_by_id, :bigint
change_column :results, :restored_by_id, :bigint
change_column :results, :my_module_id, :bigint
change_column :result_tables, :result_id, :bigint
change_column :result_tables, :table_id, :bigint
change_column :result_texts, :result_id, :bigint
change_column :sample_custom_fields, :custom_field_id, :bigint
change_column :sample_custom_fields, :sample_id, :bigint
change_column :sample_groups, :team_id, :bigint
change_column :sample_groups, :last_modified_by_id, :bigint
change_column :sample_groups, :created_by_id, :bigint
change_column :sample_my_modules, :my_module_id, :bigint
change_column :sample_my_modules, :sample_id, :bigint
change_column :sample_my_modules, :assigned_by_id, :bigint
change_column :samples, :sample_group_id, :bigint
change_column :samples, :last_modified_by_id, :bigint
change_column :samples, :sample_type_id, :bigint
change_column :samples, :team_id, :bigint
change_column :samples, :user_id, :bigint
change_column :sample_types, :created_by_id, :bigint
change_column :sample_types, :team_id, :bigint
change_column :sample_types, :last_modified_by_id, :bigint
change_column :step_assets, :asset_id, :bigint
change_column :step_assets, :step_id, :bigint
change_column :steps, :last_modified_by_id, :bigint
change_column :steps, :user_id, :bigint
change_column :steps, :protocol_id, :bigint
change_column :step_tables, :step_id, :bigint
change_column :step_tables, :table_id, :bigint
change_column :tables, :created_by_id, :bigint
change_column :tables, :last_modified_by_id, :bigint
change_column :tags, :last_modified_by_id, :bigint
change_column :tags, :created_by_id, :bigint
change_column :tags, :project_id, :bigint
change_column :teams, :last_modified_by_id, :bigint
change_column :teams, :created_by_id, :bigint
change_column :tokens, :user_id, :bigint
change_column :user_my_modules, :my_module_id, :bigint
change_column :user_my_modules, :assigned_by_id, :bigint
change_column :user_my_modules, :user_id, :bigint
change_column :user_notifications, :user_id, :bigint
change_column :user_notifications, :notification_id, :bigint
change_column :user_projects, :assigned_by_id, :bigint
change_column :user_projects, :user_id, :bigint
change_column :user_projects, :project_id, :bigint
change_column :users, :current_team_id, :bigint
change_column :user_teams, :user_id, :bigint
change_column :user_teams, :team_id, :bigint
change_column :user_teams, :assigned_by_id, :bigint
change_column :wopi_actions, :wopi_app_id, :bigint
change_column :wopi_apps, :wopi_discovery_id, :bigint
change_column :zip_exports, :user_id, :bigint
create_view :datatables_teams
end
def down
# Bad, bad things can happen if we reverse this migration, so we'll
# simply skip it
end
end

View file

@ -10,22 +10,22 @@
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20181212162649) do
ActiveRecord::Schema.define(version: 20190117155006) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
enable_extension "pg_trgm"
enable_extension "btree_gist"
create_table "activities", id: :serial, force: :cascade do |t|
t.integer "my_module_id"
t.integer "user_id"
create_table "activities", force: :cascade do |t|
t.bigint "my_module_id"
t.bigint "user_id"
t.integer "type_of", null: false
t.string "message", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "project_id", null: false
t.integer "experiment_id"
t.bigint "project_id", null: false
t.bigint "experiment_id"
t.index ["created_at"], name: "index_activities_on_created_at"
t.index ["experiment_id"], name: "index_activities_on_experiment_id"
t.index ["my_module_id"], name: "index_activities_on_my_module_id"
@ -34,9 +34,9 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["user_id"], name: "index_activities_on_user_id"
end
create_table "asset_text_data", id: :serial, force: :cascade do |t|
create_table "asset_text_data", force: :cascade do |t|
t.text "data", null: false
t.integer "asset_id", null: false
t.bigint "asset_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.tsvector "data_vector"
@ -44,15 +44,15 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["data_vector"], name: "index_asset_text_data_on_data_vector", using: :gin
end
create_table "assets", id: :serial, force: :cascade do |t|
create_table "assets", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "file_file_name"
t.string "file_content_type"
t.integer "file_file_size"
t.datetime "file_updated_at"
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.integer "estimated_size", default: 0, null: false
t.boolean "file_present", default: false, null: false
t.string "lock", limit: 1024
@ -67,14 +67,14 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["team_id"], name: "index_assets_on_team_id"
end
create_table "checklist_items", id: :serial, force: :cascade do |t|
create_table "checklist_items", force: :cascade do |t|
t.string "text", null: false
t.boolean "checked", default: false, null: false
t.integer "checklist_id", null: false
t.bigint "checklist_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.integer "position"
t.index "trim_html_tags((text)::text) gin_trgm_ops", name: "index_checklist_items_on_text", using: :gin
t.index ["checklist_id"], name: "index_checklist_items_on_checklist_id"
@ -82,25 +82,25 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["last_modified_by_id"], name: "index_checklist_items_on_last_modified_by_id"
end
create_table "checklists", id: :serial, force: :cascade do |t|
create_table "checklists", force: :cascade do |t|
t.string "name", null: false
t.integer "step_id", null: false
t.bigint "step_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_checklists_on_name", using: :gin
t.index ["created_by_id"], name: "index_checklists_on_created_by_id"
t.index ["last_modified_by_id"], name: "index_checklists_on_last_modified_by_id"
t.index ["step_id"], name: "index_checklists_on_step_id"
end
create_table "comments", id: :serial, force: :cascade do |t|
create_table "comments", force: :cascade do |t|
t.string "message", null: false
t.integer "user_id", null: false
t.bigint "user_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "last_modified_by_id"
t.bigint "last_modified_by_id"
t.string "type"
t.integer "associated_id"
t.index "trim_html_tags((message)::text) gin_trgm_ops", name: "index_comments_on_message", using: :gin
@ -111,26 +111,26 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["user_id"], name: "index_comments_on_user_id"
end
create_table "connections", id: :serial, force: :cascade do |t|
t.integer "input_id", null: false
t.integer "output_id", null: false
create_table "connections", force: :cascade do |t|
t.bigint "input_id", null: false
t.bigint "output_id", null: false
t.index ["input_id"], name: "index_connections_on_input_id"
t.index ["output_id"], name: "index_connections_on_output_id"
end
create_table "custom_fields", id: :serial, force: :cascade do |t|
create_table "custom_fields", force: :cascade do |t|
t.string "name", null: false
t.integer "user_id", null: false
t.integer "team_id", null: false
t.bigint "user_id", null: false
t.bigint "team_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "last_modified_by_id"
t.bigint "last_modified_by_id"
t.index ["last_modified_by_id"], name: "index_custom_fields_on_last_modified_by_id"
t.index ["team_id"], name: "index_custom_fields_on_team_id"
t.index ["user_id"], name: "index_custom_fields_on_user_id"
end
create_table "delayed_jobs", id: :serial, force: :cascade do |t|
create_table "delayed_jobs", force: :cascade do |t|
t.integer "priority", default: 0, null: false
t.integer "attempts", default: 0, null: false
t.text "handler", null: false
@ -146,16 +146,16 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["queue"], name: "delayed_jobs_queue"
end
create_table "experiments", id: :serial, force: :cascade do |t|
create_table "experiments", force: :cascade do |t|
t.string "name", null: false
t.text "description"
t.integer "project_id", null: false
t.integer "created_by_id", null: false
t.integer "last_modified_by_id", null: false
t.bigint "created_by_id", null: false
t.bigint "last_modified_by_id", null: false
t.boolean "archived", default: false, null: false
t.integer "archived_by_id"
t.bigint "archived_by_id"
t.datetime "archived_on"
t.integer "restored_by_id"
t.bigint "restored_by_id"
t.datetime "restored_on"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@ -171,53 +171,53 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["restored_by_id"], name: "index_experiments_on_restored_by_id"
end
create_table "my_module_groups", id: :serial, force: :cascade do |t|
create_table "my_module_groups", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "created_by_id"
t.integer "experiment_id", default: 0, null: false
t.bigint "created_by_id"
t.bigint "experiment_id", default: 0, null: false
t.index ["created_by_id"], name: "index_my_module_groups_on_created_by_id"
t.index ["experiment_id"], name: "index_my_module_groups_on_experiment_id"
end
create_table "my_module_repository_rows", id: :serial, force: :cascade do |t|
create_table "my_module_repository_rows", force: :cascade do |t|
t.bigint "repository_row_id", null: false
t.integer "my_module_id"
t.integer "assigned_by_id", null: false
t.bigint "assigned_by_id", null: false
t.datetime "created_at"
t.datetime "updated_at"
t.index ["my_module_id", "repository_row_id"], name: "index_my_module_ids_repository_row_ids"
t.index ["repository_row_id"], name: "index_my_module_repository_rows_on_repository_row_id"
end
create_table "my_module_tags", id: :serial, force: :cascade do |t|
create_table "my_module_tags", force: :cascade do |t|
t.integer "my_module_id"
t.integer "tag_id"
t.integer "created_by_id"
t.bigint "created_by_id"
t.index ["created_by_id"], name: "index_my_module_tags_on_created_by_id"
t.index ["my_module_id"], name: "index_my_module_tags_on_my_module_id"
t.index ["tag_id"], name: "index_my_module_tags_on_tag_id"
end
create_table "my_modules", id: :serial, force: :cascade do |t|
create_table "my_modules", force: :cascade do |t|
t.string "name", null: false
t.datetime "due_date"
t.string "description"
t.integer "x", default: 0, null: false
t.integer "y", default: 0, null: false
t.integer "my_module_group_id"
t.bigint "my_module_group_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "archived", default: false, null: false
t.datetime "archived_on"
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.integer "archived_by_id"
t.integer "restored_by_id"
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.bigint "archived_by_id"
t.bigint "restored_by_id"
t.datetime "restored_on"
t.integer "nr_of_assigned_samples", default: 0
t.integer "workflow_order", default: -1, null: false
t.integer "experiment_id", default: 0, null: false
t.bigint "experiment_id", default: 0, null: false
t.integer "state", limit: 2, default: 0
t.datetime "completed_on"
t.index "trim_html_tags((description)::text) gin_trgm_ops", name: "index_my_modules_on_description", using: :gin
@ -230,18 +230,18 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["restored_by_id"], name: "index_my_modules_on_restored_by_id"
end
create_table "notifications", id: :serial, force: :cascade do |t|
create_table "notifications", force: :cascade do |t|
t.string "title"
t.string "message"
t.integer "type_of", null: false
t.integer "generator_user_id"
t.bigint "generator_user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["created_at"], name: "index_notifications_on_created_at"
end
create_table "oauth_access_grants", force: :cascade do |t|
t.integer "resource_owner_id", null: false
t.bigint "resource_owner_id", null: false
t.bigint "application_id", null: false
t.string "token", null: false
t.integer "expires_in", null: false
@ -254,7 +254,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do
end
create_table "oauth_access_tokens", force: :cascade do |t|
t.integer "resource_owner_id"
t.bigint "resource_owner_id"
t.bigint "application_id"
t.text "token", null: false
t.string "refresh_token"
@ -281,19 +281,19 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["uid"], name: "index_oauth_applications_on_uid", unique: true
end
create_table "projects", id: :serial, force: :cascade do |t|
create_table "projects", force: :cascade do |t|
t.string "name", null: false
t.integer "visibility", default: 0, null: false
t.datetime "due_date"
t.integer "team_id", null: false
t.bigint "team_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "archived", default: false, null: false
t.datetime "archived_on"
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.integer "archived_by_id"
t.integer "restored_by_id"
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.bigint "archived_by_id"
t.bigint "restored_by_id"
t.datetime "restored_on"
t.string "experiments_order"
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_projects_on_name", using: :gin
@ -304,36 +304,36 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["team_id"], name: "index_projects_on_team_id"
end
create_table "protocol_keywords", id: :serial, force: :cascade do |t|
create_table "protocol_keywords", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "nr_of_protocols", default: 0
t.integer "team_id", null: false
t.bigint "team_id", null: false
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_protocol_keywords_on_name", using: :gin
t.index ["team_id"], name: "index_protocol_keywords_on_team_id"
end
create_table "protocol_protocol_keywords", id: :serial, force: :cascade do |t|
t.integer "protocol_id", null: false
t.integer "protocol_keyword_id", null: false
create_table "protocol_protocol_keywords", force: :cascade do |t|
t.bigint "protocol_id", null: false
t.bigint "protocol_keyword_id", null: false
t.index ["protocol_id"], name: "index_protocol_protocol_keywords_on_protocol_id"
t.index ["protocol_keyword_id"], name: "index_protocol_protocol_keywords_on_protocol_keyword_id"
end
create_table "protocols", id: :serial, force: :cascade do |t|
create_table "protocols", force: :cascade do |t|
t.string "name"
t.text "authors"
t.text "description"
t.integer "added_by_id"
t.integer "my_module_id"
t.integer "team_id", null: false
t.bigint "added_by_id"
t.bigint "my_module_id"
t.bigint "team_id", null: false
t.integer "protocol_type", default: 0, null: false
t.integer "parent_id"
t.bigint "parent_id"
t.datetime "parent_updated_at"
t.integer "archived_by_id"
t.bigint "archived_by_id"
t.datetime "archived_on"
t.integer "restored_by_id"
t.bigint "restored_by_id"
t.datetime "restored_on"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@ -351,22 +351,22 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["team_id"], name: "index_protocols_on_team_id"
end
create_table "report_elements", id: :serial, force: :cascade do |t|
create_table "report_elements", force: :cascade do |t|
t.integer "position", null: false
t.integer "type_of", null: false
t.integer "sort_order", default: 0
t.integer "report_id"
t.bigint "report_id"
t.integer "parent_id"
t.integer "project_id"
t.integer "my_module_id"
t.integer "step_id"
t.integer "result_id"
t.integer "checklist_id"
t.integer "asset_id"
t.integer "table_id"
t.bigint "project_id"
t.bigint "my_module_id"
t.bigint "step_id"
t.bigint "result_id"
t.bigint "checklist_id"
t.bigint "asset_id"
t.bigint "table_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "experiment_id"
t.bigint "experiment_id"
t.integer "repository_id"
t.index ["asset_id"], name: "index_report_elements_on_asset_id"
t.index ["checklist_id"], name: "index_report_elements_on_checklist_id"
@ -381,14 +381,14 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["table_id"], name: "index_report_elements_on_table_id"
end
create_table "reports", id: :serial, force: :cascade do |t|
create_table "reports", force: :cascade do |t|
t.string "name", null: false
t.string "description"
t.integer "project_id", null: false
t.integer "user_id", null: false
t.bigint "project_id", null: false
t.bigint "user_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "last_modified_by_id"
t.bigint "last_modified_by_id"
t.bigint "team_id"
t.index "trim_html_tags((description)::text) gin_trgm_ops", name: "index_reports_on_description", using: :gin
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_reports_on_name", using: :gin
@ -398,9 +398,9 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["user_id"], name: "index_reports_on_user_id"
end
create_table "repositories", id: :serial, force: :cascade do |t|
create_table "repositories", force: :cascade do |t|
t.integer "team_id"
t.integer "created_by_id", null: false
t.bigint "created_by_id", null: false
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
@ -434,7 +434,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do
create_table "repository_columns", force: :cascade do |t|
t.integer "repository_id"
t.integer "created_by_id", null: false
t.bigint "created_by_id", null: false
t.string "name"
t.integer "data_type", null: false
t.datetime "created_at"
@ -446,8 +446,8 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.datetime "data"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "created_by_id", null: false
t.integer "last_modified_by_id", null: false
t.bigint "created_by_id", null: false
t.bigint "last_modified_by_id", null: false
end
create_table "repository_list_items", force: :cascade do |t|
@ -478,8 +478,8 @@ ActiveRecord::Schema.define(version: 20181212162649) do
create_table "repository_rows", force: :cascade do |t|
t.integer "repository_id"
t.integer "created_by_id", null: false
t.integer "last_modified_by_id", null: false
t.bigint "created_by_id", null: false
t.bigint "last_modified_by_id", null: false
t.string "name"
t.datetime "created_at"
t.datetime "updated_at"
@ -487,7 +487,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["repository_id"], name: "index_repository_rows_on_repository_id"
end
create_table "repository_table_states", id: :serial, force: :cascade do |t|
create_table "repository_table_states", force: :cascade do |t|
t.jsonb "state", null: false
t.integer "user_id", null: false
t.integer "repository_id", null: false
@ -501,41 +501,41 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.string "data"
t.datetime "created_at"
t.datetime "updated_at"
t.integer "created_by_id", null: false
t.integer "last_modified_by_id", null: false
t.bigint "created_by_id", null: false
t.bigint "last_modified_by_id", null: false
t.index "trim_html_tags((data)::text) gin_trgm_ops", name: "index_repository_text_values_on_data", using: :gin
end
create_table "result_assets", id: :serial, force: :cascade do |t|
t.integer "result_id", null: false
t.integer "asset_id", null: false
create_table "result_assets", force: :cascade do |t|
t.bigint "result_id", null: false
t.bigint "asset_id", null: false
t.index ["result_id", "asset_id"], name: "index_result_assets_on_result_id_and_asset_id"
end
create_table "result_tables", id: :serial, force: :cascade do |t|
t.integer "result_id", null: false
t.integer "table_id", null: false
create_table "result_tables", force: :cascade do |t|
t.bigint "result_id", null: false
t.bigint "table_id", null: false
t.index ["result_id", "table_id"], name: "index_result_tables_on_result_id_and_table_id"
end
create_table "result_texts", id: :serial, force: :cascade do |t|
create_table "result_texts", force: :cascade do |t|
t.string "text", null: false
t.integer "result_id", null: false
t.bigint "result_id", null: false
t.index "trim_html_tags((text)::text) gin_trgm_ops", name: "index_result_texts_on_text", using: :gin
t.index ["result_id"], name: "index_result_texts_on_result_id"
end
create_table "results", id: :serial, force: :cascade do |t|
create_table "results", force: :cascade do |t|
t.string "name"
t.integer "my_module_id", null: false
t.integer "user_id", null: false
t.bigint "my_module_id", null: false
t.bigint "user_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "archived", default: false, null: false
t.datetime "archived_on"
t.integer "last_modified_by_id"
t.integer "archived_by_id"
t.integer "restored_by_id"
t.bigint "last_modified_by_id"
t.bigint "archived_by_id"
t.bigint "restored_by_id"
t.datetime "restored_on"
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_results_on_name", using: :gin
t.index ["archived_by_id"], name: "index_results_on_archived_by_id"
@ -546,10 +546,10 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["user_id"], name: "index_results_on_user_id"
end
create_table "sample_custom_fields", id: :serial, force: :cascade do |t|
create_table "sample_custom_fields", force: :cascade do |t|
t.string "value", null: false
t.integer "custom_field_id", null: false
t.integer "sample_id"
t.bigint "custom_field_id", null: false
t.bigint "sample_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index "trim_html_tags((value)::text) gin_trgm_ops", name: "index_sample_custom_fields_on_value", using: :gin
@ -557,52 +557,52 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["sample_id"], name: "index_sample_custom_fields_on_sample_id"
end
create_table "sample_groups", id: :serial, force: :cascade do |t|
create_table "sample_groups", force: :cascade do |t|
t.string "name", null: false
t.string "color", default: "#ff0000", null: false
t.integer "team_id", null: false
t.bigint "team_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_sample_groups_on_name", using: :gin
t.index ["created_by_id"], name: "index_sample_groups_on_created_by_id"
t.index ["last_modified_by_id"], name: "index_sample_groups_on_last_modified_by_id"
t.index ["team_id"], name: "index_sample_groups_on_team_id"
end
create_table "sample_my_modules", id: :serial, force: :cascade do |t|
t.integer "sample_id", null: false
t.integer "my_module_id", null: false
t.integer "assigned_by_id"
create_table "sample_my_modules", force: :cascade do |t|
t.bigint "sample_id", null: false
t.bigint "my_module_id", null: false
t.bigint "assigned_by_id"
t.datetime "assigned_on"
t.index ["assigned_by_id"], name: "index_sample_my_modules_on_assigned_by_id"
t.index ["my_module_id"], name: "index_sample_my_modules_on_my_module_id"
t.index ["sample_id", "my_module_id"], name: "index_sample_my_modules_on_sample_id_and_my_module_id"
end
create_table "sample_types", id: :serial, force: :cascade do |t|
create_table "sample_types", force: :cascade do |t|
t.string "name", null: false
t.integer "team_id", null: false
t.bigint "team_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_sample_types_on_name", using: :gin
t.index ["created_by_id"], name: "index_sample_types_on_created_by_id"
t.index ["last_modified_by_id"], name: "index_sample_types_on_last_modified_by_id"
t.index ["team_id"], name: "index_sample_types_on_team_id"
end
create_table "samples", id: :serial, force: :cascade do |t|
create_table "samples", force: :cascade do |t|
t.string "name", null: false
t.integer "user_id", null: false
t.integer "team_id", null: false
t.bigint "user_id", null: false
t.bigint "team_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "sample_group_id"
t.integer "sample_type_id"
t.integer "last_modified_by_id"
t.bigint "sample_group_id"
t.bigint "sample_type_id"
t.bigint "last_modified_by_id"
t.integer "nr_of_modules_assigned_to", default: 0
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_samples_on_name", using: :gin
t.index ["last_modified_by_id"], name: "index_samples_on_last_modified_by_id"
@ -612,7 +612,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["user_id"], name: "index_samples_on_user_id"
end
create_table "samples_tables", id: :serial, force: :cascade do |t|
create_table "samples_tables", force: :cascade do |t|
t.jsonb "status", default: {"time"=>0, "order"=>[[2, "desc"]], "start"=>0, "length"=>10, "search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "columns"=>[{"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}, {"search"=>{"regex"=>false, "smart"=>true, "search"=>"", "caseInsensitive"=>true}, "visible"=>true}], "assigned"=>"all", "ColReorder"=>[0, 1, 2, 3, 4, 5, 6]}, null: false
t.integer "user_id", null: false
t.integer "team_id", null: false
@ -622,35 +622,35 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["user_id"], name: "index_samples_tables_on_user_id"
end
create_table "settings", id: :serial, force: :cascade do |t|
create_table "settings", force: :cascade do |t|
t.text "type", null: false
t.jsonb "values", default: {}, null: false
t.index ["type"], name: "index_settings_on_type", unique: true
end
create_table "step_assets", id: :serial, force: :cascade do |t|
t.integer "step_id", null: false
t.integer "asset_id", null: false
create_table "step_assets", force: :cascade do |t|
t.bigint "step_id", null: false
t.bigint "asset_id", null: false
t.index ["step_id", "asset_id"], name: "index_step_assets_on_step_id_and_asset_id"
end
create_table "step_tables", id: :serial, force: :cascade do |t|
t.integer "step_id", null: false
t.integer "table_id", null: false
create_table "step_tables", force: :cascade do |t|
t.bigint "step_id", null: false
t.bigint "table_id", null: false
t.index ["step_id", "table_id"], name: "index_step_tables_on_step_id_and_table_id", unique: true
end
create_table "steps", id: :serial, force: :cascade do |t|
create_table "steps", force: :cascade do |t|
t.string "name"
t.string "description"
t.integer "position", null: false
t.boolean "completed", null: false
t.datetime "completed_on"
t.integer "user_id", null: false
t.bigint "user_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "last_modified_by_id"
t.integer "protocol_id", null: false
t.bigint "last_modified_by_id"
t.bigint "protocol_id", null: false
t.index "trim_html_tags((description)::text) gin_trgm_ops", name: "index_steps_on_description", using: :gin
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_steps_on_name", using: :gin
t.index ["created_at"], name: "index_steps_on_created_at"
@ -660,12 +660,12 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["user_id"], name: "index_steps_on_user_id"
end
create_table "tables", id: :serial, force: :cascade do |t|
create_table "tables", force: :cascade do |t|
t.binary "contents", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.tsvector "data_vector"
t.string "name", default: ""
t.integer "team_id"
@ -677,26 +677,26 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["team_id"], name: "index_tables_on_team_id"
end
create_table "tags", id: :serial, force: :cascade do |t|
create_table "tags", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "color", default: "#ff0000", null: false
t.integer "project_id", null: false
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.bigint "project_id", null: false
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.index "trim_html_tags((name)::text) gin_trgm_ops", name: "index_tags_on_name", using: :gin
t.index ["created_by_id"], name: "index_tags_on_created_by_id"
t.index ["last_modified_by_id"], name: "index_tags_on_last_modified_by_id"
t.index ["project_id"], name: "index_tags_on_project_id"
end
create_table "teams", id: :serial, force: :cascade do |t|
create_table "teams", force: :cascade do |t|
t.string "name", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "created_by_id"
t.integer "last_modified_by_id"
t.bigint "created_by_id"
t.bigint "last_modified_by_id"
t.string "description"
t.bigint "space_taken", default: 1048576, null: false
t.index ["created_by_id"], name: "index_teams_on_created_by_id"
@ -704,7 +704,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["name"], name: "index_teams_on_name"
end
create_table "temp_files", id: :serial, force: :cascade do |t|
create_table "temp_files", force: :cascade do |t|
t.string "session_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@ -714,7 +714,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.datetime "file_updated_at"
end
create_table "tiny_mce_assets", id: :serial, force: :cascade do |t|
create_table "tiny_mce_assets", force: :cascade do |t|
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
@ -730,13 +730,13 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["team_id"], name: "index_tiny_mce_assets_on_team_id"
end
create_table "tokens", id: :serial, force: :cascade do |t|
create_table "tokens", force: :cascade do |t|
t.string "token", null: false
t.integer "ttl", null: false
t.integer "user_id", null: false
t.bigint "user_id", null: false
end
create_table "user_identities", id: :serial, force: :cascade do |t|
create_table "user_identities", force: :cascade do |t|
t.integer "user_id"
t.string "provider", null: false
t.string "uid", null: false
@ -747,20 +747,20 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["user_id"], name: "index_user_identities_on_user_id"
end
create_table "user_my_modules", id: :serial, force: :cascade do |t|
t.integer "user_id", null: false
t.integer "my_module_id", null: false
create_table "user_my_modules", force: :cascade do |t|
t.bigint "user_id", null: false
t.bigint "my_module_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "assigned_by_id"
t.bigint "assigned_by_id"
t.index ["assigned_by_id"], name: "index_user_my_modules_on_assigned_by_id"
t.index ["my_module_id"], name: "index_user_my_modules_on_my_module_id"
t.index ["user_id"], name: "index_user_my_modules_on_user_id"
end
create_table "user_notifications", id: :serial, force: :cascade do |t|
t.integer "user_id"
t.integer "notification_id"
create_table "user_notifications", force: :cascade do |t|
t.bigint "user_id"
t.bigint "notification_id"
t.boolean "checked", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
@ -769,31 +769,31 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["user_id"], name: "index_user_notifications_on_user_id"
end
create_table "user_projects", id: :serial, force: :cascade do |t|
create_table "user_projects", force: :cascade do |t|
t.integer "role"
t.integer "user_id", null: false
t.integer "project_id", null: false
t.bigint "user_id", null: false
t.bigint "project_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "assigned_by_id"
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"], name: "index_user_projects_on_user_id"
end
create_table "user_teams", id: :serial, force: :cascade do |t|
create_table "user_teams", force: :cascade do |t|
t.integer "role", default: 1, null: false
t.integer "user_id", null: false
t.integer "team_id", null: false
t.bigint "user_id", null: false
t.bigint "team_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "assigned_by_id"
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"], name: "index_user_teams_on_user_id"
end
create_table "users", id: :serial, force: :cascade do |t|
create_table "users", force: :cascade do |t|
t.string "full_name", null: false
t.string "initials", null: false
t.string "email", default: "", null: false
@ -824,7 +824,7 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.string "invited_by_type"
t.integer "invited_by_id"
t.integer "invitations_count", default: 0
t.integer "current_team_id"
t.bigint "current_team_id"
t.string "authentication_token", limit: 30
t.jsonb "settings", default: {}, null: false
t.jsonb "variables", default: {}, null: false
@ -849,21 +849,21 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.index ["viewable_type", "viewable_id"], name: "index_view_states_on_viewable_type_and_viewable_id"
end
create_table "wopi_actions", id: :serial, force: :cascade do |t|
create_table "wopi_actions", force: :cascade do |t|
t.string "action", null: false
t.string "extension", null: false
t.string "urlsrc", null: false
t.integer "wopi_app_id", null: false
t.bigint "wopi_app_id", null: false
t.index ["extension", "action"], name: "index_wopi_actions_on_extension_and_action"
end
create_table "wopi_apps", id: :serial, force: :cascade do |t|
create_table "wopi_apps", force: :cascade do |t|
t.string "name", null: false
t.string "icon", null: false
t.integer "wopi_discovery_id", null: false
t.bigint "wopi_discovery_id", null: false
end
create_table "wopi_discoveries", id: :serial, force: :cascade do |t|
create_table "wopi_discoveries", force: :cascade do |t|
t.integer "expires", null: false
t.string "proof_key_mod", null: false
t.string "proof_key_exp", null: false
@ -871,8 +871,8 @@ ActiveRecord::Schema.define(version: 20181212162649) do
t.string "proof_key_old_exp", null: false
end
create_table "zip_exports", id: :serial, force: :cascade do |t|
t.integer "user_id"
create_table "zip_exports", force: :cascade do |t|
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "zip_file_file_name"

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
namespace :exportable_items do
desc 'Removes exportable zip files'
task cleanup: :environment do
@ -6,23 +8,4 @@ namespace :exportable_items do
puts "All exportable zip files older than " \
"'#{num.days.ago}' have been removed"
end
desc 'Resets export project counter to 0'
task reset_export_projects_counter: :environment do
User.find_each do |user|
User.transaction do
begin
user.export_vars['num_of_export_all_last_24_hours'] = 0
user.save
rescue ActiveRecord::ActiveRecordError,
ArgumentError,
ActiveRecord::RecordNotSaved => e
puts "Error resetting users num_of_export_all_last_24_hours " \
"variable to 0, transaction reverted: #{e}"
end
end
end
puts 'Export project counter successfully ' \
'reset on all users'
end
end

10
pull_request_template.md Normal file
View file

@ -0,0 +1,10 @@
Jira ticket: [SCI-xxxx](https://biosistemika.atlassian.net/browse/SCI-xxxx)
### What was done
_In a sentence or two describe what was done, focus on the tech aspect_
#### ToDo:
_(Optional) If you have discovered an oppurtunity for refactor or another task that was not part of the jira issue, please write it here_
### Note:
_(Optional) Note any other remarks, problems you were facing during the development, suggestion etc_

View file

@ -54,11 +54,15 @@ describe ProjectsController, type: :controller do
end
end
# rubocop:disable Security/Eval
# rubocop:disable Style/EvalWithLocation
(1..PROJECTS_CNT).each do |i|
let!("user_projects_#{i}") do
create :user_project, project: eval("project_#{i}"), user: user
create :user_project, :owner, project: eval("project_#{i}"), user: user
end
end
# rubocop:enable Security/Eval
# rubocop:enable Style/EvalWithLocation
describe '#index' do
context 'in JSON format' do

View file

@ -12,7 +12,7 @@ FactoryBot.define do
project { create :project, created_by: user }
factory :experiment_with_tasks do
after(:create) do |e|
create_list :my_module, 3, experiment: e
create_list :my_module, 3, :with_tag, experiment: e
end
end
end

View file

@ -8,5 +8,8 @@ FactoryBot.define do
workflow_order { MyModule.where(experiment_id: experiment.id).count + 1 }
experiment
my_module_group { create :my_module_group, experiment: experiment }
trait :with_tag do
tags { create_list :tag, 3, project: experiment.project }
end
end
end

9
spec/factories/tags.rb Normal file
View file

@ -0,0 +1,9 @@
# frozen_string_literal: true
FactoryBot.define do
factory :tag do
sequence(:name) { |n| "My tag-#{n}" }
project
color { Faker::Color.hex_color }
end
end

View file

@ -7,5 +7,8 @@ FactoryBot.define do
description { Faker::Lorem.sentence }
space_taken { 1048576 }
without_intro_demo true
trait :with_members do
users { create_list :user, 3 }
end
end
end

View file

@ -1,5 +0,0 @@
FactoryBot.define do
factory :user_project do
role 'owner'
end
end

View file

@ -0,0 +1,21 @@
# frozen_string_literal: true
FactoryBot.define do
factory :user_project do
user
project
assigned_by { user }
trait :owner do
role { UserProject.roles[:owner] }
end
trait :normal_user do
role { UserProject.roles[:normal_user] }
end
trait :technician do
role { UserProject.roles[:technician] }
end
trait :viewer do
role { UserProject.roles[:viewer] }
end
end
end

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'rails_helper'
describe Experiment, type: :model do

View file

@ -1,3 +1,5 @@
# frozen_string_literal: true
require 'rails_helper'
describe Tag, type: :model do
@ -23,15 +25,46 @@ describe Tag, type: :model do
it { should have_many :my_modules }
end
describe 'Should be a valid object' do
it { should validate_presence_of :name }
it { should validate_presence_of :color }
it { should validate_presence_of :project }
it do
should validate_length_of(:name).is_at_most(Constants::NAME_MAX_LENGTH)
describe 'Validations' do
describe '#name' do
it { should validate_presence_of :name }
it do
should validate_length_of(:name)
.is_at_most(Constants::NAME_MAX_LENGTH)
end
end
it do
should validate_length_of(:color).is_at_most(Constants::COLOR_MAX_LENGTH)
describe '#color' do
it { should validate_presence_of :color }
it do
should validate_length_of(:color)
.is_at_most(Constants::COLOR_MAX_LENGTH)
end
end
describe '#projects' do
it { should validate_presence_of :project }
end
end
describe '.clone_to_project_or_return_existing' do
let(:project) { create :project }
let(:tag) { create :tag }
context 'when tag does not exits' do
it 'does create new tag for project' do
expect do
tag.clone_to_project_or_return_existing(project)
end.to(change { Tag.where(project_id: project.id).count })
end
end
context 'when tag already exists' do
it 'return existing tag for project' do
Tag.create(name: tag.name, color: tag.color, project: project)
expect do
tag.clone_to_project_or_return_existing(project)
end.to_not(change { Tag.where(project_id: project.id).count })
end
end
end
end

View file

@ -187,7 +187,9 @@ describe User, type: :model do
describe '#last_activities' do
let!(:user) { create :user }
let!(:project) { create :project }
let!(:user_projects) { create :user_project, project: project, user: user }
let!(:user_projects) do
create :user_project, :viewer, project: project, user: user
end
let!(:activity_one) { create :activity, user: user, project: project }
let!(:activity_two) { create :activity, user: user, project: project }
@ -198,4 +200,62 @@ describe User, type: :model do
expect(activities).to include activity_two
end
end
describe '#increase_daily_exports_counter!' do
let(:user) { create :user }
context 'when last_export_timestamp is set' do
it 'increases counter by 1' do
expect { user.increase_daily_exports_counter! }
.to change {
user.reload.export_vars['num_of_export_all_last_24_hours']
}.from(0).to(1)
end
it 'sets last_export_timestamp on today' do
user.export_vars['last_export_timestamp'] = Date.yesterday.to_time.to_i
user.save
expect { user.increase_daily_exports_counter! }
.to change {
user.reload.export_vars['last_export_timestamp']
}.to(Date.today.to_time.to_i)
end
it 'sets new counter for today' do
user.export_vars = {
'num_of_export_all_last_24_hours': 3,
'last_export_timestamp': Date.yesterday.to_time.to_i
}
user.save
expect { user.increase_daily_exports_counter! }
.to change {
user.reload.export_vars['num_of_export_all_last_24_hours']
}.from(3).to(1)
end
end
context 'when last_export_timestamp not exists (existing users)' do
it 'sets last_export_timestamp on today' do
user.export_vars.delete('last_export_timestamp')
user.save
expect { user.increase_daily_exports_counter! }
.to change {
user.reload.export_vars['last_export_timestamp']
}.from(nil).to(Date.today.to_time.to_i)
end
it 'starts count reports with 1' do
user.export_vars.delete('last_export_timestamp')
user.export_vars['num_of_export_all_last_24_hours'] = 2
user.save
expect { user.increase_daily_exports_counter! }
.to change {
user.reload.export_vars['num_of_export_all_last_24_hours']
}.from(2).to(1)
end
end
end
end

View file

@ -4,9 +4,9 @@ require 'rails_helper'
RSpec.describe 'Api::V1::UsersController', type: :request do
before :all do
@user1 = create(:user, email: Faker::Internet.unique.email)
@user2 = create(:user, email: Faker::Internet.unique.email)
@user3 = create(:user, email: Faker::Internet.unique.email)
@user1 = create(:user)
@user2 = create(:user)
@user3 = create(:user)
@team1 = create(:team, created_by: @user1)
@team2 = create(:team, created_by: @user2)
@team3 = create(:team, created_by: @user3)
@ -40,7 +40,7 @@ RSpec.describe 'Api::V1::UsersController', type: :request do
it 'When invalid request, non existing user' do
hash_body = nil
get api_v1_user_path(id: 123), headers: @valid_headers
get api_v1_user_path(id: -1), headers: @valid_headers
expect(response).to have_http_status(403)
expect { hash_body = json }.not_to raise_exception
expect(hash_body['errors'][0]).to include('status': 403)

View file

@ -0,0 +1,60 @@
# frozen_string_literal: true
require 'rails_helper'
describe Experiments::CopyExperimentAsTemplateService do
let(:team) { create :team, :with_members }
let(:user_project) { create :user_project, :normal_user, user: user }
let(:project) do
create :project, team: team
end
let(:new_project) do
create :project, team: team, user_projects: [user_project]
end
let(:experiment) do
create :experiment_with_tasks, name: 'MyExp', project: project
end
let(:user) { create :user }
let(:service_call) do
Experiments::CopyExperimentAsTemplateService
.call(experiment_id: experiment.id,
project_id: new_project.id,
user_id: user.id)
end
context 'when service call is successful' do
it 'adds new experiment to target project' do
expect { service_call }.to(change { new_project.experiments.count })
end
it 'adds Activity record' do
expect { service_call }.to(change { Activity.all.count })
end
it 'copies all tasks to new experiment' do
expect(service_call.cloned_experiment.my_modules.count)
.to be == experiment.my_modules.count
end
it 'copies all task groups to new experiment' do
expect(service_call.cloned_experiment.my_module_groups.count)
.to be == experiment.my_module_groups.count
end
end
context 'when service call is not successful' do
it 'returns an error when can\'t find experiment' do
allow(Experiment).to receive(:find).and_return(nil)
expect(service_call.errors).to have_key(:invalid_arguments)
end
it 'returns error when user don\'t have permissions' do
expect_any_instance_of(Experiment)
.to receive(:projects_with_role_above_user).and_return([])
expect(service_call.errors).not_to be_empty
end
end
end

View file

@ -0,0 +1,98 @@
# frozen_string_literal: true
require 'rails_helper'
describe Experiments::MoveToProjectService do
let(:team) { create :team, :with_members }
let(:project) do
create :project, team: team, user_projects: []
end
let(:new_project) do
create :project, team: team, user_projects: [user_project2]
end
let(:experiment) do
create :experiment_with_tasks, name: 'MyExp', project: project
end
let(:user) { create :user }
let(:user_project2) { create :user_project, :normal_user, user: user }
let(:service_call) do
Experiments::MoveToProjectService.call(experiment_id: experiment.id,
project_id: new_project.id,
user_id: user.id)
end
context 'when call with valid params' do
it 'unnasigns experiment from project' do
service_call
expect(project.experiments.pluck(:id)).to_not include(experiment.id)
end
it 'assigns experiment to new_project' do
service_call
expect(new_project.experiments.pluck(:id)).to include(experiment.id)
end
it 'copies tags to new project' do
expect { service_call }.to(change { new_project.tags.count })
end
it 'leaves tags on an old project' do
experiment # explicit call to create tags
expect { service_call }.not_to(change { project.tags.count })
end
it 'sets new project tags to modules' do
service_call
new_tags = experiment.my_modules.map { |m| m.tags.map { |t| t } }.flatten
tags_project_id = new_tags.map(&:project_id).uniq.first
expect(tags_project_id).to be == new_project.id
end
it 'adds Activity record' do
expect { service_call }.to(change { Activity.all.count })
end
end
context 'when call with invalid params' do
it 'returns an error when can\'t find user and project' do
allow(Project).to receive(:find).and_return(nil)
allow(User).to receive(:find).and_return(nil)
expect(service_call.errors).to have_key(:invalid_arguments)
end
it 'returns an error on validate' do
FactoryBot.create :experiment, project: new_project, name: 'MyExp'
expect(service_call.succeed?).to be_falsey
end
it 'returns an error on saving' do
expect_any_instance_of(Experiments::MoveToProjectService)
.to receive(:valid?)
.and_return(true)
FactoryBot.create :experiment, project: new_project, name: 'MyExp'
expect(service_call.succeed?).to be_falsey
end
it 'rollbacks cloned tags after unsucessful save' do
expect_any_instance_of(Experiments::MoveToProjectService)
.to receive(:valid?)
.and_return(true)
FactoryBot.create :experiment, project: new_project, name: 'MyExp'
experiment
expect { service_call }.not_to(change { Tag.count })
end
it 'returns error if teams is not the same' do
t = create :team, :with_members
project.update(team: t)
expect(service_call.errors).to have_key(:target_project_not_valid)
end
end
end