diff --git a/Pipfile b/Pipfile index 535b7e4..59200d4 100644 --- a/Pipfile +++ b/Pipfile @@ -9,6 +9,10 @@ verify_ssl = true flask = "*" connexion = {extras = ["swagger-ui"],version = "*"} gevent = "*" +flask-wtf = "*" +sqlalchemy = "*" +flask-sqlalchemy = "*" +flask-table = "*" [requires] python_version = "3.7" diff --git a/Pipfile.lock b/Pipfile.lock index 678df94..5ce8cea 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,11 +1,11 @@ { "_meta": { "hash": { - "sha256": "53f3c053174686a450c6276f298ee9678a273d57984d0126070d08fd9b4f97e6" + "sha256": "72fff5c0cf2514d4307a4fc4eb60d57cc5bd0bf19603dc77587835bf01e43bd7" }, "pipfile-spec": 6, "requires": { - "python_version": "3.6" + "python_version": "3.7" }, "sources": [ { @@ -23,6 +23,13 @@ ], "version": "==19.3.0" }, + "babel": { + "hashes": [ + "sha256:1aac2ae2d0d8ea368fa90906567f5c08463d98ade155c0c4bfedd6a0f7160e38", + "sha256:d670ea0b10f8b723672d3a6abeb87b565b244da220d76b4dba1b66269ec152d4" + ], + "version": "==2.8.0" + }, "certifi": { "hashes": [ "sha256:017c25db2a153ce562900032d5bc68e9f191e44e9a0f762f373977de9df1fbb3", @@ -56,11 +63,11 @@ "swagger-ui" ], "hashes": [ - "sha256:3f6c2cb8a3f24af281ff561eebaaa926700b24f4c430f4d945c8ace0384c220c", - "sha256:56854c65334281b303120a1097df09a79ffa44bdaecfc4400e7194821e17566d" + "sha256:bf32bfae6af337cfa4a8489c21516adbe5c50e3f8dc0b7ed2394ce8dde218018", + "sha256:c568e579f84be808e387dcb8570bb00a536891be1318718a0dad3ba90f034191" ], "index": "pypi", - "version": "==2.5.1" + "version": "==2.6.0" }, "flask": { "hashes": [ @@ -70,6 +77,36 @@ "index": "pypi", "version": "==1.1.1" }, + "flask-babel": { + "hashes": [ + "sha256:247f4ec34cf605d03781f480bccb1a5acb719df1d1a2a743c091ab3db5d5fde2", + "sha256:d6a70468f9a8919d59fba2a291a003da3a05ff884275dddbd965f3b98b09ab3e" + ], + "version": "==1.0.0" + }, + "flask-sqlalchemy": { + "hashes": [ + "sha256:0078d8663330dc05a74bc72b3b6ddc441b9a744e2f56fe60af1a5bfc81334327", + "sha256:6974785d913666587949f7c2946f7001e4fa2cb2d19f4e69ead02e4b8f50b33d" + ], + "index": "pypi", + "version": "==2.4.1" + }, + "flask-table": { + "hashes": [ + "sha256:320e5756cd7252e902e03b6cd1087f2c7ebc31364341b482f41f30074d10a770" + ], + "index": "pypi", + "version": "==0.5.0" + }, + "flask-wtf": { + "hashes": [ + "sha256:57b3faf6fe5d6168bda0c36b0df1d05770f8e205e18332d0376ddb954d17aef2", + "sha256:d417e3a0008b5ba583da1763e4db0f55a1269d9dd91dcc3eb3c026d3c5dbd720" + ], + "index": "pypi", + "version": "==0.14.3" + }, "gevent": { "hashes": [ "sha256:0774babec518a24d9a7231d4e689931f31b332c4517a771e532002614e270a64", @@ -105,7 +142,9 @@ "sha256:0d48200bc50cbf498716712129eef819b1729339e34c3ae71656964dac907c28", "sha256:23d12eacffa9d0f290c0fe0c4e81ba6d5f3a5b7ac3c30a5eaf0126bf4deda5c8", "sha256:37c9ba82bd82eb6a23c2e5acc03055c0e45697253b2393c9a50cef76a3985304", + "sha256:51155342eb4d6058a0ffcd98a798fe6ba21195517da97e15fca3db12ab201e6e", "sha256:51503524dd6f152ab4ad1fbd168fc6c30b5795e8c70be4410a64940b3abb55c0", + "sha256:7457d685158522df483196b16ec648b28f8e847861adb01a55d41134e7734122", "sha256:8041e2de00e745c0e05a502d6e6db310db7faa7c979b3a5877123548a4c0b214", "sha256:81fcd96a275209ef117e9ec91f75c731fa18dcfd9ffaa1c0adbdaa3616a86043", "sha256:853da4f9563d982e4121fed8c92eea1a4594a2299037b3034c3c898cb8e933d6", @@ -119,7 +158,8 @@ "sha256:bcb530089ff24f6458a81ac3fa699e8c00194208a724b644ecc68422e1111939", "sha256:beeabe25c3b704f7d56b573f7d2ff88fc99f0138e43480cecdfcaa3b87fe4f87", "sha256:d634a7ea1fc3380ff96f9e44d8d22f38418c1c381d5fac680b272d7d90883720", - "sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656" + "sha256:d97b0661e1aead761f0ded3b769044bb00ed5d33e1ec865e891a8b128bf7c656", + "sha256:e538b8dae561080b542b0f5af64d47ef859f22517f7eca617bb314e0e03fd7ef" ], "markers": "platform_python_implementation == 'CPython'", "version": "==0.4.15" @@ -133,11 +173,11 @@ }, "importlib-metadata": { "hashes": [ - "sha256:073a852570f92da5f744a3472af1b61e28e9f78ccf0c9117658dc32b15de7b45", - "sha256:d95141fbfa7ef2ec65cfd945e2af7e5a6ddbd7c8d9a25e66ff3be8e3daf9f60f" + "sha256:06f5b3a99029c7134207dd882428a66992a9de2bef7c2b699b5641f9886c3302", + "sha256:b97607a1a18a5100839aec1dc26a1ea17ee0d93b20b0f008d80a5a050afb200b" ], "markers": "python_version < '3.8'", - "version": "==1.3.0" + "version": "==1.5.0" }, "inflection": { "hashes": [ @@ -154,10 +194,10 @@ }, "jinja2": { "hashes": [ - "sha256:74320bb91f31270f9551d46522e33af46a80c3d619f4a4bf42b3164d30b5911f", - "sha256:9fe95f19286cfefaa917656583d020be14e7859c6b0252588391e47db34527de" + "sha256:93187ffbc7808079673ef52771baa950426fd664d3aad1d0fa3e95644360e250", + "sha256:b0eaf100007721b5c16c1fc1eecb87409464edc10469ddc9a22a27a99123be49" ], - "version": "==2.10.3" + "version": "==2.11.1" }, "jsonschema": { "hashes": [ @@ -172,13 +212,16 @@ "sha256:09027a7803a62ca78792ad89403b1b7a73a01c8cb65909cd876f7fcebd79b161", "sha256:09c4b7f37d6c648cb13f9230d847adf22f8171b1ccc4d5682398e77f40309235", "sha256:1027c282dad077d0bae18be6794e6b6b8c91d58ed8a8d89a89d59693b9131db5", + "sha256:13d3144e1e340870b25e7b10b98d779608c02016d5184cfb9927a9f10c689f42", "sha256:24982cc2533820871eba85ba648cd53d8623687ff11cbb805be4ff7b4c971aff", "sha256:29872e92839765e546828bb7754a68c418d927cd064fd4708fab9fe9c8bb116b", "sha256:43a55c2930bbc139570ac2452adf3d70cdbb3cfe5912c71cdce1c2c6bbd9c5d1", "sha256:46c99d2de99945ec5cb54f23c8cd5689f6d7177305ebff350a58ce5f8de1669e", "sha256:500d4957e52ddc3351cabf489e79c91c17f6e0899158447047588650b5e69183", "sha256:535f6fc4d397c1563d08b88e485c3496cf5784e927af890fb3c3aac7f933ec66", + "sha256:596510de112c685489095da617b5bcbbac7dd6384aeebeda4df6025d0256a81b", "sha256:62fe6c95e3ec8a7fad637b7f3d372c15ec1caa01ab47926cfdf7a75b40e0eac1", + "sha256:6788b695d50a51edb699cb55e35487e430fa21f1ed838122d722e0ff0ac5ba15", "sha256:6dd73240d2af64df90aa7c4e7481e23825ea70af4b4922f8ede5b9e35f78a3b1", "sha256:717ba8fe3ae9cc0006d7c451f0bb265ee07739daf76355d06366154ee68d221e", "sha256:79855e1c5b8da654cf486b830bd42c06e8780cea587384cf6545b7d9ac013a0b", @@ -195,17 +238,12 @@ "sha256:ba59edeaa2fc6114428f1637ffff42da1e311e29382d81b339c1817d37ec93c6", "sha256:c8716a48d94b06bb3b2524c2b77e055fb313aeb4ea620c8dd03a105574ba704f", "sha256:cd5df75523866410809ca100dc9681e301e3c27567cf498077e8551b6d20e42f", - "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7" + "sha256:cdb132fc825c38e1aeec2c8aa9338310d29d337bebbd7baa06889d09a60a1fa2", + "sha256:e249096428b3ae81b08327a63a485ad0878de3fb939049038579ac0ef61e17e7", + "sha256:e8313f01ba26fbbe36c7be1966a7b7424942f670f38e666995b88d012765b9be" ], "version": "==1.1.1" }, - "more-itertools": { - "hashes": [ - "sha256:b84b238cce0d9adad5ed87e745778d20a3f8487d0f0cb8b8a586816c7496458d", - "sha256:c833ef592a0324bcc6a60e48440da07645063c453880c9477ceb22490aec1564" - ], - "version": "==8.0.2" - }, "openapi-spec-validator": { "hashes": [ "sha256:0caacd9829e9e3051e830165367bf58d436d9487b29a09220fa7edb9f47ff81b", @@ -216,9 +254,16 @@ }, "pyrsistent": { "hashes": [ - "sha256:f3b280d030afb652f79d67c5586157c5c1355c9a58dfc7940566e28d28f3df1b" + "sha256:cdc7b5e3ed77bed61270a47d35434a30617b9becdf2478af76ad2c6ade307280" ], - "version": "==0.15.6" + "version": "==0.15.7" + }, + "pytz": { + "hashes": [ + "sha256:1c557d7d0e871de1f5ccd5833f60fb2550652da6be2693c1e02300743d21500d", + "sha256:b02c06db6cf09c12dd25137e563b31700d3b80fcc4ad23abb7a315f2789819be" + ], + "version": "==2019.3" }, "pyyaml": { "hashes": [ @@ -245,10 +290,17 @@ }, "six": { "hashes": [ - "sha256:1f1b7d42e254082a9db6279deae68afb421ceba6158efa6131de7b3003ee93fd", - "sha256:30f610279e8b2578cab6db20741130331735c781b56053c59c4076da27f06b66" + "sha256:236bdbdce46e6e6a3d61a337c0f8b763ca1e8717c03b369e87a7ec7ce1319c0a", + "sha256:8f3cd2e254d8f793e7f3d6d9df77b92252b52637291d0f0da013c76ea2724b6c" ], - "version": "==1.13.0" + "version": "==1.14.0" + }, + "sqlalchemy": { + "hashes": [ + "sha256:64a7b71846db6423807e96820993fa12a03b89127d278290ca25c0b11ed7b4fb" + ], + "index": "pypi", + "version": "==1.3.13" }, "swagger-ui-bundle": { "hashes": [ @@ -260,24 +312,31 @@ }, "urllib3": { "hashes": [ - "sha256:a8a318824cc77d1fd4b2bec2ded92646630d7fe8619497b142c84a9e6f5a7293", - "sha256:f3c5fd51747d450d4dcf6f923c81f78f811aab8205fda64b0aba34a4e48b0745" + "sha256:2f3db8b19923a873b3e5256dc9c2dedfa883e33d87c690d9c7913e1f40673cdc", + "sha256:87716c2d2a7121198ebcb7ce7cccf6ce5e9ba539041cfbaeecfb641dc0bf6acc" ], - "version": "==1.25.7" + "version": "==1.25.8" }, "werkzeug": { "hashes": [ - "sha256:7280924747b5733b246fe23972186c6b348f9ae29724135a6dfc1e53cea433e7", - "sha256:e5f4a1f98b52b18a93da705a7458e55afb26f32bff83ff5d19189f92462d65c4" + "sha256:169ba8a33788476292d04186ab33b01d6add475033dfc07215e6d219cc077096", + "sha256:6dc65cf9091cf750012f56f2cad759fa9e879f511b5ff8685e456b4e3bf90d16" ], - "version": "==0.16.0" + "version": "==1.0.0" + }, + "wtforms": { + "hashes": [ + "sha256:0cdbac3e7f6878086c334aa25dc5a33869a3954e9d1e015130d65a69309b3b61", + "sha256:e3ee092c827582c50877cdbd49e9ce6d2c5c1f6561f849b3b068c1b8029626f1" + ], + "version": "==2.2.1" }, "zipp": { "hashes": [ - "sha256:3718b1cbcd963c7d4c5511a8240812904164b7f381b647143a89d3b98f9bcd8e", - "sha256:f06903e9f1f43b12d371004b4ac7b06ab39a44adc747266928ae6debfa7b3335" + "sha256:5c56e330306215cd3553342cfafc73dda2c60792384117893f3a83f8a1209f50", + "sha256:d65287feb793213ffe11c0f31b81602be31448f38aeb8ffc2eb286c4f6f6657e" ], - "version": "==0.6.0" + "version": "==2.2.0" } }, "develop": {} diff --git a/api.yml b/api.yml index ee221dc..a5785d3 100644 --- a/api.yml +++ b/api.yml @@ -121,10 +121,6 @@ components: type: number example: 11770 description: The unique id of the study in Protocol Builder. Sometimes referenced as the "HSR Submission number" if you are looking at Protocol Builder - PID: - type: number - example: - description: (Protocol_id) is similar to studyid but its for IRB online HSRNUMBER: type: number example: 15378 @@ -211,7 +207,7 @@ components: type: number enum: [0,1,null] example: 1 - description: Unknown + description: Was the investigational new drug developed at UVa? IS_IDE: type: number enum: [0,1,null] @@ -221,7 +217,7 @@ components: type: number enum: [0,1,null] example: 1 - description: Unknown + description: Was the investigational device developed at UVa? IDE_NUMBERS: type: array items: @@ -231,306 +227,268 @@ components: type: number enum: [0,1,null] example: 1 - description: Unknown + description: Does this include chart reviews which do not requre the consent of the Study group? IS_RADIATION: type: number enum: [0,1,null] example: true - description: Unknown + description: Does the Study include radiation treatment? GCRC_NUMBER: type: string - description: Unknown + description: The General Clinical Research Center number, if funded by GCRC IS_GCRC: type: number enum: [0,1,null] example: true - description: Unkown + description: Will any part of this study take place in or be funded by the General Clinical Research Center? IS_PRC_DSMP: type: number enum: [0,1,null] example: true - description: Unkown + description: Does this protocol involve the testing of a new device or improvement of a method of detection or diagnosis for cancer? IS_PRC: type: number enum: [0,1,null] example: true - description: Unkown + description: Does the Study require Cancer Center Protocol Review Committee oversight? PRC_NUMBER: type: string - description: Unkown + description: Cancer Center Protocol Review Committee number IS_IBC: type: number enum: [0,1,null] example: true - description: Unkown + description: Will this Study need the approval of the Institutional Biosafety Committee? IBC_NUMBER: type: number enum: [0,1,null] example: true - description: Unkown - IS_SPONSOR_TRACKING: - type: number - enum: [0,1,null] - example: true - description: Unkown - SPONSOR_TRACKING: - type: number - enum: [0,1,null] - example: true - description: Unkown + description: Site identifier that the location has been approved to handle biological agents by the Institutional Biosafety Committee. SPONSORS_PROTOCOL_REVISION_DATE: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the study be part of a UVA faculty member's-sponsored Investigational Device application? IS_SPONSOR_MONITORING: type: number enum: [0,1,null] example: true - description: Unkown - IS_DSMB: - type: number - enum: [0,1,null] - example: true - description: Unkown - IS_COMPLETE_NON_IRB_REGULATORY: - type: number - enum: [0,1,null] - example: true - description: Unkown + description: Will the Sponsor monitor the Study? IS_AUX: type: number enum: [0,1,null] example: true - description: Unkown + description: Is Auxilliary Info needed? IS_SPONSOR: type: number enum: [0,1,null] example: true - description: Unkown + description: Is there an outside sponsor involved? IS_GRANT: type: number enum: [0,1,null] example: true - description: Unkown + description: Is grant money funding all or part of the Study? IS_COMMITTEE_CONFLICT: type: number enum: [0,1,null] example: true - description: Unkown + description: Does someone on the IRB Review Committee have a conflict of interest? DSMB: type: number enum: [0,1,null] example: true - description: Unkown + description: Will this study be monitored by a Data and Safety Monitoring Board? DSMB_FREQUENCY: - type: number - enum: [0,1,null] - example: true - description: Unkown + type: String + example: A lot + description: UNDOCUMENTED. IS_DB: type: number enum: [0,1,null] example: true - description: Unkown + description: Is the ONLY intent of this protocol to establish a research database (repository)? IS_UVA_DB: type: number enum: [0,1,null] example: true - description: Unkown + description: Will any of the data be kept at UVA outside of the official medical record or will specimens be kept at UVA outside of clinical labs? IS_CENTRAL_REG_DB: type: number enum: [0,1,null] example: true - description: Unkown + description: Will any data from UVA subjects be sent to a central registry or database outside of UVA? IS_CONSENT_WAIVER: type: number enum: [0,1,null] example: true - description: Unkown + description: Will there be a consent waiver? IS_HGT: type: number enum: [0,1,null] example: true - description: Unkown + description: Are you storing specimens, sequences analysis of DNA/RNA for an unspecified future use. IS_GENE_TRANSFER: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study involve gene transfer? IS_TISSUE_BANKING: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study involve tissue banking? IS_SURROGATE_CONSENT: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study require the consent of a surrogate? IS_ADULT_PARTICIPANT: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study group include adults? IS_MINOR_PARTICIPANT: type: number enum: [0,1,null] example: true - description: Unkown + description: Are any of the subjects between the ages of 15 and < 18? IS_MINOR: type: number enum: [0,1,null] example: true - description: Unkown + description: Does the study enroll subjects under the age of 18? IS_BIOMEDICAL: type: number enum: [0,1,null] example: true - description: Unkown + description: Will this study involve biomedical research? IS_QUALITATIVE: type: number enum: [0,1,null] example: true - description: Unkown + description: Will this study involve only qualitative research? IS_PI_SCHOOL: type: number enum: [0,1,null] example: true - description: Unkown + description: Will this study be submitted through the PI's current primary school and department appointment? IS_PRISONERS_POP: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study group include prisoners? IS_PREGNANT_POP: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study group include pregnant women? IS_FETUS_POP: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study be done on fetuses? IS_MENTAL_IMPAIRMENT_POP: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study group include anyone who is mentally impaired? IS_ELDERLY_POP: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study group include the elderly? IS_OTHER_VULNERABLE_POP: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study group include other vulnerable populations? OTHER_VULNERABLE_DESC: - type: number - enum: [0,1,null] - example: true - description: Unkown + type: string + example: homeless people + description: Other vulnerable population description IS_MULTI_SITE: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study be conducted at multiple sites? IS_UVA_LOCATION: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study be done at UVa? NON_UVA_LOCATION: type: string - description: Unkown + description: Will the Study be done at non-UVa sites? MULTI_SITE_LOCATIONS: type: string - description: Unkown + description: List if other sites involved. IS_OUTSIDE_CONTRACT: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study be funded by an outside contract? IS_UVA_PI_MULTI: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the PI be from UVa for multi-site study? IS_NOT_PRC_WAIVER: type: number enum: [0,1,null] example: true - description: Unkown - IS_INSIDE_CONTRACT: - type: number - enum: [0,1,null] - example: true - description: Unkown + description: Have you received notification from the Cancer Center Protocol Review Committee (PRC) that this protocol does not need approval from the PRC? IS_CANCER_PATIENT: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study involve cancer patients? UPLOAD_COMPLETE: type: number enum: [0,1,null] example: true - description: Unkown + description: bit indicating that the study was moved to IRB online IS_FUNDING_SOURCE: type: number enum: [0,1,null] example: true - description: Unkown - IS_CODED_RESEARCH: - type: number - enum: [0,1,null] - example: true - description: Unkown - IS_OUTSIDE_SPONSOR: - type: number - enum: [0,1,null] - example: true - description: Unkown + description: Is there an outside supply source other than the sponsor/funding source supporting this study? IS_PI_INITIATED: type: number enum: [0,1,null] example: true - description: Unkown + description: Was the Study initiated by the Principal Investigator? IS_ENGAGED_RESEARCH: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study group be contacted by UVa employees or agents or obtain personnally identifiable information about them? IS_APPROVED_DEVICE: type: number enum: [0,1,null] example: true - description: Unkown + description: Has the device in the Study been approved by the FDA? IS_FINANCIAL_CONFLICT: type: number enum: [0,1,null] example: true - description: Unkown + description: Has the Conflict of Interest Committee provided a COI Management Plan? IS_NOT_CONSENT_WAIVER: type: number enum: [0,1,null] example: true - description: Unkown + description: Do you plan on getting VERBAL CONSENT for ONLY a PART of this study (waiver of documentation of consent) and getting WRITTEN CONSENT for another part of the study? IS_FOR_CANCER_CENTER: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study involve the UVa Cancer Center? IS_REVIEW_BY_CENTRAL_IRB: type: number enum: [0,1,null] example: true - description: Unkown + description: Will the Study require review by the Central IRB? IRBREVIEWERADMIN: - type: number - enum: [0,1,null] - example: true - description: Unkown + type: string + example: "dhf8r" + description: Netbadge id of IRB staff assinged to do the review diff --git a/app.py b/app.py index 0f13b39..82ebf80 100644 --- a/app.py +++ b/app.py @@ -1,5 +1,7 @@ import connexion -from flask import url_for, json +from flask import url_for, json, redirect, render_template, request, flash + +from flask_sqlalchemy import SQLAlchemy PROTOCOLS = {} @@ -25,11 +27,13 @@ def get_form(id, requirement_code): return -app = connexion.App('Protocol Builder', specification_dir='./') -app.add_api('api.yml') +conn = connexion.App('Protocol Builder', specification_dir='./') +conn.add_api('api.yml') -application = app.app +app = conn.app +app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db' +db = SQLAlchemy(app) def has_no_empty_params(rule): defaults = rule.defaults if rule.defaults is not None else () @@ -37,10 +41,10 @@ def has_no_empty_params(rule): return len(defaults) >= len(arguments) -@application.route("/") +@app.route("/site_map") def site_map(): links = [] - for rule in application.url_map.iter_rules(): + 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): @@ -49,6 +53,55 @@ def site_map(): return json.dumps({"links": links}) +app.config['SECRET_KEY'] = 'a really really really really long secret key' + +from forms import Study, StudyForm, StudySearchForm, StudyTable, RequiredDocument + +@app.route('/', methods=['GET', 'POST']) +def index(): + search = StudySearchForm(request.form) + if request.method == 'POST': + return search_results(search) + return render_template('index.html', form=search) + + +@app.route('/results') +def search_results(search): + results = [] + search_string = search.data['search'] + if search.data['search'] == '': + qry = db.session.query(Study) + results = qry.all() + if not results: + flash('No results found!') + return redirect('/') + else: + # display results + studies = db.session.query("Study").all() + table = StudyTable(studies) + return render_template('results.html', table=table) + + +@app.route('/new_study', methods=['GET', 'POST']) +def new_study(): + form = StudyForm(request.form) + if request.method == 'POST': + # save the study + study = Study() + study.id = form.id + study.title = form.title +# for r in form.requirements: +# requirement = RequiredDocument(id = r.id, +# study.requirements = form.requirements + db.session.add(study) + db.session.commit() + flash('Album created successfully!') + return redirect('/') + + form = StudyForm(request.form) + return render_template('study_form.html', form=form) + + if __name__ == '__main__': # run our standalone gevent server app.run(port=4200) \ No newline at end of file diff --git a/forms.py b/forms.py new file mode 100644 index 0000000..a1a9a8e --- /dev/null +++ b/forms.py @@ -0,0 +1,70 @@ +from flask_table import Table, Col +from flask_wtf import FlaskForm +from wtforms import SelectMultipleField, SubmitField, StringField, IntegerField + +from app import db + + +class Study(db.Model): + id = db.Column(db.Integer, primary_key=True) + title = db.Column(db.String(80), nullable=False) + requirements = db.relationship("RequiredDocument", backref="study", lazy='dynamic') + + +class RequiredDocument(db.Model): + id = db.Column(db.Integer, primary_key=True) + name = db.Column(db.String(80), nullable=False) + active = db.Column(db.Boolean, default=False) + study_id = db.Column(db.Integer, db.ForeignKey('study.id')) + + @staticmethod + def all(): + docs = [RequiredDocument(id=1, name="Investigators Brochure"), + RequiredDocument(id=6, name="Cancer Center's PRC Approval Form"), + RequiredDocument(id=8, name="SOM CTO IND/IDE Review Letter"), + RequiredDocument(id=9, name="HIRE Approval"), + RequiredDocument(id=10, name="Cancer Center's PRC Approval Waiver"), + RequiredDocument(id=12, name="Certificate of Confidentiality Application"), + RequiredDocument(id=14, name="Institutional Biosafety Committee Approval"), + RequiredDocument(id=18, name="SOM CTO Approval Letter - UVA PI Multisite Trial"), + RequiredDocument(id=20, + name="IRB Approval or Letter of Approval from Administration: Study Conducted at non- UVA Facilities "), + RequiredDocument(id=21, name="New Medical Device Form"), + RequiredDocument(id=22, name="SOM CTO Review regarding need for IDE"), + RequiredDocument(id=23, name="SOM CTO Review regarding need for IND"), + RequiredDocument(id=24, name="InfoSec Approval"), + RequiredDocument(id=25, name="Scientific Pre-review Documentation"), + RequiredDocument(id=26, name="IBC Number"), + RequiredDocument(id=32, name="IDS - Investigational Drug Service Approval"), + RequiredDocument(id=36, name="RDRC Approval "), + RequiredDocument(id=40, name="SBS/IRB Approval-FERPA"), + RequiredDocument(id=41, name="HIRE Standard Radiation Language"), + RequiredDocument(id=42, name="COI Management Plan "), + RequiredDocument(id=43, name="SOM CTO Approval Letter-Non UVA, Non Industry PI MultiSite Study"), + RequiredDocument(id=44, name="GRIME Approval"), + RequiredDocument(id=45, name="GMEC Approval"), + RequiredDocument(id=46, name="IRB Reliance Agreement Request Form- IRB-HSR is IRB of Record"), + RequiredDocument(id=47, name="Non UVA IRB Approval - Initial and Last Continuation"), + RequiredDocument(id=48, name="MR Physicist Approval- Use of Gadolinium"), + RequiredDocument(id=49, name="SOM CTO Approval- Non- UVA Academia PI of IDE"), + RequiredDocument(id=51, name="IDS Waiver"), + RequiredDocument(id=52, name="Package Inserts"), + RequiredDocument(id=53, name="IRB Reliance Agreement Request Form- IRB-HSR Not IRB of Record"), + RequiredDocument(id=54, name="ESCRO Approval"), + RequiredDocument(id=57, name="Laser Safety Officer Approval")] + return docs + + +class StudySearchForm(FlaskForm): + search = StringField('') + + +class StudyForm(FlaskForm): + id = IntegerField('Study Id') + title = StringField('Title') + requirements = SelectMultipleField("Requirements", choices=[(rd.id, rd.name) for rd in RequiredDocument.all()]) + +class StudyTable(Table): + id = Col('Id') + title = Col('Artist') + requirements = Col('Title') diff --git a/templates/index.html b/templates/index.html new file mode 100644 index 0000000..05b242d --- /dev/null +++ b/templates/index.html @@ -0,0 +1,24 @@ + + + + Protocol Builder Mock + + +

Protocol Builder Mock

+ +

+ New Study + +{% with messages = get_flashed_messages() %} + {% if messages %} +

+ {% endif %} +{% endwith %} + +{{ table }} + +
\ No newline at end of file diff --git a/templates/study_form.html b/templates/study_form.html new file mode 100644 index 0000000..3da688f --- /dev/null +++ b/templates/study_form.html @@ -0,0 +1,31 @@ + + + + + New Study 0 Protocol Builder Mock Configuration + + + + +

New Study

+
+ + {{ form.csrf_token() }} + + {% for field in form if field.name != "csrf_token" %} +

{{ field.label() }}

+

{{ field }} + {% for error in field.errors %} + {{ error }} + {% endfor %} +

+ {% endfor %} + +
+ + +