mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-02 01:45:38 +08:00
Merge branch 'decoupling-settings-page' of https://github.com/biosistemika/scinote-web into zd_SCI_1564
This commit is contained in:
commit
39025769ec
43 changed files with 790 additions and 3864 deletions
4
Gemfile
4
Gemfile
|
@ -59,7 +59,9 @@ gem 'deface', '~> 1.0'
|
|||
gem 'nokogiri' # HTML/XML parser
|
||||
gem 'sneaky-save', git: 'https://github.com/einzige/sneaky-save'
|
||||
gem 'rails_autolink', '~> 1.1', '>= 1.1.6'
|
||||
gem 'delayed_paperclip'
|
||||
gem 'delayed_paperclip',
|
||||
git: 'https://github.com/jrgifford/delayed_paperclip.git',
|
||||
ref: 'fcf574c'
|
||||
gem 'rubyzip'
|
||||
gem 'jbuilder' # JSON structures via a Builder-style DSL
|
||||
gem 'activerecord-import'
|
||||
|
|
14
Gemfile.lock
14
Gemfile.lock
|
@ -12,6 +12,15 @@ GIT
|
|||
sneaky-save (0.1.2)
|
||||
activerecord (>= 3.2.0)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/jrgifford/delayed_paperclip.git
|
||||
revision: fcf574c1188213d3c8fce934fd52861a8ba080cb
|
||||
ref: fcf574c
|
||||
specs:
|
||||
delayed_paperclip (3.0.1)
|
||||
activejob (>= 4.2)
|
||||
paperclip (>= 3.3)
|
||||
|
||||
GIT
|
||||
remote: https://github.com/phatworx/devise_security_extension.git
|
||||
revision: b2ee978af7d49f0fb0e7271c6ac074dfb4d39353
|
||||
|
@ -160,9 +169,6 @@ GEM
|
|||
delayed_job_active_record (4.1.2)
|
||||
activerecord (>= 3.0, < 5.2)
|
||||
delayed_job (>= 3.0, < 5)
|
||||
delayed_paperclip (3.0.1)
|
||||
activejob (>= 4.2)
|
||||
paperclip (>= 3.3)
|
||||
devise (4.3.0)
|
||||
bcrypt (~> 3.0)
|
||||
orm_adapter (~> 0.1)
|
||||
|
@ -479,7 +485,7 @@ DEPENDENCIES
|
|||
database_cleaner
|
||||
deface (~> 1.0)
|
||||
delayed_job_active_record
|
||||
delayed_paperclip
|
||||
delayed_paperclip!
|
||||
devise (~> 4.3.0)
|
||||
devise-async
|
||||
devise_invitable
|
||||
|
|
File diff suppressed because it is too large
Load diff
4
Makefile
4
Makefile
|
@ -19,7 +19,7 @@ all: docker database
|
|||
heroku:
|
||||
@heroku buildpacks:remove https://github.com/ddollar/heroku-buildpack-multi.git
|
||||
@heroku buildpacks:set https://github.com/ddollar/heroku-buildpack-multi.git
|
||||
@echo "Set environment variables, DATABASE_URL, RAILS_SERVE_STATIC_FILES, RAKE_ENV, RAILS_ENV, SECRET_KEY_BASE, SKYLIGHT_AUTHENTICATION"
|
||||
@echo "Set environment variables, DATABASE_URL, RAILS_SERVE_STATIC_FILES, RAKE_ENV, RAILS_ENV, SECRET_KEY_BASE"
|
||||
|
||||
docker:
|
||||
@docker-compose build
|
||||
|
@ -66,7 +66,7 @@ stop:
|
|||
@docker-compose stop
|
||||
|
||||
worker:
|
||||
@$(MAKE) rails cmd="rake jobs:work"
|
||||
@$(MAKE) rails cmd="rake jobs:work export WORKER=1"
|
||||
|
||||
cli:
|
||||
@$(MAKE) rails cmd="/bin/bash"
|
||||
|
|
|
@ -728,21 +728,26 @@ var RepositoryDatatable = (function(global) {
|
|||
if (rowsSelected.length === 1) {
|
||||
$('#editRepositoryRecord').prop('disabled', false);
|
||||
$('#editRepositoryRecord').removeClass('disabled');
|
||||
|
||||
// If we switched from 2 selections to 1, then this is not needed
|
||||
var events = $._data($('#exportRepositoriesButton').get(0), 'events');
|
||||
if (!events || !events.click) {
|
||||
$('#exportRepositoriesButton').removeClass('disabled');
|
||||
$('#exportRepositoriesButton').prop('disabled', false);
|
||||
$('#exportRepositoriesButton').off('click').on('click', function() {
|
||||
$('#exportRepositoryModal').modal('show');
|
||||
});
|
||||
$('#export-repositories').off('click').on('click', function() {
|
||||
animateSpinner(null, true);
|
||||
$('#form-export').submit();
|
||||
});
|
||||
}
|
||||
} else {
|
||||
$('#editRepositoryRecord').prop('disabled', true);
|
||||
$('#editRepositoryRecord').addClass('disabled');
|
||||
}
|
||||
$('#deleteRepositoryRecordsButton').prop('disabled', false);
|
||||
$('#deleteRepositoryRecordsButton').removeClass('disabled');
|
||||
$('#exportRepositoriesButton').removeClass('disabled');
|
||||
$('#exportRepositoriesButton').prop('disabled', false);
|
||||
$('#exportRepositoriesButton').on('click', function() {
|
||||
$('#exportRepositoryModal').modal('show');
|
||||
});
|
||||
$('#export-repositories').on('click', function() {
|
||||
animateSpinner(null, true);
|
||||
$('#form-export').submit();
|
||||
});
|
||||
$('#assignRepositoryRecords').removeClass('disabled');
|
||||
$('#assignRepositoryRecords').prop('disabled', false);
|
||||
$('#unassignRepositoryRecords').removeClass('disabled');
|
||||
|
|
|
@ -87,6 +87,6 @@ class ApplicationController < ActionController::Base
|
|||
end
|
||||
|
||||
def set_time_zone(&block)
|
||||
Time.use_zone(current_user.time_zone, &block)
|
||||
Time.use_zone(current_user.settings[:time_zone], &block)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -4,59 +4,93 @@ module ClientApi
|
|||
include ClientApi::Users::UserTeamsHelper
|
||||
|
||||
def index
|
||||
success_response('/client_api/teams/index',
|
||||
teams: current_user.teams_data)
|
||||
success_response(template: '/client_api/teams/index',
|
||||
locals: { teams: current_user.teams_data })
|
||||
end
|
||||
|
||||
def create
|
||||
service = ClientApi::Teams::CreateService.new(
|
||||
current_user: current_user,
|
||||
params: team_params
|
||||
)
|
||||
result = service.execute
|
||||
|
||||
if result[:status] == :success
|
||||
success_response(details: { id: service.team.id })
|
||||
else
|
||||
error_response(
|
||||
message: result[:message],
|
||||
details: service.team.errors
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
def details
|
||||
team_service = ClientApi::TeamsService.new(team_id: params[:team_id],
|
||||
teams_service = ClientApi::TeamsService.new(team_id: params[:team_id],
|
||||
current_user: current_user)
|
||||
success_response('/client_api/teams/details',
|
||||
team_service.team_page_details_data)
|
||||
success_response(template: '/client_api/teams/details',
|
||||
locals: teams_service.team_page_details_data)
|
||||
rescue ClientApi::CustomTeamError
|
||||
error_response
|
||||
end
|
||||
|
||||
def change_team
|
||||
team_service = ClientApi::TeamsService.new(team_id: params[:team_id],
|
||||
teams_service = ClientApi::TeamsService.new(team_id: params[:team_id],
|
||||
current_user: current_user)
|
||||
team_service.change_current_team!
|
||||
success_response('/client_api/teams/index', team_service.teams_data)
|
||||
teams_service.change_current_team!
|
||||
success_response(template: '/client_api/teams/index',
|
||||
locals: teams_service.teams_data)
|
||||
rescue ClientApi::CustomTeamError
|
||||
error_response
|
||||
end
|
||||
|
||||
def update
|
||||
team_service = ClientApi::TeamsService.new(team_id: params[:team_id],
|
||||
teams_service = ClientApi::TeamsService.new(team_id: params[:team_id],
|
||||
current_user: current_user,
|
||||
params: team_params)
|
||||
team_service.update_team!
|
||||
success_response('/client_api/teams/update_details',
|
||||
team_service.single_team_details_data)
|
||||
teams_service.update_team!
|
||||
success_response(template: '/client_api/teams/update_details',
|
||||
locals: teams_service.single_team_details_data)
|
||||
rescue ClientApi::CustomTeamError => error
|
||||
error_response(error.to_s)
|
||||
error_response(message: error.to_s)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def team_params
|
||||
params.require(:team).permit(:description, :name)
|
||||
params.require(:team).permit(:name, :description)
|
||||
end
|
||||
|
||||
def success_response(template, locals)
|
||||
def success_response(args = {})
|
||||
template = args.fetch(:template) { nil }
|
||||
locals = args.fetch(:locals) { {} }
|
||||
details = args.fetch(:details) { {} }
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render template: template,
|
||||
status: :ok,
|
||||
locals: locals
|
||||
if template
|
||||
render template: template,
|
||||
status: :ok,
|
||||
locals: locals
|
||||
else
|
||||
render json: { details: details }, status: :ok
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def error_response(message = t('client_api.generic_error_message'))
|
||||
def error_response(args = {})
|
||||
message = args.fetch(:message) { t('client_api.generic_error_message') }
|
||||
details = args.fetch(:details) { {} }
|
||||
status = args.fetch(:status) { :unprocessable_entity }
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
render json: { message: message }, status: :unprocessable_entity
|
||||
render json: {
|
||||
message: message,
|
||||
details: details
|
||||
},
|
||||
status: status
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -180,7 +180,7 @@ module Users
|
|||
message: sanitize_input(message)
|
||||
)
|
||||
|
||||
if target_user.assignments_notification
|
||||
if target_user.settings[:notifications][:assignments]
|
||||
UserNotification.create(notification: notification, user: target_user)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -77,15 +77,15 @@ module Users
|
|||
end
|
||||
|
||||
def notifications_settings
|
||||
@user.assignments_notification =
|
||||
@user.settings[:notifications][:assignments] =
|
||||
params[:assignments_notification] ? true : false
|
||||
@user.recent_notification =
|
||||
@user.settings[:notifications][:recent] =
|
||||
params[:recent_notification] ? true : false
|
||||
@user.recent_notification_email =
|
||||
@user.settings[:notifications][:recent_email] =
|
||||
params[:recent_notification_email] ? true : false
|
||||
@user.assignments_notification_email =
|
||||
@user.settings[:notifications][:assignments_email] =
|
||||
params[:assignments_notification_email] ? true : false
|
||||
@user.system_message_notification_email =
|
||||
@user.settings[:notifications][:system_message_email] =
|
||||
params[:system_message_notification_email] ? true : false
|
||||
|
||||
if @user.save
|
||||
|
|
|
@ -100,7 +100,7 @@ module ApplicationHelper
|
|||
title: sanitize_input(title),
|
||||
message: sanitize_input(message)
|
||||
)
|
||||
if target_user.assignments_notification
|
||||
if target_user.settings[:notifications][:assignments]
|
||||
UserNotification.create(notification: notification, user: target_user)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -44,7 +44,7 @@ module NotificationsHelper
|
|||
message: sanitize_input(message)
|
||||
)
|
||||
|
||||
if target_user.assignments_notification
|
||||
if target_user.settings[:notifications][:assignments]
|
||||
UserNotification.create(notification: notification, user: target_user)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -6,6 +6,7 @@ export const SETTINGS_PATH = "/settings";
|
|||
export const SETTINGS_ACCOUNT_PATH = "/settings/account";
|
||||
// teams
|
||||
export const TEAMS_PATH = "/client_api/teams";
|
||||
export const TEAMS_NEW_PATH = "/client_api/teams";
|
||||
export const CHANGE_TEAM_PATH = "/client_api/teams/change_team";
|
||||
export const TEAM_DETAILS_PATH = "/client_api/teams/:team_id/details";
|
||||
export const TEAM_UPDATE_PATH = "/client_api/teams/update";
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
export const ENTER_KEY_CODE = 13;
|
||||
export const TEXT_MAX_LENGTH = 10000;
|
||||
export const NAME_MIN_LENGTH = 2;
|
||||
export const NAME_MAX_LENGTH = 255;
|
||||
export const TEXT_MAX_LENGTH = 10000;
|
||||
|
|
|
@ -8,6 +8,7 @@ export default {
|
|||
loading: "Loading ..."
|
||||
},
|
||||
error_messages: {
|
||||
text_too_short: "is too short (minimum is {min_length} characters)",
|
||||
text_too_long: "is too long (maximum is {max_length} characters)"
|
||||
},
|
||||
navbar: {
|
||||
|
@ -101,6 +102,15 @@ export default {
|
|||
administrator: "Administrator",
|
||||
remove_user: "Remove"
|
||||
}
|
||||
},
|
||||
new_team: {
|
||||
title: "New team",
|
||||
name_label: "Team name",
|
||||
name_placeholder: "My team",
|
||||
name_sublabel: "Pick a name that would best describe your team (e.g. 'University of ..., Department of ...').",
|
||||
description_label: "Description",
|
||||
description_sublabel: "Describe your team.",
|
||||
create: "Create team"
|
||||
}
|
||||
},
|
||||
activities: {
|
||||
|
|
|
@ -3,5 +3,6 @@ export const ROOT_PATH = "/";
|
|||
// Settings page
|
||||
export const SETTINGS_TEAMS_ROUTE = "/settings/teams";
|
||||
export const SETTINGS_TEAM_ROUTE = "/settings/teams/:id";
|
||||
export const SETTINGS_NEW_TEAM_ROUTE = "/settings/teams/new";
|
||||
export const SETTINGS_ACCOUNT_PROFILE = "/settings/account/profile";
|
||||
export const SETTINGS_ACCOUNT_PREFERENCES = "/settings/account/preferences";
|
||||
export const SETTINGS_ACCOUNT_PREFERENCES = "/settings/account/preferences";
|
|
@ -10,6 +10,7 @@ import {
|
|||
SETTINGS_TEAM_ROUTE,
|
||||
SETTINGS_ACCOUNT_PROFILE,
|
||||
SETTINGS_ACCOUNT_PREFERENCES
|
||||
SETTINGS_NEW_TEAM_ROUTE
|
||||
} from "../../config/routes";
|
||||
|
||||
import { SETTINGS_PATH, SETTINGS_TEAMS } from "../../config/api_endpoints";
|
||||
|
@ -19,6 +20,7 @@ import SettingsProfile from "./scenes/profile";
|
|||
import SettingsPreferences from "./scenes/preferences";
|
||||
import SettingsTeams from "./scenes/teams";
|
||||
import SettingsTeam from "./scenes/team";
|
||||
import SettingsNewTeam from "./scenes/teams/new";
|
||||
|
||||
export default class SettingsPage extends Component {
|
||||
constructor(props) {
|
||||
|
@ -69,7 +71,7 @@ export default class SettingsPage extends Component {
|
|||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Route path={SETTINGS_NEW_TEAM_ROUTE} component={SettingsNewTeam} />
|
||||
<Route path={SETTINGS_TEAM_ROUTE} component={SettingsTeam} />
|
||||
<Route path={SETTINGS_TEAMS_ROUTE} component={SettingsTeams} />
|
||||
<Route
|
||||
|
@ -87,5 +89,3 @@ export default class SettingsPage extends Component {
|
|||
);
|
||||
}
|
||||
}
|
||||
|
||||
<Switch />;
|
||||
|
|
|
@ -1,14 +1,15 @@
|
|||
import React, { Component } from "react";
|
||||
import ReactRouterPropTypes from "react-router-prop-types";
|
||||
import { Link } from "react-router-dom";
|
||||
import styled from "styled-components";
|
||||
import { Row, Col, Glyphicon, Well } from "react-bootstrap";
|
||||
import { Breadcrumb, Row, Col, Glyphicon, Well } from "react-bootstrap";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
import { FormattedHTMLMessage, FormattedMessage } from "react-intl";
|
||||
import moment from "moment";
|
||||
import prettysize from "prettysize";
|
||||
import axios from "../../../../config/axios";
|
||||
|
||||
import { TEAM_DETAILS_PATH, SETTINGS_TEAMS } from "../../../../config/api_endpoints";
|
||||
import { SETTINGS_TEAMS_ROUTE } from "../../../../config/routes";
|
||||
import { TEAM_DETAILS_PATH } from "../../../../config/api_endpoints";
|
||||
import { BORDER_LIGHT_COLOR } from "../../../../config/constants/colors";
|
||||
|
||||
import TeamsMembers from "./components/TeamsMembers";
|
||||
|
@ -145,16 +146,16 @@ class SettingsTeam extends Component {
|
|||
render() {
|
||||
return (
|
||||
<Wrapper>
|
||||
<StyledOl className="breadcrumb">
|
||||
<li>
|
||||
<Link to={SETTINGS_TEAMS}>
|
||||
<Breadcrumb>
|
||||
<LinkContainer to={SETTINGS_TEAMS_ROUTE}>
|
||||
<Breadcrumb.Item>
|
||||
<FormattedMessage id="settings_page.all_teams" />
|
||||
</Link>
|
||||
</li>
|
||||
<li className="active">
|
||||
</Breadcrumb.Item>
|
||||
</LinkContainer>
|
||||
<Breadcrumb.Item active={true}>
|
||||
{this.state.team.name}
|
||||
</li>
|
||||
</StyledOl>
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<TabTitle>
|
||||
<StyledH3 onClick={this.showNameModal}>
|
||||
{this.state.team.name}
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
import React from "react";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
import PropTypes, { number, string, bool } from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import { FormattedMessage, FormattedPlural } from "react-intl";
|
||||
import { Button, Glyphicon } from "react-bootstrap";
|
||||
import { SETTINGS_NEW_TEAM_ROUTE } from "../../../../../config/routes";
|
||||
|
||||
const Wrapper = styled.div`margin: 15px 0;`;
|
||||
const TeamsPageDetails = ({ teams }) => {
|
||||
|
@ -28,13 +30,11 @@ const TeamsPageDetails = ({ teams }) => {
|
|||
/>
|
||||
}
|
||||
/>
|
||||
<Button
|
||||
onClick={() => {
|
||||
window.location = "/users/settings/teams/new";
|
||||
}}
|
||||
>
|
||||
<Glyphicon glyph="plus" /> <FormattedMessage id="global_team_switch.new_team" />
|
||||
</Button>
|
||||
<LinkContainer to={SETTINGS_NEW_TEAM_ROUTE}>
|
||||
<Button>
|
||||
<Glyphicon glyph="plus" /> <FormattedMessage id="global_team_switch.new_team" />
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
</Wrapper>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import React from "react";
|
||||
import PropTypes, { number, string, bool } from "prop-types";
|
||||
import styled from "styled-components";
|
||||
import { Breadcrumb } from "react-bootstrap";
|
||||
import { connect } from "react-redux";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
|
||||
import {
|
||||
BORDER_LIGHT_COLOR,
|
||||
COLOR_CONCRETE
|
||||
} from "../../../../config/constants/colors";
|
||||
import { BORDER_LIGHT_COLOR } from "../../../../config/constants/colors";
|
||||
|
||||
import TeamsPageDetails from "./components/TeamsPageDetails";
|
||||
import TeamsDataTable from "./components/TeamsDataTable";
|
||||
|
@ -21,16 +19,13 @@ const Wrapper = styled.div`
|
|||
padding: 16px 15px 50px 15px;
|
||||
`;
|
||||
|
||||
const TabTitle = styled.div`
|
||||
background-color: ${COLOR_CONCRETE};
|
||||
padding: 15px;
|
||||
`;
|
||||
|
||||
const SettingsTeams = ({ teams }) =>
|
||||
<Wrapper>
|
||||
<TabTitle>
|
||||
<FormattedMessage id="settings_page.all_teams" />
|
||||
</TabTitle>
|
||||
<Breadcrumb>
|
||||
<Breadcrumb.Item active={false}>
|
||||
<FormattedMessage id="settings_page.all_teams" />
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
<TeamsPageDetails teams={teams} />
|
||||
<TeamsDataTable teams={teams} />
|
||||
</Wrapper>;
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
import React from "react";
|
||||
import {defineMessages, injectIntl, intlShape} from 'react-intl';
|
||||
import { FormControl } from "react-bootstrap";
|
||||
|
||||
const messages = defineMessages({
|
||||
placeholder: { id: "settings_page.new_team.name_placeholder" }
|
||||
});
|
||||
|
||||
const NameFormControl = ({ intl, ...props }) =>
|
||||
<FormControl
|
||||
type="text"
|
||||
placeholder={intl.formatMessage(messages.placeholder)}
|
||||
autoFocus={true}
|
||||
{...props}
|
||||
/>;
|
||||
|
||||
NameFormControl.propTypes = {
|
||||
intl: intlShape.isRequired
|
||||
};
|
||||
|
||||
export default injectIntl(NameFormControl);
|
|
@ -0,0 +1,244 @@
|
|||
import React, { Component } from "react";
|
||||
import { Breadcrumb, FormGroup, FormControl, ControlLabel, HelpBlock, Button } from "react-bootstrap";
|
||||
import { Redirect } from "react-router";
|
||||
import { LinkContainer } from "react-router-bootstrap";
|
||||
import { FormattedMessage } from "react-intl";
|
||||
import update from "immutability-helper";
|
||||
import styled from "styled-components";
|
||||
import _ from "lodash";
|
||||
import axios from "../../../../../config/axios";
|
||||
import {
|
||||
SETTINGS_TEAMS_ROUTE,
|
||||
SETTINGS_TEAM_ROUTE
|
||||
} from "../../../../../config/routes";
|
||||
import { TEAMS_NEW_PATH } from "../../../../../config/api_endpoints";
|
||||
import {
|
||||
NAME_MIN_LENGTH,
|
||||
NAME_MAX_LENGTH,
|
||||
TEXT_MAX_LENGTH
|
||||
} from "../../../../../config/constants/numeric";
|
||||
|
||||
import { BORDER_LIGHT_COLOR } from "../../../../../config/constants/colors";
|
||||
|
||||
import NameFormControl from "./components/NameFormControl";
|
||||
|
||||
const Wrapper = styled.div`
|
||||
background: white;
|
||||
box-sizing: border-box;
|
||||
border: 1px solid ${BORDER_LIGHT_COLOR};
|
||||
border-top: none;
|
||||
margin: 0;
|
||||
padding: 16px 15px 50px 15px;
|
||||
`;
|
||||
|
||||
const MyFormGroupDiv = styled.div`
|
||||
margin-bottom: 15px;
|
||||
`;
|
||||
|
||||
class SettingsNewTeam extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {
|
||||
team: {
|
||||
name: "",
|
||||
description: "",
|
||||
},
|
||||
formErrors: {
|
||||
name: "",
|
||||
description: ""
|
||||
},
|
||||
redirectTo: ""
|
||||
};
|
||||
|
||||
this.onSubmit = this.onSubmit.bind(this);
|
||||
this.validateField = this.validateField.bind(this);
|
||||
this.handleChange = this.handleChange.bind(this);
|
||||
this.renderTeamNameFormGroup = this.renderTeamNameFormGroup.bind(this);
|
||||
this.renderTeamDescriptionFormGroup = this.renderTeamDescriptionFormGroup.bind(this);
|
||||
}
|
||||
|
||||
onSubmit(e) {
|
||||
e.preventDefault();
|
||||
|
||||
axios({
|
||||
method: "post",
|
||||
url: TEAMS_NEW_PATH,
|
||||
withCredentials: true,
|
||||
data: { team: this.state.team }
|
||||
})
|
||||
.then(sr => {
|
||||
// Redirect to the new team page
|
||||
this.newState = { ...this.state };
|
||||
this.newState = update(
|
||||
this.newState,
|
||||
{ redirectTo: {
|
||||
$set: SETTINGS_TEAM_ROUTE.replace(':id', sr.data.details.id)
|
||||
}
|
||||
}
|
||||
);
|
||||
this.setState(this.newState);
|
||||
})
|
||||
.catch(er => {
|
||||
// Display errors
|
||||
this.newState = { ...this.state };
|
||||
['name', 'description'].forEach((el) => {
|
||||
if (er.response.data.details[el]) {
|
||||
this.newState = update(
|
||||
this.newState,
|
||||
{ formErrors: { name: { $set: <span>{er.response.data.details[el]}</span> } } }
|
||||
);
|
||||
}
|
||||
});
|
||||
this.setState(this.newState);
|
||||
});
|
||||
}
|
||||
|
||||
validateField(key, value) {
|
||||
let errorMessage;
|
||||
if (key === "name") {
|
||||
errorMessage = "";
|
||||
|
||||
if (value.length < NAME_MIN_LENGTH) {
|
||||
errorMessage = <FormattedMessage id="error_messages.text_too_short" values={{ min_length: NAME_MIN_LENGTH }} />;
|
||||
} else if (value.length > NAME_MAX_LENGTH) {
|
||||
errorMessage = <FormattedMessage id="error_messages.text_too_long" values={{ max_length: NAME_MAX_LENGTH }} />;
|
||||
}
|
||||
|
||||
this.newState = update(
|
||||
this.newState,
|
||||
{ formErrors: { name: { $set: errorMessage } } }
|
||||
);
|
||||
} else if (key === "description") {
|
||||
errorMessage = "";
|
||||
|
||||
if (value.length > TEXT_MAX_LENGTH) {
|
||||
errorMessage = <FormattedMessage id="error_messages.text_too_long" values={{ max_length: TEXT_MAX_LENGTH }} />;
|
||||
}
|
||||
|
||||
this.newState = update(
|
||||
this.newState,
|
||||
{ formErrors: { description: { $set: errorMessage } } }
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
handleChange(e) {
|
||||
const key = e.target.name;
|
||||
const value = e.target.value;
|
||||
|
||||
this.newState = { ...this.state };
|
||||
|
||||
// Update value in the state
|
||||
this.newState = update(
|
||||
this.newState,
|
||||
{ team: { [key]: { $set: value } } }
|
||||
);
|
||||
|
||||
// Validate the input
|
||||
this.validateField(key, value);
|
||||
|
||||
// Refresh state
|
||||
this.setState(this.newState);
|
||||
}
|
||||
|
||||
renderTeamNameFormGroup() {
|
||||
const formGroupClass = this.state.formErrors.name ? "form-group has-error" : "form-group";
|
||||
const validationState = this.state.formErrors.name ? "error" : null;
|
||||
return (
|
||||
<FormGroup
|
||||
controlId="formTeamName"
|
||||
className={formGroupClass}
|
||||
validationState={validationState}
|
||||
>
|
||||
<ControlLabel>
|
||||
<FormattedMessage id="settings_page.new_team.name_label" />
|
||||
</ControlLabel>
|
||||
<NameFormControl
|
||||
value={this.state.team.name}
|
||||
onChange={this.handleChange}
|
||||
name="name"
|
||||
/>
|
||||
<FormControl.Feedback />
|
||||
<HelpBlock>{this.state.formErrors.name}</HelpBlock>
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
renderTeamDescriptionFormGroup() {
|
||||
const formGroupClass = this.state.formErrors.description ? "form-group has-error" : "form-group";
|
||||
const validationState = this.state.formErrors.description ? "error" : null;
|
||||
return (
|
||||
<FormGroup
|
||||
controlId="formTeamDescription"
|
||||
className={formGroupClass}
|
||||
validationState={validationState}
|
||||
>
|
||||
<ControlLabel>
|
||||
<FormattedMessage id="settings_page.new_team.description_label" />
|
||||
</ControlLabel>
|
||||
<FormControl
|
||||
componentClass="textarea"
|
||||
value={this.state.team.description}
|
||||
onChange={this.handleChange}
|
||||
name="description"
|
||||
/>
|
||||
<FormControl.Feedback />
|
||||
<HelpBlock>{this.state.formErrors.description}</HelpBlock>
|
||||
</FormGroup>
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
// Redirect if form successful
|
||||
if (!_.isEmpty(this.state.redirectTo)) {
|
||||
return <Redirect to={this.state.redirectTo} />;
|
||||
}
|
||||
|
||||
const btnDisabled = !_.isEmpty(this.state.formErrors.name) ||
|
||||
!_.isEmpty(this.state.formErrors.description);
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Breadcrumb>
|
||||
<LinkContainer to={SETTINGS_TEAMS_ROUTE}>
|
||||
<Breadcrumb.Item>
|
||||
<FormattedMessage id="settings_page.all_teams" />
|
||||
</Breadcrumb.Item>
|
||||
</LinkContainer>
|
||||
<Breadcrumb.Item active={true}>
|
||||
<FormattedMessage id="settings_page.new_team.title" />
|
||||
</Breadcrumb.Item>
|
||||
</Breadcrumb>
|
||||
|
||||
<form onSubmit={this.onSubmit} style={{ maxWidth: "500px" }}>
|
||||
|
||||
<MyFormGroupDiv>
|
||||
{this.renderTeamNameFormGroup()}
|
||||
<small>
|
||||
<FormattedMessage id="settings_page.new_team.name_sublabel" />
|
||||
</small>
|
||||
</MyFormGroupDiv>
|
||||
|
||||
<MyFormGroupDiv>
|
||||
{this.renderTeamDescriptionFormGroup()}
|
||||
<small>
|
||||
<FormattedMessage id="settings_page.new_team.description_sublabel" />
|
||||
</small>
|
||||
</MyFormGroupDiv>
|
||||
|
||||
<LinkContainer to={SETTINGS_TEAMS_ROUTE}>
|
||||
<Button>
|
||||
<FormattedMessage id="general.cancel" />
|
||||
</Button>
|
||||
</LinkContainer>
|
||||
<Button type="submit" className="btn-primary" disabled={btnDisabled}>
|
||||
<FormattedMessage id="settings_page.new_team.create" />
|
||||
</Button>
|
||||
|
||||
</form>
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default SettingsNewTeam;
|
|
@ -118,9 +118,9 @@ class Activity < ApplicationRecord
|
|||
|
||||
project.users.each do |project_user|
|
||||
next if project_user == user
|
||||
next if !project_user.assignments_notification &&
|
||||
next if !project_user.settings[:notifications][:assignments] &&
|
||||
notification.type_of == 'assignment'
|
||||
next if !project_user.recent_notification &&
|
||||
next if !project_user.settings[:notifications][:recent] &&
|
||||
notification.type_of == 'recent_changes'
|
||||
UserNotification.create(notification: notification, user: project_user)
|
||||
end
|
||||
|
|
22
app/models/concerns/settings_model.rb
Normal file
22
app/models/concerns/settings_model.rb
Normal file
|
@ -0,0 +1,22 @@
|
|||
module SettingsModel
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@default_settings = HashWithIndifferentAccess.new
|
||||
|
||||
included do
|
||||
serialize :settings, JsonbHashSerializer
|
||||
after_initialize :init_default_settings, if: :new_record?
|
||||
end
|
||||
|
||||
class_methods do
|
||||
def default_settings(dfs)
|
||||
@@default_settings.merge!(dfs)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
def init_default_settings
|
||||
self.settings = @@default_settings
|
||||
end
|
||||
end
|
|
@ -112,7 +112,7 @@ class Team < ApplicationRecord
|
|||
|
||||
sheet.row(i).each.with_index do |value, index|
|
||||
if index == stype_index
|
||||
stype = SampleType.where(name: value, team: self).take
|
||||
stype = SampleType.where(name: value.strip, team: self).take
|
||||
|
||||
unless stype
|
||||
stype = SampleType.new(name: value, team: self)
|
||||
|
@ -123,7 +123,7 @@ class Team < ApplicationRecord
|
|||
end
|
||||
sample.sample_type = stype
|
||||
elsif index == sgroup_index
|
||||
sgroup = SampleGroup.where(name: value, team: self).take
|
||||
sgroup = SampleGroup.where(name: value.strip, team: self).take
|
||||
|
||||
unless sgroup
|
||||
sgroup = SampleGroup.new(name: value, team: self)
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
class User < ApplicationRecord
|
||||
include SearchableModel
|
||||
include SettingsModel
|
||||
|
||||
acts_as_token_authenticatable
|
||||
devise :invitable, :confirmable, :database_authenticatable, :registerable,
|
||||
|
@ -33,9 +34,21 @@ class User < ApplicationRecord
|
|||
validates_attachment :avatar,
|
||||
:content_type => { :content_type => ["image/jpeg", "image/png"] },
|
||||
size: { less_than: Constants::AVATAR_MAX_SIZE_MB.megabytes }
|
||||
validates :time_zone, presence: true
|
||||
validate :time_zone_check
|
||||
|
||||
store_accessor :settings, :time_zone
|
||||
|
||||
default_settings(
|
||||
time_zone: 'UTC',
|
||||
notifications: {
|
||||
assignments: true,
|
||||
assignments_email: false,
|
||||
recent: true,
|
||||
recent_email: false,
|
||||
system_message_email: false
|
||||
}
|
||||
)
|
||||
|
||||
# Relations
|
||||
has_many :user_teams, inverse_of: :user
|
||||
has_many :teams, through: :user_teams
|
||||
|
@ -407,7 +420,8 @@ class User < ApplicationRecord
|
|||
end
|
||||
|
||||
def time_zone_check
|
||||
if time_zone.nil? or ActiveSupport::TimeZone.new(time_zone).nil?
|
||||
if time_zone.nil? ||
|
||||
ActiveSupport::TimeZone.new(time_zone).nil?
|
||||
errors.add(:time_zone)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -40,17 +40,17 @@ class UserNotification < ApplicationRecord
|
|||
send_email_notification(
|
||||
user,
|
||||
notification
|
||||
) if user.system_message_notification_email
|
||||
) if user.settings[:notifications][:system_message_email]
|
||||
when 'assignment'
|
||||
send_email_notification(
|
||||
user,
|
||||
notification
|
||||
) if user.assignments_notification_email
|
||||
) if user.settings[:notifications][:assignments_email]
|
||||
when 'recent_changes'
|
||||
send_email_notification(
|
||||
user,
|
||||
notification
|
||||
) if user.recent_notification_email
|
||||
) if user.settings[:notifications][:recent_email]
|
||||
when 'deliver'
|
||||
send_email_notification(
|
||||
user,
|
||||
|
|
11
app/serializers/jsonb_hash_serializer.rb
Normal file
11
app/serializers/jsonb_hash_serializer.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
class JsonbHashSerializer
|
||||
def self.dump(hash)
|
||||
hash.to_json
|
||||
end
|
||||
|
||||
def self.load(hash)
|
||||
hash ||= {}
|
||||
hash = JSON.parse(hash) if hash.instance_of? String
|
||||
hash.with_indifferent_access
|
||||
end
|
||||
end
|
27
app/services/client_api/base_service.rb
Normal file
27
app/services/client_api/base_service.rb
Normal file
|
@ -0,0 +1,27 @@
|
|||
module ClientApi
|
||||
class BaseService
|
||||
attr_accessor :current_user, :params
|
||||
|
||||
def initialize(args)
|
||||
@current_user = args.fetch(:current_user) { raise StandardError }
|
||||
@params = (args.fetch(:params) { {} }).dup
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def error(message, http_status = nil)
|
||||
result = {
|
||||
message: message,
|
||||
status: :error
|
||||
}
|
||||
|
||||
result[:http_status] = http_status if http_status
|
||||
result
|
||||
end
|
||||
|
||||
def success(pass_back = {})
|
||||
pass_back[:status] = :success
|
||||
pass_back
|
||||
end
|
||||
end
|
||||
end
|
26
app/services/client_api/teams/create_service.rb
Normal file
26
app/services/client_api/teams/create_service.rb
Normal file
|
@ -0,0 +1,26 @@
|
|||
module ClientApi
|
||||
module Teams
|
||||
class CreateService < BaseService
|
||||
attr_accessor :team
|
||||
|
||||
def execute
|
||||
@team = Team.new(@params)
|
||||
@team.created_by = @current_user
|
||||
|
||||
if @team.save
|
||||
# Okay, team is created, now
|
||||
# add the current user as admin
|
||||
UserTeam.create(
|
||||
user: @current_user,
|
||||
team: @team,
|
||||
role: 2
|
||||
)
|
||||
|
||||
success
|
||||
else
|
||||
error(@team.errors.full_messages.uniq.join('. '))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,10 +1,10 @@
|
|||
module ClientApi
|
||||
class TeamsService
|
||||
def initialize(arg)
|
||||
team_id = arg.fetch(:team_id) { raise ClientApi::CustomTeamError }
|
||||
@params = arg.fetch(:params) { false }
|
||||
@team = Team.find_by_id(team_id)
|
||||
@user = arg.fetch(:current_user) { raise ClientApi::CustomTeamError }
|
||||
team_id = arg.fetch(:team_id) { raise ClientApi::CustomTeamError }
|
||||
@team = Team.find_by_id(team_id)
|
||||
raise ClientApi::CustomTeamError unless @user.teams.include? @team
|
||||
end
|
||||
|
||||
|
@ -34,5 +34,6 @@ module ClientApi
|
|||
{ teams: @user.teams_data }
|
||||
end
|
||||
end
|
||||
|
||||
CustomTeamError = Class.new(StandardError)
|
||||
end
|
||||
|
|
|
@ -28,7 +28,7 @@
|
|||
<% if th.nil? %>
|
||||
<i><%= t('samples.modal_import.no_header_name') %></i>
|
||||
<% else %>
|
||||
<% if th.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
|
||||
<% if th.to_s.length > Constants::NAME_TRUNCATION_LENGTH_DROPDOWN %>
|
||||
<div class="modal-tooltip">
|
||||
<%= truncate(th, length: Constants::NAME_TRUNCATION_LENGTH_DROPDOWN) %>
|
||||
</div>
|
||||
|
|
|
@ -12,7 +12,11 @@
|
|||
<%= form_for(@user,
|
||||
url: update_preferences_path(format: :json),
|
||||
remote: true,
|
||||
html: { method: :put, 'data-for' => 'time_zone' }) do |f| %>
|
||||
html: {
|
||||
method: :put,
|
||||
'data-for' => 'settings[time_zone]',
|
||||
'data-turbolinks' => false
|
||||
}) do |f| %>
|
||||
<div data-part="view">
|
||||
<div class="form-group">
|
||||
<%= f.label t("users.settings.account.preferences.edit.time_zone_label") %>
|
||||
|
@ -21,9 +25,9 @@
|
|||
disabled="disabled"
|
||||
autocomplete="off"
|
||||
type="text"
|
||||
value="<%= @user.time_zone %>"
|
||||
name="fake_user[time_zone]"
|
||||
id="fake_user_time_zone">
|
||||
value="<%= @user.settings[:time_zone] %>"
|
||||
name="fake_user[settings][time_zone]"
|
||||
id="fake_user_settings_time_zone">
|
||||
<span class="input-group-btn">
|
||||
<a href="#" class="btn btn-default" data-action="edit"><%=t "general.edit" %></a>
|
||||
</span>
|
||||
|
@ -75,7 +79,7 @@
|
|||
<%=t 'notifications.form.notification_scinote' %>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<%= check_box_tag :assignments_notification, @user.assignments_notification %>
|
||||
<%= check_box_tag :assignments_notification, @user.settings[:notifications][:assignments] %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -83,7 +87,7 @@
|
|||
<%=t 'notifications.form.notification_email' %>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<%= check_box_tag :assignments_notification_email, @user.assignments_notification_email %>
|
||||
<%= check_box_tag :assignments_notification_email, @user.settings[:notifications][:assignments_email] %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -100,7 +104,7 @@
|
|||
<%=t 'notifications.form.notification_scinote' %>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<%= check_box_tag :recent_notification, @user.recent_notification %>
|
||||
<%= check_box_tag :recent_notification, @user.settings[:notifications][:recent] %>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -108,7 +112,7 @@
|
|||
<%=t 'notifications.form.notification_email' %>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<%= check_box_tag :recent_notification_email, @user.recent_notification_email %>
|
||||
<%= check_box_tag :recent_notification_email, @user.settings[:notifications][:recent_email] %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -133,7 +137,7 @@
|
|||
<%=t 'notifications.form.notification_email' %>
|
||||
</div>
|
||||
<div class="col-sm-8">
|
||||
<%= check_box_tag :system_message_notification_email, @user.system_message_notification_email %>
|
||||
<%= check_box_tag :system_message_notification_email, @user.settings[:notifications][:system_message_email] %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,10 +4,10 @@ Rails.application.configure do
|
|||
# In the development environment your application's code is reloaded on
|
||||
# every request. This slows down response time but is perfect for development
|
||||
# since you don't have to restart the web server when you make code changes.
|
||||
config.cache_classes = false
|
||||
config.cache_classes = ENV['WORKER'] ? true : false
|
||||
|
||||
# Do not eager load code on boot.
|
||||
config.eager_load = false
|
||||
config.eager_load = ENV['WORKER'] ? true : false
|
||||
|
||||
# Show full error reports and disable caching.
|
||||
config.consider_all_requests_local = true
|
||||
|
|
|
@ -19,7 +19,9 @@ Rails.application.routes.draw do
|
|||
# teams
|
||||
get '/teams', to: 'teams/teams#index'
|
||||
namespace :teams do
|
||||
get '/new', to: 'teams#new'
|
||||
get '/:team_id/details', to: 'teams#details'
|
||||
post '/', to: 'teams#create'
|
||||
post '/change_team', to: 'teams#change_team'
|
||||
post '/update', to: 'teams#update'
|
||||
end
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
---
|
||||
# The authentication token for the application.
|
||||
authentication: <%= ENV["SKYLIGHT_AUTHENTICATION"] ?>
|
53
db/migrate/20170809131000_refactor_user_settings.rb
Normal file
53
db/migrate/20170809131000_refactor_user_settings.rb
Normal file
|
@ -0,0 +1,53 @@
|
|||
class RefactorUserSettings < ActiveRecord::Migration[5.1]
|
||||
def up
|
||||
add_column :users, :settings, :jsonb, default: {}, null: false
|
||||
|
||||
User.find_each do |user|
|
||||
settings = {
|
||||
time_zone: user['time_zone'],
|
||||
notifications: {
|
||||
assignments: user['assignments_notification'],
|
||||
assignments_email: user['assignments_notification_email'],
|
||||
recent: user['recent_notification'],
|
||||
recent_email: user['recent_notification_email'],
|
||||
system_message_email: user['system_message_notification_email']
|
||||
}
|
||||
}
|
||||
user.update(settings: settings)
|
||||
end
|
||||
|
||||
remove_column :users, :time_zone, :string
|
||||
remove_column :users, :assignments_notification, :boolean
|
||||
remove_column :users, :assignments_notification_email, :boolean
|
||||
remove_column :users, :recent_notification, :boolean
|
||||
remove_column :users, :recent_notification_email, :boolean
|
||||
remove_column :users, :system_message_notification_email, :boolean
|
||||
end
|
||||
|
||||
def down
|
||||
add_column :users, :time_zone, :string, default: false
|
||||
add_column :users, :assignments_notification, :boolean, default: false
|
||||
add_column :users, :assignments_notification_email, :boolean, default: false
|
||||
add_column :users, :recent_notification, :boolean, default: false
|
||||
add_column :users, :recent_notification_email, :boolean, default: false
|
||||
add_column :users,
|
||||
:system_message_notification_email, :boolean, default: false
|
||||
|
||||
User.find_each do |user|
|
||||
user.time_zone = user.settings[:time_zone]
|
||||
user.assignments_notification =
|
||||
user.settings[:notifications][:assignments]
|
||||
user.assignments_notification_email =
|
||||
user.settings[:notifications][:assignments_email]
|
||||
user.recent_notification =
|
||||
user.settings[:notifications][:recent]
|
||||
user.recent_notification_email =
|
||||
user.settings[:notifications][:recent_email]
|
||||
user.system_message_notification_email =
|
||||
user.settings[:notifications][:system_message_email]
|
||||
user.save
|
||||
end
|
||||
|
||||
remove_column :users, :settings, :jsonb
|
||||
end
|
||||
end
|
File diff suppressed because it is too large
Load diff
54
lib/tasks/versions/adjectives.json
Normal file
54
lib/tasks/versions/adjectives.json
Normal file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"a":
|
||||
["aback", "abaft", "abandoned", "abashed", "aberrant", "abhorrent", "abiding", "abject", "ablaze", "able", "abnormal", "aboard", "aboriginal", "abortive", "abounding", "abrasive", "abrupt", "absent", "absolute", "absorbed", "absorbing", "abstracted", "absurd", "abundant", "abusive", "academic", "acceptable", "accessible", "accidental", "acclaimed", "accomplished", "accurate", "aching", "acid", "acidic", "acoustic", "acrid", "acrobatic", "active", "actual", "actually", "ad hoc", "adamant", "adaptable", "addicted", "adept", "adhesive", "adjoining", "admirable", "admired", "adolescent", "adorable", "adored", "advanced", "adventurous", "affectionate", "afraid", "aged", "aggravating", "aggressive", "agile", "agitated", "agonizing", "agreeable", "ahead", "ajar", "alarmed", "alarming", "alcoholic", "alert", "alienated", "alike", "alive", "all", "alleged", "alluring", "aloof", "altruistic", "amazing", "ambiguous", "ambitious", "amiable", "ample", "amuck", "amused", "amusing", "anchored", "ancient", "ancient", "angelic", "angry", "angry", "anguished", "animated", "annoyed", "annoying", "annual", "another", "antique", "antsy", "anxious", "any", "apathetic", "appetizing", "apprehensive", "appropriate", "apt", "aquatic", "arctic", "arid", "aromatic", "arrogant", "artistic", "ashamed", "aspiring", "assorted", "assured", "astonishing", "athletic", "attached", "attentive", "attractive", "auspicious", "austere", "authentic", "authorized", "automatic", "available", "avaricious", "average", "awake", "aware", "awesome", "awful", "awkward", "axiomatic"],
|
||||
"b":
|
||||
["babyish", "back", "bad", "baggy", "barbarous", "bare", "barren", "bashful", "basic", "batty", "bawdy", "beautiful", "beefy", "befitting", "belated", "belligerent", "beloved", "beneficial", "bent", "berserk", "best", "better", "bewildered", "bewitched", "big", "big-hearted", "billowy", "biodegradable", "bite-sized", "biting", "bitter", "bizarre", "black", "black-and-white", "bland", "blank", "blaring", "bleak", "blind", "blissful", "blond", "bloody", "blue", "blue-eyed", "blushing", "bogus", "boiling", "bold", "bony", "boorish", "bored", "boring", "bossy", "both", "bouncy", "boundless", "bountiful", "bowed", "brainy", "brash", "brave", "brawny", "breakable", "breezy", "brief", "bright", "brilliant", "brisk", "broad", "broken", "bronze", "brown", "bruised", "bubbly", "bulky", "bumpy", "buoyant", "burdensome", "burly", "bustling", "busy", "buttery", "buzzing"],
|
||||
"c":
|
||||
["cagey", "calculating", "callous", "calm", "candid", "canine", "capable", "capital", "capricious", "carefree", "careful", "careless", "caring", "cautious", "cavernous", "ceaseless", "celebrated", "certain", "changeable", "charming", "cheap", "cheeky", "cheerful", "cheery", "chemical", "chief", "childlike", "chilly", "chivalrous", "chubby", "chunky", "circular", "clammy", "classic", "classy", "clean", "clear", "clear-cut", "clever", "cloistered", "close", "closed", "cloudy", "clueless", "clumsy", "cluttered", "coarse", "coherent", "cold", "colorful", "colorless", "colossal", "colossal", "combative", "comfortable", "common", "compassionate", "competent", "complete", "complex", "complicated", "composed", "concerned", "concrete", "condemned", "condescending", "confused", "conscious", "considerate", "constant", "contemplative", "content", "conventional", "convincing", "convoluted", "cooing", "cooked", "cool", "cooperative", "coordinated", "corny", "corrupt", "costly", "courageous", "courteous", "cowardly", "crabby", "crafty", "craven", "crazy", "creamy", "creative", "creepy", "criminal", "crisp", "critical", "crooked", "crowded", "cruel", "crushing", "cuddly", "cultivated", "cultured", "cumbersome", "curious", "curly", "curved", "curvy", "cut", "cute", "cylindrical", "cynical"],
|
||||
"d":
|
||||
["daffy", "daily", "damaged", "damaging", "damp", "dangerous", "dapper", "dapper", "daring", "dark", "darling", "dashing", "dazzling", "dead", "deadly", "deadpan", "deafening", "dear", "dearest", "debonair", "decayed", "deceitful", "decent", "decimal", "decisive", "decorous", "deep", "deeply", "defeated", "defective", "defenseless", "defensive", "defiant", "deficient", "definite", "delayed", "delectable", "delicate", "delicious", "delightful", "delirious", "demanding", "demonic", "dense", "dental", "dependable", "dependent", "depraved", "depressed", "deranged", "descriptive", "deserted", "despicable", "detailed", "determined", "devilish", "devoted", "didactic", "different", "difficult", "digital", "dilapidated", "diligent", "dim", "diminutive", "dimpled", "dimwitted", "direct", "direful", "dirty", "disagreeable", "disastrous", "discreet", "discrete", "disfigured", "disguised", "disgusted", "disgusting", "dishonest", "disillusioned", "disloyal", "dismal", "dispensable", "distant", "distinct", "distorted", "distraught", "distressed", "disturbed", "divergent", "dizzy", "domineering", "dopey", "doting", "double", "doubtful", "downright", "drab", "draconian", "drafty", "drained", "dramatic", "dreary", "droopy", "drunk", "dry", "dual", "dull", "dull", "dusty", "dutiful", "dynamic", "dysfunctional"],
|
||||
"e":
|
||||
["each", "eager", "early", "earnest", "earsplitting", "earthy", "easy", "easy-going", "eatable", "economic", "ecstatic", "edible", "educated", "efficacious", "efficient", "eight", "elaborate", "elastic", "elated", "elderly", "electric", "elegant", "elementary", "elfin", "elite", "elliptical", "emaciated", "embarrassed", "embellished", "eminent", "emotional", "empty", "enchanted", "enchanting", "encouraging", "endurable", "energetic", "enlightened", "enormous", "enraged", "entertaining", "enthusiastic", "entire", "envious", "envious", "equable", "equal", "equatorial", "erect", "erratic", "essential", "esteemed", "ethereal", "ethical", "euphoric", "evanescent", "evasive", "even", "evergreen", "everlasting", "every", "evil", "exalted", "exasperated", "excellent", "excitable", "excited", "exciting", "exclusive", "exemplary", "exhausted", "exhilarated", "exotic", "expensive", "experienced", "expert", "extensive", "extra-large", "extraneous", "extra-small", "extroverted", "exuberant", "exultant"],
|
||||
"f":
|
||||
["fabulous", "faded", "failing", "faint", "fair", "faithful", "fake", "fallacious", "false", "familiar", "famous", "fanatical", "fancy", "fantastic", "far", "faraway", "far-flung", "far-off", "fascinated", "fast", "fat", "fatal", "fatherly", "faulty", "favorable", "favorite", "fearful", "fearless", "feeble", "feigned", "feisty", "feline", "female", "feminine", "fertile", "festive", "few", "fickle", "fierce", "filthy", "fine", "finicky", "finished", "firm", "first", "firsthand", "fitting", "five", "fixed", "flagrant", "flaky", "flamboyant", "flashy", "flat", "flawed", "flawless", "flickering", "flimsy", "flippant", "floppy", "flowery", "flufy", "fluid", "flustered", "fluttering", "foamy", "focused", "fond", "foolhardy", "foolish", "forceful", "foregoing", "forgetful", "forked", "formal", "forsaken", "forthright", "fortunate", "four", "fragile", "fragrant", "frail", "frank", "frantic", "frayed", "free", "freezing", "French", "frequent", "fresh", "fretful", "friendly", "frightened", "frightening", "frigid", "frilly", "frivolous", "frizzy", "front", "frosty", "frothy", "frozen", "frugal", "fruitful", "frustrating", "full", "fumbling", "fumbling", "functional", "funny", "furry", "furtive", "fussy", "future", "futuristic", "fuzzy"],
|
||||
"g":
|
||||
["gabby", "gainful", "gamy", "gaping", "gargantuan", "garrulous", "gaseous", "gaudy", "general", "general", "generous", "gentle", "genuine", "ghastly", "giant", "giddy", "gifted", "gigantic", "giving", "glamorous", "glaring", "glass", "gleaming", "gleeful", "glib", "glistening", "glittering", "gloomy", "glorious", "glossy", "glum", "godly", "golden", "good", "good-natured", "goofy", "gorgeous", "graceful", "gracious", "grand", "grandiose", "grandiose", "granular", "grateful", "gratis", "grave", "gray", "greasy", "great", "greedy", "green", "gregarious", "grey", "grieving", "grim", "grimy", "gripping", "grizzled", "groovy", "gross", "grotesque", "grouchy", "grounded", "growing", "growling", "grown", "grubby", "gruesome", "grumpy", "guarded", "guiltless", "guilty", "gullible", "gummy", "gusty", "guttural"],
|
||||
"h":
|
||||
["habitual", "hairy", "half", "half", "hallowed", "halting", "handmade", "handsome", "handsomely", "handy", "hanging", "hapless", "happy", "happy-go-lucky", "hard", "hard-to-find", "harebrained", "harmful", "harmless", "harmonious", "harsh", "hasty", "hateful", "haunting", "heady", "healthy", "heartbreaking", "heartfelt", "hearty", "heavenly", "heavy", "hefty", "hellish", "helpful", "helpless", "hesitant", "hidden", "hideous", "high", "highfalutin", "high-level", "high-pitched", "hilarious", "hissing", "historical", "hoarse", "holistic", "hollow", "homeless", "homely", "honest", "honorable", "honored", "hopeful", "horrible", "horrific", "hospitable", "hot", "huge", "hulking", "humble", "humdrum", "humiliating", "humming", "humongous", "humorous", "hungry", "hurried", "hurt", "hurtful", "hushed", "husky", "hypnotic", "hysterical"],
|
||||
"i":
|
||||
["icky", "icy", "ideal", "ideal", "idealistic", "identical", "idiotic", "idle", "idolized", "ignorant", "ill", "illegal", "ill-fated", "ill-informed", "illiterate", "illustrious", "imaginary", "imaginative", "immaculate", "immaterial", "immediate", "immense", "imminent", "impartial", "impassioned", "impeccable", "imperfect", "imperturbable", "impish", "impolite", "important", "imported", "impossible", "impractical", "impressionable", "impressive", "improbable", "impure", "inborn", "incandescent", "incomparable", "incompatible", "incompetent", "incomplete", "inconclusive", "inconsequential", "incredible", "indelible", "indolent", "industrious", "inexpensive", "inexperienced", "infamous", "infantile", "infatuated", "inferior", "infinite", "informal", "innate", "innocent", "inquisitive", "insecure", "insidious", "insignificant", "insistent", "instinctive", "instructive", "insubstantial", "intelligent", "intent", "intentional", "interesting", "internal", "international", "intrepid", "intrigued", "invincible", "irate", "ironclad", "irresponsible", "irritable", "irritating", "itchy"],
|
||||
"j":
|
||||
["jaded", "jagged", "jam-packed", "jaunty", "jazzy", "jealous", "jittery", "jobless", "joint", "jolly", "jovial", "joyful", "joyous", "jubilant", "judicious", "juicy", "jumbled", "jumbo", "jumpy", "jumpy", "junior", "juvenile"],
|
||||
"k":
|
||||
["kaleidoscopic", "kaput", "keen", "key", "kind", "kindhearted", "kindly", "klutzy", "knobby", "knotty", "knowing", "knowledgeable", "known", "kooky", "kosher"],
|
||||
"l":
|
||||
["labored", "lackadaisical", "lacking", "lame", "lame", "lamentable", "languid", "lanky", "large", "last", "lasting", "late", "laughable", "lavish", "lawful", "lazy", "leading", "leafy", "lean", "learned", "left", "legal", "legitimate", "lethal", "level", "lewd", "light", "lighthearted", "likable", "like", "likeable", "likely", "limited", "limp", "limping", "linear", "lined", "liquid", "literate", "little", "live", "lively", "livid", "living", "loathsome", "lone", "lonely", "long", "longing", "long-term", "loose", "lopsided", "lost", "loud", "loutish", "lovable", "lovely", "loving", "low", "lowly", "loyal", "lucky", "ludicrous", "lumbering", "luminous", "lumpy", "lush", "lustrous", "luxuriant", "luxurious", "lying", "lyrical"],
|
||||
"m":
|
||||
["macabre", "macho", "mad", "maddening", "made-up", "madly", "magenta", "magical", "magnificent", "majestic", "major", "makeshift", "male", "malicious", "mammoth", "maniacal", "many", "marked", "married", "marvelous", "masculine", "massive", "material", "materialistic", "mature", "meager", "mealy", "mean", "measly", "meaty", "medical", "mediocre", "medium", "meek", "melancholy", "mellow", "melodic", "melted", "memorable", "menacing", "merciful", "mere", "merry", "messy", "metallic", "mighty", "mild", "military", "milky", "mindless", "miniature", "minor", "minty", "minute", "miscreant", "miserable", "miserly", "misguided", "mistaken", "misty", "mixed", "moaning", "modern", "modest", "moist", "moldy", "momentous", "monstrous", "monthly", "monumental", "moody", "moral", "mortified", "motherly", "motionless", "mountainous", "muddled", "muddy", "muffled", "multicolored", "mundane", "mundane", "murky", "mushy", "musty", "mute", "muted", "mysterious"],
|
||||
"n":
|
||||
["naive", "nappy", "narrow", "nasty", "natural", "naughty", "nauseating", "nautical", "near", "neat", "nebulous", "necessary", "needless", "needy", "negative", "neglected", "negligible", "neighboring", "neighborly", "nervous", "nervous", "new", "next", "nice", "nice", "nifty", "nimble", "nine", "nippy", "nocturnal", "noiseless", "noisy", "nonchalant", "nondescript", "nonsensical", "nonstop", "normal", "nostalgic", "nosy", "notable", "noted", "noteworthy", "novel", "noxious", "null", "numb", "numberless", "numerous", "nutritious", "nutty"],
|
||||
"o":
|
||||
["oafish", "obedient", "obeisant", "obese", "oblivious", "oblong", "obnoxious", "obscene", "obsequious", "observant", "obsolete", "obtainable", "obvious", "occasional", "oceanic", "odd", "oddball", "offbeat", "offensive", "official", "oily", "old", "old-fashioned", "omniscient", "one", "onerous", "only", "open", "opposite", "optimal", "optimistic", "opulent", "orange", "orderly", "ordinary", "organic", "original", "ornate", "ornery", "ossified", "other", "our", "outgoing", "outlandish", "outlying", "outrageous", "outstanding", "oval", "overconfident", "overcooked", "overdue", "overjoyed", "overlooked", "overrated", "overt", "overwrought"],
|
||||
"p":
|
||||
["painful", "painstaking", "palatable", "pale", "paltry", "panicky", "panoramic", "parallel", "parched", "parsimonious", "partial", "passionate", "past", "pastel", "pastoral", "pathetic", "peaceful", "penitent", "peppery", "perfect", "perfumed", "periodic", "perky", "permissible", "perpetual", "perplexed", "personal", "pertinent", "pesky", "pessimistic", "petite", "petty", "petty", "phobic", "phony", "physical", "picayune", "piercing", "pink", "piquant", "pitiful", "placid", "plain", "plaintive", "plant", "plastic", "plausible", "playful", "pleasant", "pleased", "pleasing", "plucky", "plump", "plush", "pointed", "pointless", "poised", "polished", "polite", "political", "pompous", "poor", "popular", "portly", "posh", "positive", "possessive", "possible", "potable", "powerful", "powerless", "practical", "precious", "premium", "present", "present", "prestigious", "pretty", "previous", "pricey", "prickly", "primary", "prime", "pristine", "private", "prize", "probable", "productive", "profitable", "profuse", "proper", "protective", "proud", "prudent", "psychedelic", "psychotic", "public", "puffy", "pumped", "punctual", "pungent", "puny", "pure", "purple", "purring", "pushy", "pushy", "putrid", "puzzled", "puzzling"],
|
||||
"q":
|
||||
["quack", "quaint", "quaint", "qualified", "quarrelsome", "quarterly", "queasy", "querulous", "questionable", "quick", "quickest", "quick-witted", "quiet", "quintessential", "quirky", "quixotic", "quixotic", "quizzical"],
|
||||
"r":
|
||||
["rabid", "racial", "radiant", "ragged", "rainy", "rambunctious", "rampant", "rapid", "rare", "rash", "raspy", "ratty", "raw", "ready", "real", "realistic", "reasonable", "rebel", "recent", "receptive", "reckless", "recondite", "rectangular", "red", "redundant", "reflecting", "reflective", "regal", "regular", "reliable", "relieved", "remarkable", "reminiscent", "remorseful", "remote", "repentant", "repulsive", "required", "resolute", "resonant", "respectful", "responsible", "responsive", "revolving", "rewarding", "rhetorical", "rich", "right", "righteous", "rightful", "rigid", "ringed", "ripe", "ritzy", "roasted", "robust", "romantic", "roomy", "rosy", "rotating", "rotten", "rotund", "rough", "round", "rowdy", "royal", "rubbery", "ruddy", "rude", "rundown", "runny", "rural", "rustic rusty", "ruthless"],
|
||||
"s":
|
||||
["sable", "sad", "safe", "salty", "same", "sandy", "sane", "sarcastic", "sardonic", "sassy", "satisfied", "satisfying", "savory", "scaly", "scandalous", "scant", "scarce", "scared", "scary", "scattered", "scented", "scholarly", "scientific", "scintillating", "scornful", "scratchy", "scrawny", "screeching", "second", "secondary", "second-hand", "secret", "secretive", "sedate", "seemly", "selective", "self-assured", "selfish", "self-reliant", "sentimental", "separate", "serene", "serious", "serpentine", "several", "severe", "shabby", "shadowy", "shady", "shaggy", "shaky", "shallow", "shameful", "shameless", "sharp", "shimmering", "shiny", "shivering", "shocked", "shocking", "shoddy", "short", "short-term", "showy", "shrill", "shut", "shy", "sick", "silent", "silky", "silly", "silver", "similar", "simple", "simplistic", "sincere", "sinful", "single", "six", "sizzling", "skeletal", "skillful", "skinny", "sleepy", "slight", "slim", "slimy", "slippery", "sloppy", "slow", "slushy", "small", "smarmy", "smart", "smelly", "smiling", "smoggy", "smooth", "smug", "snappy", "snarling", "sneaky", "sniveling", "snobbish", "snoopy", "snotty", "sociable", "soft", "soggy", "solid", "somber", "some", "sophisticated", "sordid", "sore", "sorrowful", "soulful", "soupy", "sour", "sour", "Spanish", "sparkling", "sparse", "special", "specific", "spectacular", "speedy", "spherical", "spicy", "spiffy", "spiky", "spirited", "spiritual", "spiteful", "splendid", "spooky", "spotless", "spotted", "spotty", "spry", "spurious", "squalid", "square", "squeaky", "squealing", "squeamish", "squiggly", "stable", "staid", "stained", "staking", "stale", "standard", "standing", "starchy", "stark", "starry", "statuesque", "steadfast", "steady", "steel", "steep", "stereotyped", "sticky", "stiff", "stimulating", "stingy", "stormy", "stout", "straight", "strange", "strict", "strident", "striking", "striped", "strong", "studious", "stunning", "stunning", "stupendous", "stupid", "sturdy", "stylish", "subdued", "submissive", "subsequent", "substantial", "subtle", "suburban", "successful", "succinct", "succulent", "sudden", "sugary", "sulky", "sunny", "super", "superb", "superficial", "superior", "supportive", "supreme", "sure-footed", "surprised", "suspicious", "svelte", "swanky", "sweaty", "sweet", "sweltering", "swift", "sympathetic", "symptomatic", "synonymous"],
|
||||
"t":
|
||||
["taboo", "tacit", "tacky", "talented", "talkative", "tall", "tame", "tan", "tangible", "tangy", "tart", "tasteful", "tasteless", "tasty", "tattered", "taut", "tawdry", "tearful", "tedious", "teeming", "teeny", "teeny-tiny", "telling", "temporary", "tempting", "ten", "tender", "tense", "tenuous", "tepid", "terrible", "terrific", "tested", "testy", "thankful", "that", "therapeutic", "these", "thick", "thin", "thinkable", "third", "thirsty", "this", "thorny", "thorough", "those", "thoughtful", "thoughtless", "threadbare", "threatening", "three", "thrifty", "thundering", "thunderous", "tidy", "tight", "tightfisted", "timely", "tinted", "tiny", "tired", "tiresome", "toothsome", "torn", "torpid", "total", "tough", "towering", "tragic", "trained", "tranquil", "trashy", "traumatic", "treasured", "tremendous", "triangular", "tricky", "trifling", "trim", "trite", "trivial", "troubled", "truculent", "true", "trusting", "trustworthy", "trusty", "truthful", "tubby", "turbulent", "twin", "two", "typical"],
|
||||
"u":
|
||||
["ubiquitous", "ugliest", "ugly", "ultimate", "ultra", "unable", "unaccountable", "unarmed", "unaware", "unbecoming", "unbiased", "uncomfortable", "uncommon", "unconscious", "uncovered", "understated", "understood", "undesirable", "unequal", "unequaled", "uneven", "unfinished", "unfit", "unfolded", "unfortunate", "unhappy", "unhealthy", "uniform", "unimportant", "uninterested", "unique", "united", "unkempt", "unknown", "unlawful", "unlined", "unlucky", "unnatural", "unpleasant", "unrealistic", "unripe", "unruly", "unselfish", "unsightly", "unsteady", "unsuitable", "unsung", "untidy", "untimely", "untried", "untrue", "unused", "unusual", "unwelcome", "unwieldy", "unwitting", "unwritten", "upbeat", "uppity", "upright", "upset", "uptight", "urban", "usable", "used", "used", "useful", "useless", "utilized", "utopian", "utter", "uttermost"],
|
||||
"v":
|
||||
["vacant", "vacuous", "vagabond", "vague", "vain", "valid", "valuable", "vapid", "variable", "various", "vast", "velvety", "venerated", "vengeful", "venomous", "verdant", "verifiable", "versed", "vexed", "vibrant", "vicious", "victorious", "vigilant", "vigorous", "villainous", "violent", "violet", "virtual", "virtuous", "visible", "vital", "vivacious", "vivid", "voiceless", "volatile", "voluminous", "voracious", "vulgar"],
|
||||
"w":
|
||||
["wacky", "waggish", "waiting", "wakeful", "wan", "wandering", "wanting", "warlike", "warm", "warmhearted", "warped", "wary", "wasteful", "watchful", "waterlogged", "watery", "wavy", "weak", "wealthy", "weary", "webbed", "wee", "weekly", "weepy", "weighty", "weird", "welcome", "well-documented", "well-groomed", "well-informed", "well-lit", "well-made", "well-off", "well-to-do", "well-worn", "wet", "which", "whimsical", "whirlwind", "whispered", "whispering", "white", "whole", "wholesale", "whopping", "wicked", "wide", "wide-eyed", "wiggly", "wild", "willing", "wilted", "winding", "windy", "winged", "wiry", "wise", "wistful", "witty", "wobbly", "woebegone", "woeful", "womanly", "wonderful", "wooden", "woozy", "wordy", "workable", "worldly", "worn", "worried", "worrisome", "worse", "worst", "worthless", "worthwhile", "worthy", "wrathful", "wretched", "writhing", "wrong", "wry"],
|
||||
"x":
|
||||
["xenophobic"],
|
||||
"y":
|
||||
["yawning", "yearly", "yellow", "yellowish", "yielding", "young", "youthful", "yummy"],
|
||||
"z":
|
||||
["zany", "zealous", "zesty", "zigzag", "zippy", "zonked"]
|
||||
}
|
88
lib/tasks/versions/generate_release_name.rake
Normal file
88
lib/tasks/versions/generate_release_name.rake
Normal file
|
@ -0,0 +1,88 @@
|
|||
namespace :versions do
|
||||
desc 'Generate a new release name'
|
||||
task generate_release_name: :environment do
|
||||
def rand_el(arr)
|
||||
arr[rand(arr.count)]
|
||||
end
|
||||
|
||||
# Load the data from JSON
|
||||
adjectives = JSON.parse(File.read('lib/tasks/versions/adjectives.json'))
|
||||
scientists = JSON.parse(File.read('lib/tasks/versions/scientists.json'))
|
||||
|
||||
puts '------------------------------------'
|
||||
puts ''
|
||||
puts 'sciNote release name generator v0.1 ALPHA'
|
||||
puts ''
|
||||
puts '------------------------------------'
|
||||
|
||||
puts ''
|
||||
puts 'Choose what you would like to do:'
|
||||
puts '1) Provide a scientist by yourself'
|
||||
puts '2) Randomly choose a scientist from a pre-defined list'
|
||||
res = $stdin.gets.strip
|
||||
unless res.in?(['', '1', '2'])
|
||||
puts 'Invalid parameter, exiting'
|
||||
next
|
||||
end
|
||||
|
||||
# First, pick scientist name
|
||||
if res.in?(['', '1'])
|
||||
puts 'Enter full scientist first name (all but surname) ' \
|
||||
'in capitalized case'
|
||||
first_name = $stdin.gets.strip
|
||||
puts 'Enter full scientist surname ' \
|
||||
'in capitalized case'
|
||||
last_name = $stdin.gets.strip
|
||||
key = last_name[0].downcase.to_sym
|
||||
full_name = "#{first_name} #{last_name}"
|
||||
else
|
||||
key = rand_el(scientists.keys)
|
||||
full_name = rand_el(scientists[key])
|
||||
last_name = full_name.split(' ')[-1]
|
||||
puts "Randomly chosen scientist: #{full_name}"
|
||||
puts ''
|
||||
end
|
||||
|
||||
# Now, pick adjective
|
||||
adjective = rand_el(adjectives[key])
|
||||
|
||||
puts '------------------------------------'
|
||||
puts 'Tadaaaa!'
|
||||
puts 'The new release will be named......'
|
||||
puts '(waaaaait for iiiiit)'
|
||||
puts ''
|
||||
puts '##############################################'
|
||||
puts " #{adjective.capitalize} #{last_name}"
|
||||
puts " (full name: #{full_name})"
|
||||
puts '##############################################'
|
||||
|
||||
loop do
|
||||
puts ''
|
||||
puts 'What would you like to do?'
|
||||
puts '(E) Exit'
|
||||
puts '(a) generate new adjective'
|
||||
puts '(s) generate new random scientist'
|
||||
res = $stdin.gets.strip
|
||||
unless res.in?(['', 'e', 'E', 'a', 'A', 's', 'S'])
|
||||
puts 'Invalid parameter!'
|
||||
next
|
||||
end
|
||||
|
||||
break if res.in?(['', 'e', 'E'])
|
||||
|
||||
if res.in?(%w(s S))
|
||||
key = rand_el(scientists.keys)
|
||||
full_name = rand_el(scientists[key])
|
||||
last_name = full_name.split(' ')[-1]
|
||||
end
|
||||
|
||||
adjective = rand_el(adjectives[key])
|
||||
|
||||
puts ''
|
||||
puts '##############################################'
|
||||
puts " #{adjective.capitalize} #{last_name} "
|
||||
puts " (full name: #{full_name})"
|
||||
puts '##############################################'
|
||||
end
|
||||
end
|
||||
end
|
54
lib/tasks/versions/scientists.json
Normal file
54
lib/tasks/versions/scientists.json
Normal file
|
@ -0,0 +1,54 @@
|
|||
{
|
||||
"a":
|
||||
["Louis Agassiz", "Maria Gaetana Agnesi", "Al-Battani", "Abu Nasr Al-Farabi", "Jim Al-Khalili", "Muhammad ibn Musa al-Khwarizmi", "Mihailo Petrovic Alas", "Angel Alcala", "Salim Ali", "Luis Alvarez", "Andre Marie Ampère", "Anaximander", "Mary Anning", "Virginia Apgar", "Archimedes", "Agnes Arber", "Aristarchus", "Aristotle", "Svante Arrhenius", "Oswald Avery", "Amedeo Avogadro", "Avicenna"],
|
||||
"b":
|
||||
["Charles Babbage", "Francis Bacon", "Alexander Bain", "John Logie Baird", "Joseph Banks", "Ramon Barba", "John Bardeen", "Ibn Battuta", "William Bayliss", "George Beadle", "Arnold Orville Beckman", "Henri Becquerel", "Emil Adolf Behring", "Alexander Graham Bell", "Emile Berliner", "Claude Bernard", "Timothy John Berners-Lee", "Daniel Bernoulli", "Jacob Berzelius", "Henry Bessemer", "Hans Bethe", "Homi Jehangir Bhabha", "Alfred Binet", "Clarence Birdseye", "Kristian Birkeland", "Elizabeth Blackwell", "Alfred Blalock", "Katharine Burr Blodgett", "Franz Boas", "David Bohm", "Aage Bohr", "Niels Bohr", "Ludwig Boltzmann", "Max Born", "Carl Bosch", "Robert Bosch", "Jagadish Chandra Bose", "Satyendra Nath Bose", "Walther Wilhelm Georg Bothe", "Robert Boyle", "Lawrence Bragg", "Tycho Brahe", "Brahmagupta", "Georg Brandt", "Wernher Von Braun", "Louis de Broglie", "Alexander Brongniart", "Robert Brown", "Michael E. Brown", "Lester R. Brown", "Eduard Buchner", "William Buckland", "Georges-Louis Leclerc, Comte de Buffon", "Robert Bunsen", "Luther Burbank", "Jocelyn Bell Burnell", "Thomas Burnet"],
|
||||
"c":
|
||||
["Benjamin Cabrera", "Santiago Ramon y Cajal", "Rachel Carson", "George Washington Carver", "Henry Cavendish", "Anders Celsius", "James Chadwick", "Subrahmanyan Chandrasekhar", "Erwin Chargaff", "Noam Chomsky", "Steven Chu", "Leland Clark", "Arthur Compton", "Nicolaus Copernicus", "Gerty Theresa Cori", "Charles-Augustin de Coulomb", "Jacques Cousteau", "Brian Cox", "Francis Crick", "Nicholas Culpeper", "Marie Curie", "Pierre Curie", "Georges Cuvier", "Adalbert Czerny"],
|
||||
"d":
|
||||
["Gottlieb Daimler", "John Dalton", "James Dwight Dana", "Charles Darwin", "Humphry Davy", "Peter Debye", "Max Delbruck", "Jean Andre Deluc", "René Descartes", "Rudolf Christian Karl Diesel", "Paul Dirac", "Prokop Divis", "Theodosius Dobzhansky", "Frank Drake", "K. Eric Drexler"],
|
||||
"e":
|
||||
["Arthur Eddington", "Thomas Edison", "Paul Ehrlich", "Albert Einstein", "Gertrude Elion", "Empedocles", "Eratosthenes", "Euclid", "Leonhard Euler"],
|
||||
"f":
|
||||
["Michael Faraday", "Pierre de Fermat", "Enrico Fermi", "Richard Feynman", "Fibonacci – Leonardo of Pisa", "Emil Fischer", "Ronald Fisher", "Alexander Fleming", "Henry Ford", "Lee De Forest", "Dian Fossey", "Leon Foucault", "Benjamin Franklin", "Rosalind Franklin", "Sigmund Freud"],
|
||||
"g":
|
||||
["Galen", "Galileo Galilei", "Francis Galton", "Luigi Galvani", "George Gamow", "Carl Friedrich Gauss", "Murray Gell-Mann", "Sophie Germain", "Willard Gibbs", "William Gilbert", "Sheldon Lee Glashow", "Robert Goddard", "Maria Goeppert-Mayer", "Jane Goodall", "Stephen Jay Gould"],
|
||||
"h":
|
||||
["Fritz Haber", "Ernst Haeckel", "Otto Hahn", "Albrecht von Haller", "Edmund Halley", "Thomas Harriot", "William Harvey", "Stephen Hawking", "Otto Haxel", "Werner Heisenberg", "Hermann von Helmholtz", "Jan Baptist von Helmont", "Joseph Henry", "William Herschel", "Gustav Ludwig Hertz", "Heinrich Hertz", "Karl F. Herzfeld", "Antony Hewish", "David Hilbert", "Maurice Hilleman", "Hipparchus", "Hippocrates", "Shintaro Hirase", "Dorothy Hodgkin", "Robert Hooke", "Frederick Gowland Hopkins", "William Hopkins", "Grace Murray Hopper", "Frank Hornby", "Jack Horner", "Bernardo Houssay", "Fred Hoyle", "Edwin Hubble", "Alexander von Humboldt", "Zora Neale Hurston", "James Hutton", "Christiaan Huygens"],
|
||||
"i":
|
||||
["Ernesto Illy", "Ernst Ising", "Keisuke Ito"],
|
||||
"j":
|
||||
["Mae Carol Jemison", "Edward Jenner", "J. Hans D. Jensen", "Irene Joliot-Curie", "James Prescott Joule", "Percy Lavon Julian"],
|
||||
"k":
|
||||
["Michio Kaku", "Heike Kamerlingh Onnes", "Friedrich August Kekulé", "Frances Kelsey", "Pearl Kendrick", "Johannes Kepler", "Abdul Qadeer Khan", "Omar Khayyam", "Alfred Kinsey", "Gustav Kirchoff", "Robert Koch", "Emil Kraepelin", "Thomas Kuhn", "Stephanie Kwolek"],
|
||||
"l":
|
||||
["Jean-Baptiste Lamarck", "Hedy Lamarr", "Edwin Herbert Land", "Karl Landsteiner", "Pierre-Simon Laplace", "Max von Laue", "Antoine Lavoisier", "Ernest Lawrence", "Henrietta Leavitt", "Antonie van Leeuwenhoek", "Inge Lehmann", "Gottfried Leibniz", "Georges Lemaître", "Leonardo da Vinci", "Niccolo Leoniceno", "Aldo Leopold", "Rita Levi-Montalcini", "Claude Levi-Strauss", "Willard Frank Libby", "Justus von Liebig", "Carolus Linnaeus", "Joseph Lister", "John Locke", "Hendrik Antoon Lorentz", "Konrad Lorenz", "Ada Lovelace", "Lucretius", "Charles Lyell", "Trofim Lysenko"],
|
||||
"m":
|
||||
["Ernst Mach", "Marcello Malpighi", "Jane Marcet", "Guglielmo Marconi", "Lynn Margulis", "James Clerk Maxwell", "Ernst Mayr", "Barbara McClintock", "Lise Meitner", "Gregor Mendel", "Dmitri Mendeleev", "Franz Mesmer", "Antonio Meucci", "Albert Abraham Michelson", "Thomas Midgeley Jr.", "Maria Mitchell", "Mario Molina", "Thomas Hunt Morgan", "Henry Moseley"],
|
||||
"n":
|
||||
["Ukichiro Nakaya", "John Napier", "John Needham", "John von Neumann", "Thomas Newcomen", "Isaac Newton", "Florence Nightingale", "Tim Noakes", "Alfred Nobel", "Emmy Noether", "Christiane Nusslein-Volhard", "Bill Nye"],
|
||||
"o":
|
||||
["Hans Christian Oersted", "Georg Ohm", "J. Robert Oppenheimer", "Wilhelm Ostwald"],
|
||||
"p":
|
||||
["Blaise Pascal", "Louis Pasteur", "Wolfgang Ernst Pauli", "Linus Pauling", "Randy Pausch", "Ivan Pavlov", "Marguerite Perey", "Jean Piaget", "Philippe Pinel", "Max Planck", "Pliny the Elder", "Karl Popper", "Beatrix Potter", "Joseph Priestley", "Claudius Ptolemy", "Pythagoras"],
|
||||
"q":
|
||||
["Harriet Quimby", "Thabit ibn Qurra"],
|
||||
"r":
|
||||
["C. V. Raman", "Srinivasa Ramanujan", "William Ramsay", "John Ray", "Prafulla Chandra Ray", "Francesco Redi", "Sally Ride", "Bernhard Riemann", "Wilhelm Röntgen", "Hermann Rorschach", "Ronald Ross", "Ibn Rushd", "Ernest Rutherford"],
|
||||
"s":
|
||||
["Carl Sagan", "Mohammad Abdus Salam", "Jonas Salk", "Frederick Sanger", "Alberto Santos-Dumont", "Walter Schottky", "Erwin Schrödinger", "Theodor Schwann", "Glenn Seaborg", "Hans Selye", "Charles Sherrington", "Gene Shoemaker", "Ernst Werner von Siemens", "George Gaylord Simpson", "B. F. Skinner", "William Smith", "Frederick Soddy", "Arnold Sommerfeld", "Nettie Stevens", "William John Swainson", "Leo Szilard"],
|
||||
"t":
|
||||
["Niccolo Tartaglia", "Edward Teller", "Nikola Tesla", "Thales of Miletus", "Benjamin Thompson", "J. J. Thomson", "William Thomson", "Henry David Thoreau", "Kip S. Thorne", "Clyde Tombaugh", "Evangelista Torricelli", "Charles Townes", "Alan Turing", "Neil deGrasse Tyson"],
|
||||
"u":
|
||||
["Harold Urey"],
|
||||
"v":
|
||||
["Craig Venter", "Vladimir Vernadsky", "Andreas Vesalius", "Rudolf Virchow", "Artturi Virtanen", "Alessandro Volta"],
|
||||
"w":
|
||||
["George Wald", "Alfred Russel Wallace", "James Watson", "James Watt", "Alfred Wegener", "John Archibald Wheeler", "Maurice Wilkins", "Thomas Willis", "E. O. Wilson", "Sven Wingqvist", "Sergei Winogradsky", "Friedrich Wöhler", "Wilbur and Orville Wright", "Wilhelm Wundt"],
|
||||
"x":
|
||||
[],
|
||||
"y":
|
||||
["Chen-Ning Yang"],
|
||||
"z":
|
||||
["Ahmed Zewail"]
|
||||
}
|
|
@ -51,6 +51,7 @@
|
|||
"extract-text-webpack-plugin": "^3.0.0",
|
||||
"file-loader": "^0.11.2",
|
||||
"glob": "^7.1.2",
|
||||
"immutability-helper": "^2.3.0",
|
||||
"js-yaml": "^3.9.0",
|
||||
"lodash": "^4.17.4",
|
||||
"moment": "^2.18.1",
|
||||
|
|
|
@ -28,7 +28,6 @@ describe User, type: :model do
|
|||
it { should have_db_column :confirmed_at }
|
||||
it { should have_db_column :confirmation_sent_at }
|
||||
it { should have_db_column :unconfirmed_email }
|
||||
it { should have_db_column :time_zone }
|
||||
it { should have_db_column :invitation_token }
|
||||
it { should have_db_column :invitation_created_at }
|
||||
it { should have_db_column :invitation_sent_at }
|
||||
|
@ -38,12 +37,8 @@ describe User, type: :model do
|
|||
it { should have_db_column :invited_by_type }
|
||||
it { should have_db_column :invitations_count }
|
||||
it { should have_db_column :tutorial_status }
|
||||
it { should have_db_column :assignments_notification }
|
||||
it { should have_db_column :recent_notification }
|
||||
it { should have_db_column :assignments_notification_email }
|
||||
it { should have_db_column :recent_notification_email }
|
||||
it { should have_db_column :settings }
|
||||
it { should have_db_column :current_team_id }
|
||||
it { should have_db_column :system_message_notification_email }
|
||||
it { should have_db_column :authentication_token }
|
||||
end
|
||||
|
||||
|
@ -115,7 +110,7 @@ describe User, type: :model do
|
|||
it { should validate_presence_of :full_name }
|
||||
it { should validate_presence_of :initials }
|
||||
it { should validate_presence_of :email }
|
||||
it { should validate_presence_of :time_zone }
|
||||
it { should validate_presence_of :settings }
|
||||
|
||||
it do
|
||||
should validate_length_of(:full_name).is_at_most(
|
||||
|
|
|
@ -2756,6 +2756,12 @@ ignore@^3.2.0:
|
|||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.3.tgz#432352e57accd87ab3110e82d3fea0e47812156d"
|
||||
|
||||
immutability-helper@^2.3.0:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/immutability-helper/-/immutability-helper-2.3.1.tgz#8ccfce92157208c120b2afad7ed05c11114c086e"
|
||||
dependencies:
|
||||
invariant "^2.2.0"
|
||||
|
||||
imurmurhash@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
|
||||
|
@ -2845,7 +2851,7 @@ intl-relativeformat@^1.3.0:
|
|||
dependencies:
|
||||
intl-messageformat "1.3.0"
|
||||
|
||||
invariant@^2.0.0, invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.1, invariant@^2.2.2:
|
||||
invariant@^2.0.0, invariant@^2.1.0, invariant@^2.1.1, invariant@^2.2.0, invariant@^2.2.1, invariant@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.2.tgz#9e1f56ac0acdb6bf303306f338be3b204ae60360"
|
||||
dependencies:
|
||||
|
|
Loading…
Add table
Reference in a new issue