Merge pull request #17 from sartography/dev

Dev --> Staging
This commit is contained in:
Aaron Louie 2020-05-30 20:32:34 -04:00 committed by GitHub
commit b74cb502e1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 379 additions and 216 deletions

2
.gitignore vendored
View File

@ -1,5 +1,7 @@
.idea .idea
__pycache__/ __pycache__/
app.db app.db
pb/static/.webassets-cache*
pb/static/*.css
static/.webassets-cache* static/.webassets-cache*
static/*.css static/*.css

View File

@ -1,31 +1,22 @@
FROM python:3.7 FROM python:3.7-slim
ENV PATH=/root/.local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin WORKDIR /app
COPY Pipfile Pipfile.lock /app/
# install node and yarn
RUN apt-get update
RUN apt-get -y install postgresql-client libpcre3 libpcre3-dev
# config project dir
RUN mkdir /protocol-builder-mock
WORKDIR /protocol-builder-mock
# install python requirements
RUN pip install pipenv
ADD Pipfile /protocol-builder-mock/
ADD Pipfile.lock /protocol-builder-mock/
RUN pipenv install --dev
# include rejoiner code (gets overriden by local changes)
COPY . /protocol-builder-mock/
ENV FLASK_APP=/protocol-builder-mock/app.py
# run webserver by default
CMD ["pipenv", "run", "flask", "db", "upgrade"]
CMD ["pipenv", "run", "python", "/protocol-builder-mock/run.py"]
# expose ports
EXPOSE 5001
RUN set -xe \
&& pip install pipenv \
&& apt-get update -q \
&& apt-get install -y -q \
gcc python3-dev libssl-dev \
curl postgresql-client git-core \
gunicorn3 postgresql-client \
&& pipenv install --dev \
&& apt-get remove -y gcc python3-dev libssl-dev \
&& apt-get autoremove -y \
&& apt-get clean -y \
&& rm -rf /var/lib/apt/lists/* \
&& mkdir -p /app \
&& useradd _gunicorn --no-create-home --user-group
COPY . /app/
WORKDIR /app

View File

@ -4,6 +4,7 @@ url = "https://pypi.org/simple"
verify_ssl = true verify_ssl = true
[dev-packages] [dev-packages]
pbr = "*"
[packages] [packages]
flask = "*" flask = "*"
@ -21,6 +22,9 @@ marshmallow-sqlalchemy = "*"
wtforms-alchemy = "*" wtforms-alchemy = "*"
psycopg2-binary = "*" psycopg2-binary = "*"
pyscss = "*" pyscss = "*"
gunicorn = "*"
werkzeug = "*"
flask-cors = "*"
[requires] [requires]
python_version = "3.7" python_version = "3.7"

281
Pipfile.lock generated
View File

@ -1,7 +1,7 @@
{ {
"_meta": { "_meta": {
"hash": { "hash": {
"sha256": "ca92325f9ff90d6263f261dc514de998c618a3e91614f9c1987b2f0db9b72dcf" "sha256": "4897f5ad1de5dcc7a407c45a670a3e5cf332d56fa138bfe1805441aa18c195cc"
}, },
"pipfile-spec": 6, "pipfile-spec": 6,
"requires": { "requires": {
@ -38,10 +38,10 @@
}, },
"certifi": { "certifi": {
"hashes": [ "hashes": [
"sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", "sha256:1d987a998c75633c40847cc966fcf5904906c920a7f17ef374f5aa4282abd304",
"sha256:25b64c7da4cd7479594d035c08c2d809eb4aab3a26e5a990ea98cc450c320f1f" "sha256:51fcb31174be6e6664c5f69e3e1691a2d72a1a12e90f872cbdb1567eb47b6519"
], ],
"version": "==2019.11.28" "version": "==2020.4.5.1"
}, },
"chardet": { "chardet": {
"hashes": [ "hashes": [
@ -52,10 +52,10 @@
}, },
"click": { "click": {
"hashes": [ "hashes": [
"sha256:8a18b4ea89d8820c5d0c7da8a64b2c324b4dabb695804dbfea19b9be9d88c0cc", "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
"sha256:e345d143d80bf5ee7534056164e5e112ea5e22716bbb1ce727941f4c8b471b9a" "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
], ],
"version": "==7.1.1" "version": "==7.1.2"
}, },
"clickclick": { "clickclick": {
"hashes": [ "hashes": [
@ -69,11 +69,11 @@
"swagger-ui" "swagger-ui"
], ],
"hashes": [ "hashes": [
"sha256:bf32bfae6af337cfa4a8489c21516adbe5c50e3f8dc0b7ed2394ce8dde218018", "sha256:1ccfac57d4bb7adf4295ba6f5e48f5a1f66057df6a0713417766c9b5235182ee",
"sha256:c568e579f84be808e387dcb8570bb00a536891be1318718a0dad3ba90f034191" "sha256:5439e9659a89c4380d93a07acfbf3380d70be4130574de8881e5f0dfec7ad0e2"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.6.0" "version": "==2.7.0"
}, },
"decorator": { "decorator": {
"hashes": [ "hashes": [
@ -84,11 +84,11 @@
}, },
"flask": { "flask": {
"hashes": [ "hashes": [
"sha256:13f9f196f330c7c2c5d7a5cf91af894110ca0215ac051b5844701f2bfd934d52", "sha256:4efa1ae2d7c9865af48986de8aeb8504bf32c7f3d6fdc9353d34b21f4b127060",
"sha256:45eb5a6fd193d6cf7e0cf5d8a5b31f83d5faae0293695626f539a823e93b13f6" "sha256:8a4fdd8936eba2512e9c85df320a37e694c93945b33ef33c89946a340a238557"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.1.1" "version": "==1.1.2"
}, },
"flask-assets": { "flask-assets": {
"hashes": [ "hashes": [
@ -105,13 +105,21 @@
], ],
"version": "==1.0.0" "version": "==1.0.0"
}, },
"flask-marshmallow": { "flask-cors": {
"hashes": [ "hashes": [
"sha256:01520ef1851ccb64d4ffb33196cddff895cc1302ae1585bff1abf58684a8111a", "sha256:72170423eb4612f0847318afff8c247b38bd516b7737adfc10d1c2cdbb382d16",
"sha256:28b969193958d9602ab5d6add6d280e0e360c8e373d3492c2f73b024ecd36374" "sha256:f4d97201660e6bbcff2d89d082b5b6d31abee04b1b3003ee073a6fd25ad1d69a"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.11.0" "version": "==3.0.8"
},
"flask-marshmallow": {
"hashes": [
"sha256:6e6aec171b8e092e0eafaf035ff5b8637bf3a58ab46f568c4c1bab02f2a3c196",
"sha256:a1685536e7ab5abdc712bbc1ac1a6b0b50951a368502f7985e7d1c27b3c21e59"
],
"index": "pypi",
"version": "==0.12.0"
}, },
"flask-migrate": { "flask-migrate": {
"hashes": [ "hashes": [
@ -123,11 +131,11 @@
}, },
"flask-sqlalchemy": { "flask-sqlalchemy": {
"hashes": [ "hashes": [
"sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", "sha256:2298f6b874c2a2f1f048eaf21ce5d984e36a04ca849b0ac473050a67c8dae76f",
"sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" "sha256:6cd9f71a97ef18ca5ae7d8bd316a32b82814efe7b088096ba68fddfd8a17cbe7"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.4.1" "version": "==2.4.2"
}, },
"flask-table": { "flask-table": {
"hashes": [ "hashes": [
@ -146,32 +154,31 @@
}, },
"gevent": { "gevent": {
"hashes": [ "hashes": [
"sha256:0774babec518a24d9a7231d4e689931f31b332c4517a771e532002614e270a64", "sha256:00b03601b8dd1ee2aa07811cb60a4befe36173b15d91c6e207e37f8d77dd6fac",
"sha256:0e1e5b73a445fe82d40907322e1e0eec6a6745ca3cea19291c6f9f50117bb7ea", "sha256:0acc15ba2ac2a555529ad82d5a28fc85dbb6b2ff947657d67bebfd352e2b5c14",
"sha256:0ff2b70e8e338cf13bedf146b8c29d475e2a544b5d1fe14045aee827c073842c", "sha256:15eae3cd450dac7dae7f4ac59e01db1378965c9ef565c39c5ae78c5a888f9ac9",
"sha256:107f4232db2172f7e8429ed7779c10f2ed16616d75ffbe77e0e0c3fcdeb51a51", "sha256:1dc7f1f6bc1f67d625e4272b01e717eba0b4fa024d2ff7934c8d320674d6f7fa",
"sha256:14b4d06d19d39a440e72253f77067d27209c67e7611e352f79fe69e0f618f76e", "sha256:1dd95433be45e1115053878366e3f5332ae99c39cb345be23851327c062b9f4a",
"sha256:1b7d3a285978b27b469c0ff5fb5a72bcd69f4306dbbf22d7997d83209a8ba917", "sha256:28b7d83b4327ceb79668eca2049bf4b9ce66d5ace18a88335e3035b573f889fd",
"sha256:1eb7fa3b9bd9174dfe9c3b59b7a09b768ecd496debfc4976a9530a3e15c990d1", "sha256:31dc5d4ab8172cc00c4ff17cb18edee633babd961f64bf54214244d769bc3a74",
"sha256:2711e69788ddb34c059a30186e05c55a6b611cb9e34ac343e69cf3264d42fe1c", "sha256:38db524ea88d81d596b2cbb6948fced26654a15fec40ea4529224e239a6f45e8",
"sha256:28a0c5417b464562ab9842dd1fb0cc1524e60494641d973206ec24d6ec5f6909", "sha256:3ff477b6d275396123faf8ce2d5b82f96d85ba264e0b9d4b56a2bac49d1b9adc",
"sha256:3249011d13d0c63bea72d91cec23a9cf18c25f91d1f115121e5c9113d753fa12", "sha256:4d2729dd4bf9c4d0f29482f53cdf9fc90a498aebb5cd7ae8b45d35657437d2ac",
"sha256:44089ed06a962a3a70e96353c981d628b2d4a2f2a75ea5d90f916a62d22af2e8", "sha256:52e5cd607749ed3b8aa0272cacf2c11deec61fca4c3bec57a9fea8c49316627d",
"sha256:4bfa291e3c931ff3c99a349d8857605dca029de61d74c6bb82bd46373959c942", "sha256:5c604179cebcc57f10505d8db177b92a715907815a464b066e7eba322d1c33ac",
"sha256:50024a1ee2cf04645535c5ebaeaa0a60c5ef32e262da981f4be0546b26791950", "sha256:88c76df4967c5229f853aa67ad1b394d9e4f985b0359c9bc9879416bba3e7c68",
"sha256:53b72385857e04e7faca13c613c07cab411480822ac658d97fd8a4ddbaf715c8", "sha256:929c33df8e9bcbe31906024fcd21580bd018196dbd3249eb5b2f19d63e11092d",
"sha256:74b7528f901f39c39cdbb50cdf08f1a2351725d9aebaef212a29abfbb06895ee", "sha256:92edc18a357473e01a4e4a82c073ed3c99ceca6e3ce93c23668dd4a2401f07dc",
"sha256:7d0809e2991c9784eceeadef01c27ee6a33ca09ebba6154317a257353e3af922", "sha256:937d36730f2b0dee3387712074b1f15b802e2e074a3d7c6dcaf70521236d607c",
"sha256:896b2b80931d6b13b5d9feba3d4eebc67d5e6ec54f0cf3339d08487d55d93b0e", "sha256:9b4e940fc6071afebb86ba5f48dbb5f1fc3cb96ebeb8cf145eb5b499e9c6ee33",
"sha256:8d9ec51cc06580f8c21b41fd3f2b3465197ba5b23c00eb7d422b7ae0380510b0", "sha256:a7805934e8ce81610b61f806572c3d504cedd698cc8c9460d78d2893ba598c4a",
"sha256:9f7a1e96fec45f70ad364e46de32ccacab4d80de238bd3c2edd036867ccd48ad", "sha256:d07a2afe4215731eb57d5b257a2e7e7e170d8a7ae1f02f6d0682cd3403debea9",
"sha256:ab4dc33ef0e26dc627559786a4fba0c2227f125db85d970abbf85b77506b3f51", "sha256:e01d5373528e4ebdde66dc47a608d225fa3c4408ccd828d26c49b7ff75d82bd9",
"sha256:d1e6d1f156e999edab069d79d890859806b555ce4e4da5b6418616322f0a3df1", "sha256:efd9546468502a30ddd4699c3124ccb9d3099130f9b5ae1e2a54ad5b46e86120",
"sha256:d752bcf1b98174780e2317ada12013d612f05116456133a6acf3e17d43b71f05", "sha256:fcb64f3a28420d1b872b7ef41b12e8a1a4dcadfc8eff3c09993ab0cdf52584a1"
"sha256:e5bcc4270671936349249d26140c267397b7b4b1381f5ec8b13c53c5b53ab6e1"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.4.0" "version": "==20.5.0"
}, },
"greenlet": { "greenlet": {
"hashes": [ "hashes": [
@ -201,6 +208,14 @@
"markers": "platform_python_implementation == 'CPython'", "markers": "platform_python_implementation == 'CPython'",
"version": "==0.4.15" "version": "==0.4.15"
}, },
"gunicorn": {
"hashes": [
"sha256:1904bb2b8a43658807108d59c3f3d56c2b6121a701161de0ddf9ad140073c626",
"sha256:cd4a810dd51bf497552cf3f863b575dabd73d6ad6a91075b65936b151cbf4f9c"
],
"index": "pypi",
"version": "==20.0.4"
},
"idna": { "idna": {
"hashes": [ "hashes": [
"sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb", "sha256:7588d1c14ae4c77d74036e8c22ff447b26d0fde8f007354fd48a7814db15b7cb",
@ -210,11 +225,11 @@
}, },
"importlib-metadata": { "importlib-metadata": {
"hashes": [ "hashes": [
"sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302", "sha256:2a688cbaa90e0cc587f1df48bdc97a6eadccdcd9c35fb3f976a09e3b5016d90f",
"sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b" "sha256:34513a8a0c4962bc66d35b359558fd8a5e10cd472d37aec5f66858addef32c1e"
], ],
"markers": "python_version < '3.8'", "markers": "python_version < '3.8'",
"version": "==1.5.0" "version": "==1.6.0"
}, },
"infinity": { "infinity": {
"hashes": [ "hashes": [
@ -224,9 +239,10 @@
}, },
"inflection": { "inflection": {
"hashes": [ "hashes": [
"sha256:18ea7fb7a7d152853386523def08736aa8c32636b047ade55f7578c4edeb16ca" "sha256:32a5c3341d9583ec319548b9015b7fbdf8c429cbcb575d326c33ae3a0e90d52c",
"sha256:9a15d3598f01220e93f2207c432cfede50daff53137ce660fb8be838ef1ca6cc"
], ],
"version": "==0.3.1" "version": "==0.4.0"
}, },
"intervals": { "intervals": {
"hashes": [ "hashes": [
@ -243,10 +259,10 @@
}, },
"jinja2": { "jinja2": {
"hashes": [ "hashes": [
"sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250", "sha256:89aab215427ef59c34ad58735269eb58b1a5808103067f7bb9d5836c651b3bb0",
"sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49" "sha256:f0a4641d3cf955324a89c04f3d94663aa4d638abe8f733ecd3582848e1c37035"
], ],
"version": "==2.11.1" "version": "==2.11.2"
}, },
"jsonschema": { "jsonschema": {
"hashes": [ "hashes": [
@ -315,18 +331,18 @@
}, },
"marshmallow": { "marshmallow": {
"hashes": [ "hashes": [
"sha256:90854221bbb1498d003a0c3cc9d8390259137551917961c8b5258c64026b2f85", "sha256:c2673233aa21dde264b84349dc2fd1dce5f30ed724a0a00e75426734de5b84ab",
"sha256:ac2e13b30165501b7d41fc0371b8df35944f5849769d136f20e2c5f6cdc6e665" "sha256:f88fe96434b1f0f476d54224d59333eba8ca1a203a2695683c1855675c4049a7"
], ],
"version": "==3.5.1" "version": "==3.6.0"
}, },
"marshmallow-sqlalchemy": { "marshmallow-sqlalchemy": {
"hashes": [ "hashes": [
"sha256:9301c6fd197bd97337820ea1417aa1233d0ee3e22748ebd5821799bc841a57e8", "sha256:3247e41e424146340b03a369f2b7c6f0364477ccedc4e2481e84d5f3a8d3c67f",
"sha256:dde9e20bcb710e9e59f765a38e3d6d17f1b2d6b4320cbdc2cea0f6b57f70d08c" "sha256:dbbe51d28bb28e7ee2782e51310477f7a2c5a111a301f6dd8e264e11ab820427"
], ],
"index": "pypi", "index": "pypi",
"version": "==0.22.3" "version": "==0.23.0"
}, },
"openapi-spec-validator": { "openapi-spec-validator": {
"hashes": [ "hashes": [
@ -338,41 +354,39 @@
}, },
"psycopg2-binary": { "psycopg2-binary": {
"hashes": [ "hashes": [
"sha256:040234f8a4a8dfd692662a8308d78f63f31a97e1c42d2480e5e6810c48966a29", "sha256:008da3ab51adc70a5f1cfbbe5db3a22607ab030eb44bcecf517ad11a0c2b3cac",
"sha256:086f7e89ec85a6704db51f68f0dcae432eff9300809723a6e8782c41c2f48e03", "sha256:07cf82c870ec2d2ce94d18e70c13323c89f2f2a2628cbf1feee700630be2519a",
"sha256:18ca813fdb17bc1db73fe61b196b05dd1ca2165b884dd5ec5568877cabf9b039", "sha256:08507efbe532029adee21b8d4c999170a83760d38249936038bd0602327029b5",
"sha256:19dc39616850342a2a6db70559af55b22955f86667b5f652f40c0e99253d9881", "sha256:107d9be3b614e52a192719c6bf32e8813030020ea1d1215daa86ded9a24d8b04",
"sha256:2166e770cb98f02ed5ee2b0b569d40db26788e0bf2ec3ae1a0d864ea6f1d8309", "sha256:17a0ea0b0eabf07035e5e0d520dabc7950aeb15a17c6d36128ba99b2721b25b1",
"sha256:3a2522b1d9178575acee4adf8fd9f979f9c0449b00b4164bb63c3475ea6528ed", "sha256:3286541b9d85a340ee4ed42732d15fc1bb441dc500c97243a768154ab8505bb5",
"sha256:3aa773580f85a28ffdf6f862e59cb5a3cc7ef6885121f2de3fca8d6ada4dbf3b", "sha256:3939cf75fc89c5e9ed836e228c4a63604dff95ad19aed2bbf71d5d04c15ed5ce",
"sha256:3b5deaa3ee7180585a296af33e14c9b18c218d148e735c7accf78130765a47e3", "sha256:40abc319f7f26c042a11658bf3dd3b0b3bceccf883ec1c565d5c909a90204434",
"sha256:407af6d7e46593415f216c7f56ba087a9a42bd6dc2ecb86028760aa45b802bd7", "sha256:51f7823f1b087d2020d8e8c9e6687473d3d239ba9afc162d9b2ab6e80b53f9f9",
"sha256:4c3c09fb674401f630626310bcaf6cd6285daf0d5e4c26d6e55ca26a2734e39b", "sha256:6bb2dd006a46a4a4ce95201f836194eb6a1e863f69ee5bab506673e0ca767057",
"sha256:4c6717962247445b4f9e21c962ea61d2e884fc17df5ddf5e35863b016f8a1f03", "sha256:702f09d8f77dc4794651f650828791af82f7c2efd8c91ae79e3d9fe4bb7d4c98",
"sha256:50446fae5681fc99f87e505d4e77c9407e683ab60c555ec302f9ac9bffa61103", "sha256:7036ccf715925251fac969f4da9ad37e4b7e211b1e920860148a10c0de963522",
"sha256:5057669b6a66aa9ca118a2a860159f0ee3acf837eda937bdd2a64f3431361a2d", "sha256:7b832d76cc65c092abd9505cc670c4e3421fd136fb6ea5b94efbe4c146572505",
"sha256:5dd90c5438b4f935c9d01fcbad3620253da89d19c1f5fca9158646407ed7df35", "sha256:8f74e631b67482d504d7e9cf364071fc5d54c28e79a093ff402d5f8f81e23bfa",
"sha256:659c815b5b8e2a55193ede2795c1e2349b8011497310bb936da7d4745652823b", "sha256:930315ac53dc65cbf52ab6b6d27422611f5fb461d763c531db229c7e1af6c0b3",
"sha256:69b13fdf12878b10dc6003acc8d0abf3ad93e79813fd5f3812497c1c9fb9be49", "sha256:96d3038f5bd061401996614f65d27a4ecb62d843eb4f48e212e6d129171a721f",
"sha256:7a1cb80e35e1ccea3e11a48afe65d38744a0e0bde88795cc56a4d05b6e4f9d70", "sha256:a20299ee0ea2f9cca494396ac472d6e636745652a64a418b39522c120fd0a0a4",
"sha256:7e6e3c52e6732c219c07bd97fff6c088f8df4dae3b79752ee3a817e6f32e177e", "sha256:a34826d6465c2e2bbe9d0605f944f19d2480589f89863ed5f091943be27c9de4",
"sha256:7f42a8490c4fe854325504ce7a6e4796b207960dabb2cbafe3c3959cb00d1d7e", "sha256:a69970ee896e21db4c57e398646af9edc71c003bc52a3cc77fb150240fefd266",
"sha256:84156313f258eafff716b2961644a4483a9be44a5d43551d554844d15d4d224e", "sha256:b9a8b391c2b0321e0cd7ec6b4cfcc3dd6349347bd1207d48bcb752aa6c553a66",
"sha256:8578d6b8192e4c805e85f187bc530d0f52ba86c39172e61cd51f68fddd648103", "sha256:ba13346ff6d3eb2dca0b6fa0d8a9d999eff3dcd9b55f3a890f12b0b6362b2b38",
"sha256:890167d5091279a27e2505ff0e1fb273f8c48c41d35c5b92adbf4af80e6b2ed6", "sha256:bb0608694a91db1e230b4a314e8ed00ad07ed0c518f9a69b83af2717e31291a3",
"sha256:98e10634792ac0e9e7a92a76b4991b44c2325d3e7798270a808407355e7bb0a1", "sha256:c8830b7d5f16fd79d39b21e3d94f247219036b29b30c8270314c46bf8b732389",
"sha256:9aadff9032e967865f9778485571e93908d27dab21d0fdfdec0ca779bb6f8ad9", "sha256:cac918cd7c4c498a60f5d2a61d4f0a6091c2c9490d81bc805c963444032d0dab",
"sha256:9f24f383a298a0c0f9b3113b982e21751a8ecde6615494a3f1470eb4a9d70e9e", "sha256:cc30cb900f42c8a246e2cb76539d9726f407330bc244ca7729c41a44e8d807fb",
"sha256:a73021b44813b5c84eda4a3af5826dd72356a900bac9bd9dd1f0f81ee1c22c2f", "sha256:ccdc6a87f32b491129ada4b87a43b1895cf2c20fdb7f98ad979647506ffc41b6",
"sha256:afd96845e12638d2c44d213d4810a08f4dc4a563f9a98204b7428e567014b1cd", "sha256:d1a8b01f6a964fec702d6b6dac1f91f2b9f9fe41b310cbb16c7ef1fac82df06d",
"sha256:b73ddf033d8cd4cc9dfed6324b1ad2a89ba52c410ef6877998422fcb9c23e3a8", "sha256:e004db88e5a75e5fdab1620fb9f90c9598c2a195a594225ac4ed2a6f1c23e162",
"sha256:b8f490f5fad1767a1331df1259763b3bad7d7af12a75b950c2843ba319b2415f", "sha256:eb2f43ae3037f1ef5e19339c41cf56947021ac892f668765cd65f8ab9814192e",
"sha256:dbc5cd56fff1a6152ca59445178652756f4e509f672e49ccdf3d79c1043113a4", "sha256:fa466306fcf6b39b8a61d003123d442b23707d635a5cb05ac4e1b62cc79105cd"
"sha256:eac8a3499754790187bb00574ab980df13e754777d346f85e0ff6df929bcd964",
"sha256:eaed1c65f461a959284649e37b5051224f4db6ebdc84e40b5e65f2986f101a08"
], ],
"index": "pypi", "index": "pypi",
"version": "==2.8.4" "version": "==2.8.5"
}, },
"pyrsistent": { "pyrsistent": {
"hashes": [ "hashes": [
@ -382,10 +396,10 @@
}, },
"pyscss": { "pyscss": {
"hashes": [ "hashes": [
"sha256:123c1a9087f1c420bea891ebf19d5222262c7d30ced20bb38586023de28c9d4f" "sha256:f1df571569021a23941a538eb154405dde80bed35dc1ea7c5f3e18e0144746bf"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.3.6" "version": "==1.3.7"
}, },
"python-dateutil": { "python-dateutil": {
"hashes": [ "hashes": [
@ -404,10 +418,10 @@
}, },
"pytz": { "pytz": {
"hashes": [ "hashes": [
"sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", "sha256:a494d53b6d39c3c6e44c3bec237336e14305e4f29bbf800b599253057fbb79ed",
"sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be" "sha256:c35965d010ce31b23eeb663ed3cc8c906275d6be1a34393a1d73a41febf4a048"
], ],
"version": "==2019.3" "version": "==2020.1"
}, },
"pyyaml": { "pyyaml": {
"hashes": [ "hashes": [
@ -434,23 +448,50 @@
}, },
"six": { "six": {
"hashes": [ "hashes": [
"sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
"sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
], ],
"version": "==1.14.0" "version": "==1.15.0"
}, },
"sqlalchemy": { "sqlalchemy": {
"hashes": [ "hashes": [
"sha256:c4cca4aed606297afbe90d4306b49ad3a4cd36feb3f87e4bfd655c57fd9ef445" "sha256:128bc917ed20d78143a45024455ff0aed7d3b96772eba13d5dbaf9cc57e5c41b",
"sha256:156a27548ba4e1fed944ff9fcdc150633e61d350d673ae7baaf6c25c04ac1f71",
"sha256:27e2efc8f77661c9af2681755974205e7462f1ae126f498f4fe12a8b24761d15",
"sha256:2a12f8be25b9ea3d1d5b165202181f2b7da4b3395289000284e5bb86154ce87c",
"sha256:31c043d5211aa0e0773821fcc318eb5cbe2ec916dfbc4c6eea0c5188971988eb",
"sha256:65eb3b03229f684af0cf0ad3bcc771970c1260a82a791a8d07bffb63d8c95bcc",
"sha256:6cd157ce74a911325e164441ff2d9b4e244659a25b3146310518d83202f15f7a",
"sha256:703c002277f0fbc3c04d0ae4989a174753a7554b2963c584ce2ec0cddcf2bc53",
"sha256:869bbb637de58ab0a912b7f20e9192132f9fbc47fc6b5111cd1e0f6cdf5cf9b0",
"sha256:8a0e0cd21da047ea10267c37caf12add400a92f0620c8bc09e4a6531a765d6d7",
"sha256:8d01e949a5d22e5c4800d59b50617c56125fc187fbeb8fa423e99858546de616",
"sha256:925b4fe5e7c03ed76912b75a9a41dfd682d59c0be43bce88d3b27f7f5ba028fb",
"sha256:9cb1819008f0225a7c066cac8bb0cf90847b2c4a6eb9ebb7431dbd00c56c06c5",
"sha256:a87d496884f40c94c85a647c385f4fd5887941d2609f71043e2b73f2436d9c65",
"sha256:a9030cd30caf848a13a192c5e45367e3c6f363726569a56e75dc1151ee26d859",
"sha256:a9e75e49a0f1583eee0ce93270232b8e7bb4b1edc89cc70b07600d525aef4f43",
"sha256:b50f45d0e82b4562f59f0e0ca511f65e412f2a97d790eea5f60e34e5f1aabc9a",
"sha256:b7878e59ec31f12d54b3797689402ee3b5cfcb5598f2ebf26491732758751908",
"sha256:ce1ddaadee913543ff0154021d31b134551f63428065168e756d90bdc4c686f5",
"sha256:ce2646e4c0807f3461be0653502bb48c6e91a5171d6e450367082c79e12868bf",
"sha256:ce6c3d18b2a8ce364013d47b9cad71db815df31d55918403f8db7d890c9d07ae",
"sha256:e4e2664232005bd306f878b0f167a31f944a07c4de0152c444f8c61bbe3cfb38",
"sha256:e8aa395482728de8bdcca9cc0faf3765ab483e81e01923aaa736b42f0294f570",
"sha256:eb4fcf7105bf071c71068c6eee47499ab8d4b8f5a11fc35147c934f0faa60f23",
"sha256:ed375a79f06cad285166e5be74745df1ed6845c5624aafadec4b7a29c25866ef",
"sha256:f35248f7e0d63b234a109dd72fbfb4b5cb6cb6840b221d0df0ecbf54ab087654",
"sha256:f502ef245c492b391e0e23e94cba030ab91722dcc56963c85bfd7f3441ea2bbe",
"sha256:fe01bac7226499aedf472c62fa3b85b2c619365f3f14dd222ffe4f3aa91e5f98"
], ],
"index": "pypi", "index": "pypi",
"version": "==1.3.15" "version": "==1.3.17"
}, },
"sqlalchemy-utils": { "sqlalchemy-utils": {
"hashes": [ "hashes": [
"sha256:f268af5bc03597fe7690d60df3e5f1193254a83e07e4686f720f61587ec4493a" "sha256:7a7fab14bed80df065412bbf71a0a9b0bfeb4b7c111c2d9bffe57283082f3a6b"
], ],
"version": "==0.36.3" "version": "==0.36.6"
}, },
"swagger-ui-bundle": { "swagger-ui-bundle": {
"hashes": [ "hashes": [
@ -462,16 +503,16 @@
}, },
"urllib3": { "urllib3": {
"hashes": [ "hashes": [
"sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", "sha256:3018294ebefce6572a474f0604c2021e33b3fd8006ecd11d62107a5d2a963527",
"sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" "sha256:88206b0eb87e6d677d424843ac5209e3fb9d0190d0ee169599165ec25e9d9115"
], ],
"version": "==1.25.8" "version": "==1.25.9"
}, },
"validators": { "validators": {
"hashes": [ "hashes": [
"sha256:b192e6bde7d617811d59f50584ed240b580375648cd032d106edeb3164099508" "sha256:31e8bb01b48b48940a021b8a9576b840f98fa06b91762ef921d02cb96d38727a"
], ],
"version": "==0.14.2" "version": "==0.15.0"
}, },
"webassets": { "webassets": {
"hashes": [ "hashes": [
@ -482,17 +523,18 @@
}, },
"werkzeug": { "werkzeug": {
"hashes": [ "hashes": [
"sha256:169ba8a33788476292d04186ab33b01d6add475033dfc07215e6d219cc077096", "sha256:2de2a5db0baeae7b2d2664949077c2ac63fbd16d98da0ff71837f7d1dea3fd43",
"sha256:6dc65cf9091cf750012f56f2cad759fa9e879f511b5ff8685e456b4e3bf90d16" "sha256:6c80b1e5ad3665290ea39320b91e1be1e0d5f60652b964a3070216de83d2e47c"
], ],
"version": "==1.0.0" "index": "pypi",
"version": "==1.0.1"
}, },
"wtforms": { "wtforms": {
"hashes": [ "hashes": [
"sha256:0cdbac3e7f6878086c334aa25dc5a33869a3954e9d1e015130d65a69309b3b61", "sha256:6ff8635f4caeed9f38641d48cfe019d0d3896f41910ab04494143fc027866e1b",
"sha256:e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1" "sha256:861a13b3ae521d6700dac3b2771970bd354a63ba7043ecc3a82b5288596a1972"
], ],
"version": "==2.2.1" "version": "==2.3.1"
}, },
"wtforms-alchemy": { "wtforms-alchemy": {
"hashes": [ "hashes": [
@ -515,5 +557,14 @@
"version": "==3.1.0" "version": "==3.1.0"
} }
}, },
"develop": {} "develop": {
"pbr": {
"hashes": [
"sha256:07f558fece33b05caf857474a366dfcc00562bca13dd8b47b2b3e22d9f9bf55c",
"sha256:579170e23f8e0c2f24b0de612f71f648eccb79fb1322c814ae6b3c07b5ba23e8"
],
"index": "pypi",
"version": "==5.4.5"
}
}
} }

View File

@ -1,3 +1,4 @@
import re
import os import os
from os import environ from os import environ
@ -5,10 +6,13 @@ basedir = os.path.abspath(os.path.dirname(__file__))
NAME = "CR Connect Protocol Builder Mock" NAME = "CR Connect Protocol Builder Mock"
FLASK_PORT = environ.get('PORT0') or environ.get('FLASK_PORT', default="5001") FLASK_PORT = environ.get('PORT0') or environ.get('FLASK_PORT', default="5001")
CORS_ENABLED = False CORS_ALLOW_ORIGINS = re.split(r',\s*', environ.get('CORS_ALLOW_ORIGINS', default="localhost:5000"))
DEVELOPMENT = environ.get('DEVELOPMENT', default="true") == "true" DEVELOPMENT = environ.get('DEVELOPMENT', default="true") == "true"
TESTING = environ.get('TESTING', default="false") == "true" TESTING = environ.get('TESTING', default="false") == "true"
# Add trailing slash to base path
APPLICATION_ROOT = re.sub(r'//', '/', '/%s/' % environ.get('APPLICATION_ROOT', default="/").strip('/'))
DB_HOST = environ.get('DB_HOST', default="localhost") DB_HOST = environ.get('DB_HOST', default="localhost")
DB_PORT = environ.get('DB_PORT', default="5432") DB_PORT = environ.get('DB_PORT', default="5432")
DB_NAME = environ.get('DB_NAME', default="pb") DB_NAME = environ.get('DB_NAME', default="pb")
@ -22,5 +26,7 @@ SECRET_KEY = environ.get('SECRET_KEY', default='a really really really really lo
print('=== USING DEFAULT CONFIG: ===') print('=== USING DEFAULT CONFIG: ===')
print('DB_HOST = ', DB_HOST) print('DB_HOST = ', DB_HOST)
print('CORS_ALLOW_ORIGINS = ', CORS_ALLOW_ORIGINS)
print('DEVELOPMENT = ', DEVELOPMENT) print('DEVELOPMENT = ', DEVELOPMENT)
print('TESTING = ', TESTING) print('TESTING = ', TESTING)
print('APPLICATION_ROOT = ', APPLICATION_ROOT)

View File

@ -1,6 +1,25 @@
#!/bin/bash #!/bin/bash
# run migrations # run migrations
export FLASK_APP=./app.py export FLASK_APP=/app/pb/__init__.py
pipenv run flask db upgrade
pipenv run python ./run.py if [ "$DOWNGRADE_DB" = "true" ]; then
echo 'Downgrading database...'
pipenv run flask db downgrade
fi
if [ "$UPGRADE_DB" = "true" ]; then
echo 'Upgrading database...'
pipenv run flask db upgrade
fi
if [ "$RESET_DB" = "true" ]; then
echo 'Resetting database...'
pipenv run flask load-example-data
fi
if [ "$APPLICATION_ROOT" = "/" ]; then
pipenv run gunicorn --bind 0.0.0.0:$PORT0 wsgi:app
else
pipenv run gunicorn -e SCRIPT_NAME="$APPLICATION_ROOT" --bind 0.0.0.0:$PORT0 wsgi:app
fi

View File

@ -1,16 +1,17 @@
import datetime import datetime
import os import os
import re
import yaml
from datetime import date from datetime import date
import connexion import connexion
import yaml from flask_cors import CORS
from flask import url_for, json, redirect, render_template, request, flash from flask import url_for, json, redirect, render_template, request, flash
from flask_assets import Environment, Bundle from flask_assets import Environment, Bundle
from flask_sqlalchemy import SQLAlchemy from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow from flask_marshmallow import Marshmallow
from flask_migrate import Migrate from flask_migrate import Migrate
from sqlalchemy import func from sqlalchemy import func
from wtforms.ext.appengine.db import model_form from wtforms.ext.appengine.db import model_form
PROTOCOLS = {} PROTOCOLS = {}
@ -39,11 +40,8 @@ def get_study_details(studyid):
def get_form(id, requirement_code): def get_form(id, requirement_code):
return return
connexion_app = connexion.FlaskApp('Protocol Builder', specification_dir='pb')
conn = connexion.App('Protocol Builder', specification_dir='./') app = connexion_app.app
conn.add_api('api.yml')
app = conn.app
app.config.from_object('config.default') app.config.from_object('config.default')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
@ -55,17 +53,53 @@ else:
app.config.root_path = app.instance_path app.config.root_path = app.instance_path
app.config.from_pyfile('config.py', silent=True) app.config.from_pyfile('config.py', silent=True)
connexion_app.add_api('api.yml', base_path='/v2.0')
# Convert list of allowed origins to list of regexes
origins_re = [r"^https?:\/\/%s(.*)" % o.replace('.', '\.') for o in app.config['CORS_ALLOW_ORIGINS']]
cors = CORS(connexion_app.app, origins=origins_re)
db = SQLAlchemy(app) db = SQLAlchemy(app)
migrate = Migrate(app, db) migrate = Migrate(app, db)
ma = Marshmallow(app) ma = Marshmallow(app)
# Set the path of the static directory
APP_ROOT = os.path.dirname(os.path.abspath(__file__))
APP_STATIC = os.path.join(APP_ROOT, 'static')
BASE_HREF = app.config['APPLICATION_ROOT'].strip('/')
app.static_folder = APP_STATIC
app.static_url_path = app.config['APPLICATION_ROOT'] + 'static'
print('app.static_folder', app.static_folder)
print('app.static_url_path', app.static_url_path)
# remove old static map
url_map = app.url_map
try:
for rule in url_map.iter_rules('static'):
url_map._rules.remove(rule)
except ValueError:
# no static view was created yet
pass
# register new; the same view function is used
app.add_url_rule(
app.static_url_path + '/<path:filename>',
endpoint='static', view_func=app.send_static_file)
assets = Environment(app) assets = Environment(app)
assets.init_app(app)
assets.url = app.static_url_path assets.url = app.static_url_path
scss = Bundle('scss/app.scss', filters='pyscss', output='app.css') scss = Bundle(
'scss/app.scss',
filters='pyscss',
output='app.css'
)
assets.register('app_scss', scss) assets.register('app_scss', scss)
# Loads all the descriptions from the API so we can display them in the editor. # Loads all the descriptions from the API so we can display them in the editor.
description_map = {} description_map = {}
with open(r'api.yml') as file: with open(r'pb/api.yml') as file:
api_config = yaml.load(file, Loader=yaml.FullLoader) api_config = yaml.load(file, Loader=yaml.FullLoader)
study_detail_properties = api_config['components']['schemas']['StudyDetail']['properties'] study_detail_properties = api_config['components']['schemas']['StudyDetail']['properties']
for schema in api_config['components']['schemas']: for schema in api_config['components']['schemas']:
@ -82,14 +116,14 @@ def has_no_empty_params(rule):
return len(defaults) >= len(arguments) return len(defaults) >= len(arguments)
@app.route("/site_map") @app.route('/site_map')
def site_map(): def site_map():
links = [] links = []
for rule in app.url_map.iter_rules(): for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser # Filter out rules we can't navigate to in a browser
# and rules that require parameters # and rules that require parameters
if "GET" in rule.methods and has_no_empty_params(rule): if "GET" in rule.methods and has_no_empty_params(rule):
url = url_for(rule.endpoint, **(rule.defaults or {})) url = app.confg['APPLICATION_ROOT'].strip('/') + url_for(rule.endpoint, **(rule.defaults or {}))
links.append((url, rule.endpoint)) links.append((url, rule.endpoint))
return json.dumps({"links": links}) return json.dumps({"links": links})
@ -97,8 +131,8 @@ def site_map():
# ************************** # **************************
# WEB FORMS # WEB FORMS
# ************************** # **************************
from forms import StudyForm, StudyTable, InvestigatorForm, StudyDetailsForm from pb.forms import StudyForm, StudyTable, InvestigatorForm, StudyDetailsForm
from models import Study, RequiredDocument, Investigator, StudySchema, RequiredDocumentSchema, InvestigatorSchema, \ from pb.models import Study, RequiredDocument, Investigator, StudySchema, RequiredDocumentSchema, InvestigatorSchema, \
StudyDetails, StudyDetailsSchema StudyDetails, StudyDetailsSchema
@ -107,25 +141,33 @@ def index():
# display results # display results
studies = db.session.query(Study).order_by(Study.DATE_MODIFIED.desc()).all() studies = db.session.query(Study).order_by(Study.DATE_MODIFIED.desc()).all()
table = StudyTable(studies) table = StudyTable(studies)
return render_template('index.html', table=table) return render_template(
'index.html',
table=table,
base_href=BASE_HREF
)
@app.route('/new_study', methods=['GET', 'POST']) @app.route('/new_study', methods=['GET', 'POST'])
def new_study(): def new_study():
form = StudyForm(request.form) form = StudyForm(request.form)
action = "/new_study" action = BASE_HREF + "/new_study"
title = "New Study" title = "New Study"
if request.method == 'POST': if request.method == 'POST':
study = Study() study = Study()
study.study_details = StudyDetails() study.study_details = StudyDetails()
_update_study(study, form) _update_study(study, form)
flash('Study created successfully!') flash('Study created successfully!')
return redirect('/') return redirect_home()
return render_template('form.html', form=form, return render_template(
action=action, 'form.html',
title=title, form=form,
description_map=description_map) action=action,
title=title,
description_map=description_map,
base_href=BASE_HREF
)
@app.route('/study/<study_id>', methods=['GET', 'POST']) @app.route('/study/<study_id>', methods=['GET', 'POST'])
@ -133,7 +175,7 @@ def edit_study(study_id):
study = db.session.query(Study).filter(Study.STUDYID == study_id).first() study = db.session.query(Study).filter(Study.STUDYID == study_id).first()
form = StudyForm(request.form, obj=study) form = StudyForm(request.form, obj=study)
if request.method == 'GET': if request.method == 'GET':
action = "/study/" + study_id action = BASE_HREF + "/study/" + study_id
title = "Edit Study #" + study_id title = "Edit Study #" + study_id
if study.requirements: if study.requirements:
form.requirements.data = list(map(lambda r: r.AUXDOCID, list(study.requirements))) form.requirements.data = list(map(lambda r: r.AUXDOCID, list(study.requirements)))
@ -142,17 +184,21 @@ def edit_study(study_id):
if request.method == 'POST': if request.method == 'POST':
_update_study(study, form) _update_study(study, form)
flash('Study updated successfully!') flash('Study updated successfully!')
return redirect('/') return redirect_home()
return render_template('form.html', form=form, return render_template(
action=action, 'form.html',
title=title, form=form,
description_map={}) action=action,
title=title,
description_map={},
base_href=BASE_HREF
)
@app.route('/investigator/<study_id>', methods=['GET', 'POST']) @app.route('/investigator/<study_id>', methods=['GET', 'POST'])
def new_investigator(study_id): def new_investigator(study_id):
form = InvestigatorForm(request.form) form = InvestigatorForm(request.form)
action = "/investigator/" + study_id action = BASE_HREF + "/investigator/" + study_id
title = "Add Investigator to Study " + study_id title = "Add Investigator to Study " + study_id
if request.method == 'POST': if request.method == 'POST':
investigator = Investigator(STUDYID=study_id) investigator = Investigator(STUDYID=study_id)
@ -161,19 +207,23 @@ def new_investigator(study_id):
db.session.add(investigator) db.session.add(investigator)
db.session.commit() db.session.commit()
flash('Investigator created successfully!') flash('Investigator created successfully!')
return redirect('/') return redirect_home()
return render_template('form.html', form=form, return render_template(
action=action, 'form.html',
title=title, form=form,
description_map={}) action=action,
title=title,
description_map={},
base_href=BASE_HREF
)
@app.route('/del_investigator/<inv_id>', methods=['GET']) @app.route('/del_investigator/<inv_id>', methods=['GET'])
def del_investigator(inv_id): def del_investigator(inv_id):
db.session.query(Investigator).filter(Investigator.id == inv_id).delete() db.session.query(Investigator).filter(Investigator.id == inv_id).delete()
db.session.commit() db.session.commit()
return redirect('/') return redirect_home()
@app.route('/del_study/<study_id>', methods=['GET']) @app.route('/del_study/<study_id>', methods=['GET'])
@ -183,7 +233,7 @@ def del_study(study_id):
db.session.query(StudyDetails).filter(StudyDetails.STUDYID == study_id).delete() db.session.query(StudyDetails).filter(StudyDetails.STUDYID == study_id).delete()
db.session.query(Study).filter(Study.STUDYID == study_id).delete() db.session.query(Study).filter(Study.STUDYID == study_id).delete()
db.session.commit() db.session.commit()
return redirect('/') return redirect_home()
def _update_study(study, form): def _update_study(study, form):
@ -218,7 +268,7 @@ def study_details(study_id):
study_details = StudyDetails(STUDYID=study_id) study_details = StudyDetails(STUDYID=study_id)
form = StudyDetailsForm(request.form, obj=study_details) form = StudyDetailsForm(request.form, obj=study_details)
if request.method == 'GET': if request.method == 'GET':
action = "/study_details/" + study_id action = BASE_HREF + "/study_details/" + study_id
title = "Edit Study Details for Study #" + study_id title = "Edit Study Details for Study #" + study_id
details = "Numeric fields can be 1 for true, 0 or false, or Null if not applicable." details = "Numeric fields can be 1 for true, 0 or false, or Null if not applicable."
if request.method == 'POST': if request.method == 'POST':
@ -226,12 +276,20 @@ def study_details(study_id):
db.session.add(study_details) db.session.add(study_details)
db.session.commit() db.session.commit()
flash('Study updated successfully!') flash('Study updated successfully!')
return redirect('/') return redirect_home()
return render_template('form.html', form=form, return render_template(
action=action, 'form.html',
title=title, form=form,
details=details, action=action,
description_map=description_map) title=title,
details=details,
description_map=description_map,
base_href=BASE_HREF
)
def redirect_home():
return redirect(url_for('index'))
if __name__ == '__main__': if __name__ == '__main__':

View File

@ -10,8 +10,6 @@ info:
name: Apache 2.0 name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html url: http://www.apache.org/licenses/LICENSE-2.0.html
# Added by API Auto Mocking Plugin # Added by API Auto Mocking Plugin
servers:
- url: http://localhost:5000/pb
# tags are used for organizing operations # tags are used for organizing operations
tags: tags:
- name: CR-Connect - name: CR-Connect
@ -22,7 +20,7 @@ paths:
tags: tags:
- CR-Connect - CR-Connect
summary: A list of all studies related to a given UVA ID summary: A list of all studies related to a given UVA ID
operationId: app.get_user_studies operationId: pb.get_user_studies
description: "By passing in a valid UVA Id (ex: dhf8r) it will return a list of all studies that exist for that user in Protocol Builder" description: "By passing in a valid UVA Id (ex: dhf8r) it will return a list of all studies that exist for that user in Protocol Builder"
parameters: parameters:
- in: query - in: query
@ -51,7 +49,7 @@ paths:
tags: tags:
- CR-Connect - CR-Connect
summary: Required documents summary: Required documents
operationId: app.required_docs operationId: pb.required_docs
description: A list of all documents Protocol Builder considers required, given input from the PI description: A list of all documents Protocol Builder considers required, given input from the PI
parameters: parameters:
- in: query - in: query
@ -74,7 +72,7 @@ paths:
tags: tags:
- CR-Connect - CR-Connect
summary: Personnel associated with this study. summary: Personnel associated with this study.
operationId: app.investigators operationId: pb.investigators
description: A list of everyone that is associated with the study, including the PI, Study Coordinator, etc... This is currently returned on the "study" endpoint with other information. description: A list of everyone that is associated with the study, including the PI, Study Coordinator, etc... This is currently returned on the "study" endpoint with other information.
parameters: parameters:
- in: query - in: query
@ -103,7 +101,7 @@ paths:
get: get:
tags: tags:
- CR-Connect - CR-Connect
operationId: app.get_study_details operationId: pb.get_study_details
summary: Details about a specific protocol. summary: Details about a specific protocol.
responses: responses:
200: 200:

View File

@ -1,12 +1,9 @@
import sys from flask_table import Table, Col, LinkCol, BoolCol, DatetimeCol, NestedTableCol
from flask_table import Table, Col, DateCol, LinkCol, BoolCol, DatetimeCol, NestedTableCol
from flask_wtf import FlaskForm from flask_wtf import FlaskForm
from wtforms import SelectMultipleField, SubmitField, StringField, IntegerField, BooleanField, DateField, widgets, \ from wtforms import SelectMultipleField, StringField, BooleanField, SelectField, validators, HiddenField
SelectField, validators, HiddenField
from wtforms_alchemy import ModelForm from wtforms_alchemy import ModelForm
from models import RequiredDocument, Investigator, StudyDetails from pb.models import RequiredDocument, Investigator, StudyDetails
class StudyForm(FlaskForm): class StudyForm(FlaskForm):

View File

@ -1,5 +1,5 @@
from sqlalchemy import func from sqlalchemy import func
from app import db, ma from pb import db, ma
class Study(db.Model): class Study(db.Model):

View File

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 15 KiB

2
run.py
View File

@ -1,4 +1,4 @@
from app import app from pb import app
if __name__ == "__main__": if __name__ == "__main__":
flask_port = app.config['FLASK_PORT'] flask_port = app.config['FLASK_PORT']
app.run(host='0.0.0.0', port=flask_port) app.run(host='0.0.0.0', port=flask_port)

5
setup.cfg Normal file
View File

@ -0,0 +1,5 @@
[metadata]
name = pb
[files]
packages = pb

3
setup.py Normal file
View File

@ -0,0 +1,3 @@
from setuptools import setup
setup(setup_requires=["pbr"], pbr=True)

View File

@ -3,11 +3,13 @@
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<title>Protocol Builder Mock Configuration</title> <title>Protocol Builder Mock Configuration</title>
<base href="/">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://use.typekit.net/kwp6dli.css"> <link rel="stylesheet" href="https://use.typekit.net/kwp6dli.css">
{% assets 'app_scss' %} {% assets 'app_scss' %}
<link href="{{ ASSET_URL }}" rel="stylesheet" type="text/css"> <link href="{{ base_href + ASSET_URL }}" rel="stylesheet" type="text/css">
{% endassets %} {% endassets %}
<link rel="shortcut icon" href="{{ base_href + url_for('static', filename='favicon.ico') }}">
</head> </head>
<body> <body>
<h2>{{ title }}</h2> <h2>{{ title }}</h2>
@ -27,7 +29,7 @@
</div> </div>
{% endfor %} {% endfor %}
<button class="btn btn-primary" type="submit">Submit</button> <button class="btn btn-primary" type="submit">Submit</button>
<a href="/" class="btn btn-default">Cancel</a> <a href="{{ url_for('index') }}" class="btn btn-default">Cancel</a>
</form> </form>
</body> </body>

View File

@ -2,12 +2,13 @@
<html lang="en"> <html lang="en">
<head> <head>
<title>Protocol Builder Mock</title> <title>Protocol Builder Mock</title>
<base href="/">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet"> <link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://use.typekit.net/kwp6dli.css"> <link rel="stylesheet" href="https://use.typekit.net/kwp6dli.css">
{% assets 'app_scss' %} {% assets 'app_scss' %}
<link href="{{ ASSET_URL }}" rel="stylesheet" type="text/css"> <link href="{{ base_href + ASSET_URL }}" rel="stylesheet" type="text/css">
{% endassets %} {% endassets %}
<link rel="shortcut icon" href="{{ url_for('static', filename='favicon.ico') }}"> <link rel="shortcut icon" href="{{ base_href + url_for('static', filename='favicon.ico') }}">
</head> </head>
<body> <body>
<h2>Protocol Builder Mock</h2> <h2>Protocol Builder Mock</h2>

0
tests/__init__.py Normal file
View File

View File

@ -3,9 +3,11 @@ import os
os.environ["TESTING"] = "true" os.environ["TESTING"] = "true"
import unittest import unittest
from app import app, db import random
from forms import StudyForm import string
from models import Study, RequiredDocument from pb import app, db
from pb.forms import StudyForm
from pb.models import Study, RequiredDocument
class Sanity_Check_Test(unittest.TestCase): class Sanity_Check_Test(unittest.TestCase):
@ -32,7 +34,8 @@ class Sanity_Check_Test(unittest.TestCase):
def test_add_and_edit_study(self): def test_add_and_edit_study(self):
"""Add and edit a study""" """Add and edit a study"""
study = Study(TITLE="My Test Document", NETBADGEID="dhf8r") study_title = "My Test Document" + ''.join(random.choices(string.digits, k=8))
study = Study(TITLE=study_title, NETBADGEID="dhf8r")
form = StudyForm(formdata=None, obj=study) form = StudyForm(formdata=None, obj=study)
num_reqs = len(form.requirements.choices) num_reqs = len(form.requirements.choices)
self.assertGreater(num_reqs, 0) self.assertGreater(num_reqs, 0)
@ -40,9 +43,9 @@ class Sanity_Check_Test(unittest.TestCase):
for r in form.requirements: for r in form.requirements:
form.data['requirements'].append(r.data) form.data['requirements'].append(r.data)
r = self.app.post('/new_study', data=form.data, follow_redirects=True) r = self.app.post('/new_study', data=form.data, follow_redirects=False)
assert r.status_code == 200 assert r.status_code == 302
added_study = Study.query.filter(Study.TITLE == "My Test Document").first() added_study = Study.query.filter(Study.TITLE == study_title).first()
assert added_study assert added_study
num_studies_before = Study.query.count() num_studies_before = Study.query.count()
@ -50,14 +53,14 @@ class Sanity_Check_Test(unittest.TestCase):
self.assertEqual(num_reqs, num_docs_before) self.assertEqual(num_reqs, num_docs_before)
"""Edit an existing study""" """Edit an existing study"""
added_study.title = "New Title" added_study.title = "New Title" + ''.join(random.choices(string.digits, k=8))
form_2 = StudyForm(formdata=None, obj=added_study) form_2 = StudyForm(formdata=None, obj=added_study)
for r in form_2.requirements: for r in form_2.requirements:
form_2.data['requirements'].append(r.data) form_2.data['requirements'].append(r.data)
r_2 = self.app.post('/study/%i' % added_study.STUDYID, data=form_2.data, follow_redirects=True) r_2 = self.app.post('/study/%i' % added_study.STUDYID, data=form_2.data, follow_redirects=False)
assert r_2.status_code == 200 assert r_2.status_code == 302
num_studies_after = Study.query.count() num_studies_after = Study.query.count()
edited_study = Study.query.filter(Study.STUDYID == added_study.STUDYID).first() edited_study = Study.query.filter(Study.STUDYID == added_study.STUDYID).first()
assert edited_study assert edited_study

23
wsgi.py Normal file
View File

@ -0,0 +1,23 @@
from werkzeug.exceptions import NotFound
from werkzeug.middleware.dispatcher import DispatcherMiddleware
from werkzeug.middleware.proxy_fix import ProxyFix
from pb import app
if __name__ == "__main__":
def no_app(environ, start_response):
return NotFound()(environ, start_response)
# Remove trailing slash, but add leading slash
base_url = '/' + app.config['APPLICATION_ROOT'].strip('/')
routes = {'/': app.wsgi_app}
if base_url != '/':
routes[base_url] = app.wsgi_app
app.wsgi_app = DispatcherMiddleware(no_app, routes)
app.wsgi_app = ProxyFix(app.wsgi_app)
flask_port = app.config['FLASK_PORT']
app.run(host='0.0.0.0', port=flask_port)