Skip to content

Commit

Permalink
Merge pull request #241 from biocore/project_banking_support
Browse files Browse the repository at this point in the history
Project banking support
  • Loading branch information
wasade committed Jul 7, 2020
2 parents 3514d9b + 8f7864c commit 65ebac7
Show file tree
Hide file tree
Showing 6 changed files with 278 additions and 24 deletions.
22 changes: 20 additions & 2 deletions microsetta_private_api/admin/admin_impl.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import flask
from flask import jsonify
import datetime

from microsetta_private_api.exceptions import RepoException
from microsetta_private_api.repo.account_repo import AccountRepo
from microsetta_private_api.repo.transaction import Transaction
from microsetta_private_api.repo.admin_repo import AdminRepo
from werkzeug.exceptions import Unauthorized
from werkzeug.exceptions import Unauthorized, BadRequest


def search_barcode(token_info, sample_barcode):
Expand Down Expand Up @@ -114,13 +116,29 @@ def create_project(body, token_info):

project_name = body['project_name']
is_microsetta = body['is_microsetta']
bank_samples = body['bank_samples']
plating_start_date = body.get('plating_start_date')

if plating_start_date is not None:
try:
plating_start_date = datetime.datetime.strptime(
plating_start_date, "%Y-%m-%d")
except ValueError:
raise BadRequest(
"plating start date '{0}' is not a valid date in YYYY-MM-DD "
"format".format(plating_start_date))

if len(project_name) == 0:
return jsonify(code=400, message="No project name provided"), 400

if not bank_samples and plating_start_date is not None:
raise RepoException("Plating start date cannot be set for"
" unbanked projects")

with Transaction() as t:
admin_repo = AdminRepo(t)
admin_repo.create_project(project_name, is_microsetta)
admin_repo.create_project(project_name, is_microsetta, bank_samples,
plating_start_date)
t.commit()

return {}, 201
Expand Down
18 changes: 12 additions & 6 deletions microsetta_private_api/admin/tests/test_admin.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from unittest import TestCase
from datetime import date

from werkzeug.exceptions import Unauthorized, NotFound

Expand Down Expand Up @@ -129,19 +130,24 @@ def test_create_project(self):
"WHERE project = 'doesnotexist'")
self.assertEqual(len(cur.fetchall()), 0)

admin_repo.create_project('doesnotexist', True)
cur.execute("SELECT project, is_microsetta "
admin_repo.create_project('doesnotexist', True, False)
cur.execute("SELECT project, is_microsetta, bank_samples, "
"plating_start_date "
"FROM barcodes.project "
"WHERE project = 'doesnotexist'")
obs = cur.fetchall()
self.assertEqual(obs, [('doesnotexist', True), ])
self.assertEqual(obs, [('doesnotexist', True, False, None), ])

admin_repo.create_project('doesnotexist2', False)
cur.execute("SELECT project, is_microsetta "
plating_start_date = date(2020, 7, 31)
admin_repo.create_project('doesnotexist2', False, True,
plating_start_date)
cur.execute("SELECT project, is_microsetta, bank_samples, "
"plating_start_date "
"FROM barcodes.project "
"WHERE project = 'doesnotexist2'")
obs = cur.fetchall()
self.assertEqual(obs, [('doesnotexist2', False), ])
self.assertEqual(obs, [('doesnotexist2', False, True,
plating_start_date), ])

def test_create_kits(self):
with Transaction() as t:
Expand Down
37 changes: 25 additions & 12 deletions microsetta_private_api/api/microsetta_private_api.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -918,7 +918,7 @@ paths:
'422':
$ref: '#/components/responses/422UnprocessableEntity'

# START ADMINISTRATOR PATHS
# ADMINISTRATOR PATHS
'/admin/search/samples/{sample_barcode}':
get:
operationId: microsetta_private_api.admin.admin_impl.search_barcode
Expand All @@ -939,6 +939,7 @@ paths:
$ref: '#/components/responses/401Unauthorized'
'404':
$ref: '#/components/responses/404NotFound'

'/admin/search/kit/{kit_id}':
get:
operationId: microsetta_private_api.admin.admin_impl.search_kit_id
Expand All @@ -964,6 +965,7 @@ paths:
$ref: '#/components/responses/401Unauthorized'
'404':
$ref: '#/components/responses/404NotFound'

'/admin/search/account/{email}':
get:
operationId: microsetta_private_api.admin.admin_impl.search_email
Expand All @@ -990,8 +992,9 @@ paths:
$ref: '#/components/responses/401Unauthorized'
'404':
$ref: '#/components/responses/404NotFound'

'/admin/scan/{sample_barcode}':
post:
put:
# Note: We might want to be able to differentiate system administrator operations
# from technician operations in the future by user accounts and the routes they post to
operationId: microsetta_private_api.admin.admin_impl.scan_barcode
Expand Down Expand Up @@ -1019,12 +1022,17 @@ paths:
type: string
example: "Sample Processing Complete!"
responses:
'201':
description: Successfully registered new user account
'200':
description: Successfully processing information about barcode
headers:
Location:
schema:
type: string
content:
application/json:
schema:
type: object

'/admin/metadata/samples/{sample_barcode}/surveys/{survey_template_id}':
get:
operationId: microsetta_private_api.admin.admin_impl.sample_pulldown_single_survey
Expand All @@ -1037,7 +1045,7 @@ paths:
- $ref: '#/components/parameters/survey_template_id'
responses:
'200':
description: QIIME compatible sample metadata
description: QIIME-compatible sample metadata
content:
application/json:
schema:
Expand All @@ -1046,6 +1054,7 @@ paths:
$ref: '#/components/responses/401Unauthorized'
'404':
$ref: '#/components/responses/404NotFound'

'/admin/metadata/samples/{sample_barcode}/surveys/':
get:
operationId: microsetta_private_api.admin.admin_impl.sample_pulldown_multiple_survey
Expand All @@ -1057,7 +1066,7 @@ paths:
- $ref: '#/components/parameters/sample_barcode'
responses:
'200':
description: QIIIME compatible sample metadata
description: QIIME-compatible sample metadata
content:
application/json:
schema:
Expand Down Expand Up @@ -1086,11 +1095,17 @@ paths:
type: string
is_microsetta:
type: boolean

bank_samples:
type: boolean
plating_start_date:
type: string
format: date
example: "2017-07-21"
nullable: true
required:
- project_name
- is_microsetta

- bank_samples
responses:
'201':
description: Project successfully created
Expand Down Expand Up @@ -1122,25 +1137,22 @@ paths:
type: array
items:
type: string

required:
- number_of_kits
- number_of_samples
- projects

responses:
'201':
description: Kit identifiers and associated samples were successfully created
content:
application/json:
schema:
type: object

'401':
$ref: '#/components/responses/401Unauthorized'

'422':
$ref: '#/components/responses/422UnprocessableEntity'

'/admin/statistics/projects':
get:
operationId: microsetta_private_api.admin.admin_impl.project_statistics_summary
Expand All @@ -1153,6 +1165,7 @@ paths:
application/json:
schema:
type: array

'/admin/statistics/project/{project_id}':
get:
operationId: microsetta_private_api.admin.admin_impl.project_statistics_detailed
Expand Down
177 changes: 177 additions & 0 deletions microsetta_private_api/api/tests/test_admin_api.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
import pytest
from unittest import TestCase
import json
import microsetta_private_api.server
from microsetta_private_api.model.account import Account, Address
from microsetta_private_api.repo.transaction import Transaction
from microsetta_private_api.repo.account_repo import AccountRepo
from microsetta_private_api.repo.admin_repo import AdminRepo
from microsetta_private_api.api.tests.test_api import client, MOCK_HEADERS, \
ACCT_ID_1, ACCT_MOCK_ISS, ACCT_MOCK_SUB # noqa

DUMMY_PROJ_NAME = "test project"


def teardown_test_data():
with Transaction() as t:
acct_repo = AccountRepo(t)
admin_repo = AdminRepo(t)
acct_repo.delete_account(ACCT_ID_1)
admin_repo.delete_project_by_name(DUMMY_PROJ_NAME)
t.commit()


def setup_test_data():
teardown_test_data()

with Transaction() as t:
acct_repo = AccountRepo(t)

acc = Account(ACCT_ID_1,
"bar@baz.com",
"admin",
ACCT_MOCK_ISS,
ACCT_MOCK_SUB,
"Dan",
"H",
Address(
"456 Dan Lane",
"Danville",
"CA",
12345,
"US"
),
"fakekit")
acct_repo.create_account(acc)
t.commit()


@pytest.mark.usefixtures("client")
class AdminApiTests(TestCase):

def setUp(self):
app = microsetta_private_api.server.build_app()
self.client = app.app.test_client()
self.client.__enter__()
setup_test_data()

def tearDown(self):
self.client.__exit__(None, None, None)
teardown_test_data()

def _test_project_create_success(self, project_info):
input_json = json.dumps(project_info)

# execute project post (create)
response = self.client.post(
"/api/admin/create/project",
content_type='application/json',
data=input_json,
headers=MOCK_HEADERS
)

# check for successful create response code
self.assertEqual(201, response.status_code)

def test_project_create_success_unbanked_no_date(self):
"""Successfully create a new, unbanked project, do not pass date"""

# create post input json without a date field
project_info = {
"project_name": DUMMY_PROJ_NAME,
"is_microsetta": False,
"bank_samples": False
}
self._test_project_create_success(project_info)

def test_project_create_success_banked_no_date(self):
"""Successfully create a new banked project with no plating date"""

# create post input json without a date field
project_info = {
"project_name": DUMMY_PROJ_NAME,
"is_microsetta": False,
"bank_samples": True
}
self._test_project_create_success(project_info)

def test_project_create_success_banked_blank_date(self):
"""Successfully create a new project banked till an unspecified date"""

# create post input json with a blank date field
project_info = {
"project_name": DUMMY_PROJ_NAME,
"is_microsetta": False,
"bank_samples": True,
"plating_start_date": None
}
self._test_project_create_success(project_info)

def test_project_create_success_banked_real_date(self):
"""Successfully create a new project banked till a specific date"""

# create post input json
project_info = {
"project_name": DUMMY_PROJ_NAME,
"is_microsetta": False,
"bank_samples": True,
"plating_start_date": "2020-07-31"
}
self._test_project_create_success(project_info)

def test_project_create_success_not_banked_blank_date(self):
"""Successfully create a new unbanked project with a blank date"""

# create post input json with a blank date field
project_info = {
"project_name": DUMMY_PROJ_NAME,
"is_microsetta": False,
"bank_samples": False,
"plating_start_date": None
}
self._test_project_create_success(project_info)

def test_project_create_fail_not_banked_with_date(self):
"""Disallow creating a new unbanked project with a plating date"""

# create post input json
project_info = {
"project_name": DUMMY_PROJ_NAME,
"is_microsetta": False,
"bank_samples": False,
"plating_start_date": "2020-07-31"
}
input_json = json.dumps(project_info)

# execute project post (create)
response = self.client.post(
"/api/admin/create/project",
content_type='application/json',
data=input_json,
headers=MOCK_HEADERS
)

# check response code
self.assertEqual(422, response.status_code)

def test_project_create_fail_banked_nonsense_date(self):
"""Disallow creating a new banked project with a nonsense date"""

# create post input json with a nonsense date field
project_info = {
"project_name": DUMMY_PROJ_NAME,
"is_microsetta": False,
"bank_samples": True,
"plating_start_date": "red"
}
input_json = json.dumps(project_info)

# execute project post (create)
response = self.client.post(
"/api/admin/create/project",
content_type='application/json',
data=input_json,
headers=MOCK_HEADERS
)

self.assertEqual(400, response.status_code)

0 comments on commit 65ebac7

Please sign in to comment.