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
__pycache__/
app.db
pb/static/.webassets-cache*
pb/static/*.css
static/.webassets-cache*
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
# 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
WORKDIR /app
COPY Pipfile Pipfile.lock /app/
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
[dev-packages]
pbr = "*"
[packages]
flask = "*"
@ -21,6 +22,9 @@ marshmallow-sqlalchemy = "*"
wtforms-alchemy = "*"
psycopg2-binary = "*"
pyscss = "*"
gunicorn = "*"
werkzeug = "*"
flask-cors = "*"
[requires]
python_version = "3.7"

281
Pipfile.lock generated
View File

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

View File

@ -1,6 +1,25 @@
#!/bin/bash
# run migrations
export FLASK_APP=./app.py
pipenv run flask db upgrade
pipenv run python ./run.py
export FLASK_APP=/app/pb/__init__.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 os
import re
import yaml
from datetime import date
import connexion
import yaml
from flask_cors import CORS
from flask import url_for, json, redirect, render_template, request, flash
from flask_assets import Environment, Bundle
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from flask_migrate import Migrate
from sqlalchemy import func
from wtforms.ext.appengine.db import model_form
PROTOCOLS = {}
@ -39,11 +40,8 @@ def get_study_details(studyid):
def get_form(id, requirement_code):
return
conn = connexion.App('Protocol Builder', specification_dir='./')
conn.add_api('api.yml')
app = conn.app
connexion_app = connexion.FlaskApp('Protocol Builder', specification_dir='pb')
app = connexion_app.app
app.config.from_object('config.default')
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
@ -55,17 +53,53 @@ else:
app.config.root_path = app.instance_path
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)
migrate = Migrate(app, db)
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.init_app(app)
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)
# Loads all the descriptions from the API so we can display them in the editor.
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)
study_detail_properties = api_config['components']['schemas']['StudyDetail']['properties']
for schema in api_config['components']['schemas']:
@ -82,14 +116,14 @@ def has_no_empty_params(rule):
return len(defaults) >= len(arguments)
@app.route("/site_map")
@app.route('/site_map')
def site_map():
links = []
for rule in app.url_map.iter_rules():
# Filter out rules we can't navigate to in a browser
# and rules that require parameters
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))
return json.dumps({"links": links})
@ -97,8 +131,8 @@ def site_map():
# **************************
# WEB FORMS
# **************************
from forms import StudyForm, StudyTable, InvestigatorForm, StudyDetailsForm
from models import Study, RequiredDocument, Investigator, StudySchema, RequiredDocumentSchema, InvestigatorSchema, \
from pb.forms import StudyForm, StudyTable, InvestigatorForm, StudyDetailsForm
from pb.models import Study, RequiredDocument, Investigator, StudySchema, RequiredDocumentSchema, InvestigatorSchema, \
StudyDetails, StudyDetailsSchema
@ -107,25 +141,33 @@ def index():
# display results
studies = db.session.query(Study).order_by(Study.DATE_MODIFIED.desc()).all()
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'])
def new_study():
form = StudyForm(request.form)
action = "/new_study"
action = BASE_HREF + "/new_study"
title = "New Study"
if request.method == 'POST':
study = Study()
study.study_details = StudyDetails()
_update_study(study, form)
flash('Study created successfully!')
return redirect('/')
return redirect_home()
return render_template('form.html', form=form,
action=action,
title=title,
description_map=description_map)
return render_template(
'form.html',
form=form,
action=action,
title=title,
description_map=description_map,
base_href=BASE_HREF
)
@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()
form = StudyForm(request.form, obj=study)
if request.method == 'GET':
action = "/study/" + study_id
action = BASE_HREF + "/study/" + study_id
title = "Edit Study #" + study_id
if 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':
_update_study(study, form)
flash('Study updated successfully!')
return redirect('/')
return render_template('form.html', form=form,
action=action,
title=title,
description_map={})
return redirect_home()
return render_template(
'form.html',
form=form,
action=action,
title=title,
description_map={},
base_href=BASE_HREF
)
@app.route('/investigator/<study_id>', methods=['GET', 'POST'])
def new_investigator(study_id):
form = InvestigatorForm(request.form)
action = "/investigator/" + study_id
action = BASE_HREF + "/investigator/" + study_id
title = "Add Investigator to Study " + study_id
if request.method == 'POST':
investigator = Investigator(STUDYID=study_id)
@ -161,19 +207,23 @@ def new_investigator(study_id):
db.session.add(investigator)
db.session.commit()
flash('Investigator created successfully!')
return redirect('/')
return redirect_home()
return render_template('form.html', form=form,
action=action,
title=title,
description_map={})
return render_template(
'form.html',
form=form,
action=action,
title=title,
description_map={},
base_href=BASE_HREF
)
@app.route('/del_investigator/<inv_id>', methods=['GET'])
def del_investigator(inv_id):
db.session.query(Investigator).filter(Investigator.id == inv_id).delete()
db.session.commit()
return redirect('/')
return redirect_home()
@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(Study).filter(Study.STUDYID == study_id).delete()
db.session.commit()
return redirect('/')
return redirect_home()
def _update_study(study, form):
@ -218,7 +268,7 @@ def study_details(study_id):
study_details = StudyDetails(STUDYID=study_id)
form = StudyDetailsForm(request.form, obj=study_details)
if request.method == 'GET':
action = "/study_details/" + study_id
action = BASE_HREF + "/study_details/" + 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."
if request.method == 'POST':
@ -226,12 +276,20 @@ def study_details(study_id):
db.session.add(study_details)
db.session.commit()
flash('Study updated successfully!')
return redirect('/')
return render_template('form.html', form=form,
action=action,
title=title,
details=details,
description_map=description_map)
return redirect_home()
return render_template(
'form.html',
form=form,
action=action,
title=title,
details=details,
description_map=description_map,
base_href=BASE_HREF
)
def redirect_home():
return redirect(url_for('index'))
if __name__ == '__main__':

View File

@ -10,8 +10,6 @@ info:
name: Apache 2.0
url: http://www.apache.org/licenses/LICENSE-2.0.html
# Added by API Auto Mocking Plugin
servers:
- url: http://localhost:5000/pb
# tags are used for organizing operations
tags:
- name: CR-Connect
@ -22,7 +20,7 @@ paths:
tags:
- CR-Connect
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"
parameters:
- in: query
@ -51,7 +49,7 @@ paths:
tags:
- CR-Connect
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
parameters:
- in: query
@ -74,7 +72,7 @@ paths:
tags:
- CR-Connect
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.
parameters:
- in: query
@ -103,7 +101,7 @@ paths:
get:
tags:
- CR-Connect
operationId: app.get_study_details
operationId: pb.get_study_details
summary: Details about a specific protocol.
responses:
200:

View File

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

View File

@ -1,5 +1,5 @@
from sqlalchemy import func
from app import db, ma
from pb import db, ma
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__":
flask_port = app.config['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>
<meta charset="UTF-8">
<title>Protocol Builder Mock Configuration</title>
<base href="/">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://use.typekit.net/kwp6dli.css">
{% assets 'app_scss' %}
<link href="{{ ASSET_URL }}" rel="stylesheet" type="text/css">
<link href="{{ base_href + ASSET_URL }}" rel="stylesheet" type="text/css">
{% endassets %}
<link rel="shortcut icon" href="{{ base_href + url_for('static', filename='favicon.ico') }}">
</head>
<body>
<h2>{{ title }}</h2>
@ -27,7 +29,7 @@
</div>
{% endfor %}
<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>
</body>

View File

@ -2,12 +2,13 @@
<html lang="en">
<head>
<title>Protocol Builder Mock</title>
<base href="/">
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel="stylesheet" href="https://use.typekit.net/kwp6dli.css">
{% assets 'app_scss' %}
<link href="{{ ASSET_URL }}" rel="stylesheet" type="text/css">
<link href="{{ base_href + ASSET_URL }}" rel="stylesheet" type="text/css">
{% 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>
<body>
<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"
import unittest
from app import app, db
from forms import StudyForm
from models import Study, RequiredDocument
import random
import string
from pb import app, db
from pb.forms import StudyForm
from pb.models import Study, RequiredDocument
class Sanity_Check_Test(unittest.TestCase):
@ -32,7 +34,8 @@ class Sanity_Check_Test(unittest.TestCase):
def test_add_and_edit_study(self):
"""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)
num_reqs = len(form.requirements.choices)
self.assertGreater(num_reqs, 0)
@ -40,9 +43,9 @@ class Sanity_Check_Test(unittest.TestCase):
for r in form.requirements:
form.data['requirements'].append(r.data)
r = self.app.post('/new_study', data=form.data, follow_redirects=True)
assert r.status_code == 200
added_study = Study.query.filter(Study.TITLE == "My Test Document").first()
r = self.app.post('/new_study', data=form.data, follow_redirects=False)
assert r.status_code == 302
added_study = Study.query.filter(Study.TITLE == study_title).first()
assert added_study
num_studies_before = Study.query.count()
@ -50,14 +53,14 @@ class Sanity_Check_Test(unittest.TestCase):
self.assertEqual(num_reqs, num_docs_before)
"""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)
for r in form_2.requirements:
form_2.data['requirements'].append(r.data)
r_2 = self.app.post('/study/%i' % added_study.STUDYID, data=form_2.data, follow_redirects=True)
assert r_2.status_code == 200
r_2 = self.app.post('/study/%i' % added_study.STUDYID, data=form_2.data, follow_redirects=False)
assert r_2.status_code == 302
num_studies_after = Study.query.count()
edited_study = Study.query.filter(Study.STUDYID == added_study.STUDYID).first()
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)