Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Projects list #51

Merged
merged 19 commits into from
Sep 22, 2020
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
c2c5fcb
Add static client libraries used to project, include them in setup.py
AmandaBirmingham Jul 28, 2020
60c8beb
Extensive work to set up datatable with groupable columns filled from…
AmandaBirmingham Jul 28, 2020
c7a7027
Fine-tune project create/update modal, replace "create project" menu …
AmandaBirmingham Jul 29, 2020
426a875
added list/update/create functionality for new manage_projects endpoi…
AmandaBirmingham Jul 29, 2020
b4bd66c
fixed bug in error handling that tried to call response.json() on err…
AmandaBirmingham Jul 30, 2020
4865b18
beefed up error handling a bit; definitely still not production level
AmandaBirmingham Jul 30, 2020
8c1d45a
Added javascript to unhide all table columns when modal is opened, bc…
AmandaBirmingham Jul 30, 2020
1e22ab1
Merge branch 'master' of https://github.com/biocore/microsetta-admin …
AmandaBirmingham Aug 13, 2020
b0d1378
Merge branch 'master' of https://github.com/biocore/microsetta-admin …
AmandaBirmingham Aug 20, 2020
12fea08
added bunch of new computed statistics columns per Edgar requests
AmandaBirmingham Aug 20, 2020
2ecd61f
Merge branch 'master' of https://github.com/biocore/microsetta-admin …
AmandaBirmingham Sep 3, 2020
6535175
add wait message on loading of projects list
AmandaBirmingham Sep 3, 2020
24d7ae0
Merge branch 'master' of https://github.com/biocore/microsetta-admin …
AmandaBirmingham Sep 4, 2020
ea37db8
added more error handling in manage_projects, removed create_project
AmandaBirmingham Sep 9, 2020
c2bb177
added wait message to reloads of project list from project list page;…
AmandaBirmingham Sep 9, 2020
7e6ad18
tests of manage_projects routes
AmandaBirmingham Sep 9, 2020
e37a4bf
semicolons ;)
AmandaBirmingham Sep 9, 2020
eca9a4d
remove create_project.html template, rename manage_projects.html temp…
AmandaBirmingham Sep 9, 2020
6821fa5
fixes for code-review comments
AmandaBirmingham Sep 14, 2020
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions microsetta_admin/_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,11 @@ def _check_response(response):
if response.status_code == 401:
# redirect to home page for login
output = redirect("/")
elif response.status_code > 400:
if response.text:
output = response.text
else:
output = "unknown error"
else:
if response.text:
output = response.json()
Expand Down
84 changes: 56 additions & 28 deletions microsetta_admin/server.py
Original file line number Diff line number Diff line change
Expand Up @@ -154,39 +154,67 @@ def _search(resource=None):
return result


@app.route('/create_project', methods=['GET', 'POST'])
def new_project():
if request.method == 'GET':
return render_template('create_project.html',
**build_login_variables())
elif request.method == 'POST':
project_name = request.form['project_name']
is_microsetta = request.form.get('is_microsetta', 'No') == 'Yes'
bank_samples = request.form.get('bank_samples', 'No') == 'Yes'
plating_start_date = request.form.get('plating_start_date')
if plating_start_date == '':
plating_start_date = None

status, result = APIRequest.post('/api/admin/create/project',
json={'project_name': project_name,
'is_microsetta': is_microsetta,
'bank_samples': bank_samples,
'plating_start_date':
plating_start_date
})

if status == 201:
return render_template('create_project.html', message='Created!',
**build_login_variables())
def _translate_nones(a_dict, do_none_to_str):
# Note: this ISN'T a deep copy. This function is NOT set up
# for recursing through a multi-layer dictionary
result = a_dict.copy()
for k, v in result.items():
if do_none_to_str and v is None:
result[k] = ""
elif not do_none_to_str and v == '':
result[k] = None
return result


@app.route('/manage_projects', methods=['GET', 'POST'])
def manage_projects():
result = None
if request.method == 'POST':
projects_uri = '/api/admin/projects'

model = {x: request.form[x] for x in request.form}
project_id = model.pop('project_id')
model['is_microsetta'] = model.get('is_microsetta', False) == 'true'
model['bank_samples'] = model.get('bank_samples', False) == 'true'
model = _translate_nones(model, False)

if project_id.isdigit():
# update (put) an existing project
action = "update"
status, api_output = APIRequest.put(
'{}/{}'.format(projects_uri, project_id),
json=model)
else:
return render_template('create_project.html',
message='Unable to create',
**build_login_variables())
# create (post) a new project
action = "create"
status, api_output = APIRequest.post(
projects_uri, json=model)

# if api post or put failed
if status >= 400:
result = {'error_message': f'Unable to {action} project.'}
# end if post

# if the above work (if any) didn't produce an error message, return
# the projects list
if result is None:
status, projects_output = APIRequest.get('/api/admin/projects')

if status >= 400:
result = {'error_message': "Unable to load project list."}
else:
cleaned_projects = [_translate_nones(x, True) for x in
projects_output]
result = {'projects': cleaned_projects}

return render_template('manage_projects.html',
**build_login_variables(),
result=result), 200


@app.route('/create_kits', methods=['GET', 'POST'])
def new_kits():
_, result = APIRequest.get('/api/admin/statistics/projects')
_, result = APIRequest.get('/api/admin/projects')
projects = sorted([stats['project_name'] for stats in result])

if request.method == 'GET':
Expand Down
1 change: 1 addition & 0 deletions microsetta_admin/static/js/ruleset.js
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,7 @@ class ConsoleOutput {
}
}

// Expose any unit-testable functionality to node's module.exports
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined'){
module.exports = {
"NamedExpression": NamedExpression,
Expand Down
8 changes: 4 additions & 4 deletions microsetta_admin/static/js/testable.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
highFive = function(){ return 5; }
ret7 = function(){return 7;}
highFive = function(){ return 5; };
ret7 = function(){return 7;};

// Expose any unit testable functionality to node's module.exports
// Expose any unit-testable functionality to node's module.exports
// This will enable these functions to be called by our test suite.
// If statement prevents dereferencing null when script is included in browser.
// "if" statement prevents dereferencing null when script is included in browser.
if (typeof module !== 'undefined' && typeof module.exports !== 'undefined'){
module.exports = {
"highFive": highFive,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
@keyframes dtb-spinner {
100% {
transform: rotate(360deg);
}
}
@-o-keyframes dtb-spinner {
100% {
-o-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-ms-keyframes dtb-spinner {
100% {
-ms-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-webkit-keyframes dtb-spinner {
100% {
-webkit-transform: rotate(360deg);
transform: rotate(360deg);
}
}
@-moz-keyframes dtb-spinner {
100% {
-moz-transform: rotate(360deg);
transform: rotate(360deg);
}
}
div.dt-button-info {
position: fixed;
top: 50%;
left: 50%;
width: 400px;
margin-top: -100px;
margin-left: -200px;
background-color: white;
border: 2px solid #111;
box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.3);
border-radius: 3px;
text-align: center;
z-index: 21;
}
div.dt-button-info h2 {
padding: 0.5em;
margin: 0;
font-weight: normal;
border-bottom: 1px solid #ddd;
background-color: #f3f3f3;
}
div.dt-button-info > div {
padding: 1em;
}

div.dt-button-collection-title {
text-align: center;
padding: 0.3em 0 0.5em;
font-size: 0.9em;
}

div.dt-button-collection-title:empty {
display: none;
}

div.dt-button-collection {
position: absolute;
}
div.dt-button-collection ul.dropdown-menu {
display: block;
z-index: 2002;
min-width: 100%;
}
div.dt-button-collection div.dt-button-collection-title {
background-color: white;
}
div.dt-button-collection.fixed {
position: fixed;
top: 50%;
left: 50%;
margin-left: -75px;
border-radius: 0;
}
div.dt-button-collection.fixed.two-column {
margin-left: -200px;
}
div.dt-button-collection.fixed.three-column {
margin-left: -225px;
}
div.dt-button-collection.fixed.four-column {
margin-left: -300px;
}
div.dt-button-collection > :last-child {
display: block !important;
-webkit-column-gap: 8px;
-moz-column-gap: 8px;
-ms-column-gap: 8px;
-o-column-gap: 8px;
column-gap: 8px;
}
div.dt-button-collection > :last-child > * {
-webkit-column-break-inside: avoid;
break-inside: avoid;
}
div.dt-button-collection.two-column {
width: 400px;
}
div.dt-button-collection.two-column > :last-child {
padding-bottom: 1px;
-webkit-column-count: 2;
-moz-column-count: 2;
-ms-column-count: 2;
-o-column-count: 2;
column-count: 2;
}
div.dt-button-collection.three-column {
width: 450px;
}
div.dt-button-collection.three-column > :last-child {
padding-bottom: 1px;
-webkit-column-count: 3;
-moz-column-count: 3;
-ms-column-count: 3;
-o-column-count: 3;
column-count: 3;
}
div.dt-button-collection.four-column {
width: 600px;
}
div.dt-button-collection.four-column > :last-child {
padding-bottom: 1px;
-webkit-column-count: 4;
-moz-column-count: 4;
-ms-column-count: 4;
-o-column-count: 4;
column-count: 4;
}
div.dt-button-collection .dt-button {
border-radius: 0;
}

div.dt-button-background {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 2001;
}

@media screen and (max-width: 767px) {
div.dt-buttons {
float: none;
width: 100%;
text-align: center;
margin-bottom: 0.5em;
}
div.dt-buttons a.btn {
float: none;
}
}
div.dt-buttons button.btn.processing,
div.dt-buttons div.btn.processing,
div.dt-buttons a.btn.processing {
color: rgba(0, 0, 0, 0.2);
}
div.dt-buttons button.btn.processing:after,
div.dt-buttons div.btn.processing:after,
div.dt-buttons a.btn.processing:after {
position: absolute;
top: 50%;
left: 50%;
width: 16px;
height: 16px;
margin: -8px 0 0 -8px;
box-sizing: border-box;
display: block;
content: ' ';
border: 2px solid #282828;
border-radius: 50%;
border-left-color: transparent;
border-right-color: transparent;
animation: dtb-spinner 1500ms infinite linear;
-o-animation: dtb-spinner 1500ms infinite linear;
-ms-animation: dtb-spinner 1500ms infinite linear;
-webkit-animation: dtb-spinner 1500ms infinite linear;
-moz-animation: dtb-spinner 1500ms infinite linear;
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading