diff --git a/.travis.yml b/.travis.yml index bcf4edc0..56e36c69 100644 --- a/.travis.yml +++ b/.travis.yml @@ -13,9 +13,6 @@ matrix: - os: osx env: PYTHON=3.4.4 language: generic - - os: osx - language: generic - env: PYTHON=3.5.1 before_install: | if [ "$TRAVIS_OS_NAME" == "osx" ]; then diff --git a/web/project/__init__.py b/web/project/__init__.py index b843c590..d65b3197 100644 --- a/web/project/__init__.py +++ b/web/project/__init__.py @@ -22,8 +22,6 @@ MONGODB_PASSWORD = os.environ.get("MONGODB_PASSWORD") MONGODB_DB = os.environ.get("MONGODB_DB") - -#app = Flask(__name__) # Create the application instance connexionapp = connexion.App(__name__, specification_dir='./') @@ -31,6 +29,7 @@ connexionapp.add_api('swagger.yml') app = connexionapp.app app.secret_key = '\\\xfcS\x1e\x8f\xfb]6\x1e.\xa8\xb3\xe1x\xc8\x8e\xc1\xeb5^x\x81\xcc\xd5' + csrf = CSRFProtect(app) SESSION_TYPE = 'filesystem' app.config.from_object(__name__) @@ -45,12 +44,14 @@ os.environ['OAUTHLIB_INSECURE_TRANSPORT'] = '1' app.config['MAIL_ADDR'] = MAIL_ADDR app.config['MAIL_PWD'] = MAIL_PWD -app.config['MONGODB_HOST'] = 'paperstack.uchicago.edu' -app.config['MONGODB_PORT'] = 27017 -app.config['MONGODB_USERNAME'] = 'qresp_user_explorer' -app.config['MONGODB_PASSWORD'] = 'qresp_pwd' -app.config['MONGODB_DB'] = 'explorer' +app.config['MONGODB_HOST'] = MONGODB_HOST +app.config['MONGODB_PORT'] = MONGODB_PORT +app.config['MONGODB_USERNAME'] = MONGODB_USERNAME +app.config['MONGODB_PASSWORD'] = MONGODB_PASSWORD +app.config['MONGODB_DB'] = MONGODB_DB + Session(app) ext = Sitemap(app) + from project import routes diff --git a/web/project/api.py b/web/project/api.py index e40968ea..12b6a5bf 100644 --- a/web/project/api.py +++ b/web/project/api.py @@ -84,7 +84,7 @@ def paper(id): except Exception as e: msg = "Exception in paper api " + str(e) print("Exception in paper api ", e) - return e, 500 + return e, 400 return paperdetail def workflow(id): @@ -101,7 +101,7 @@ def workflow(id): except Exception as e: msg = "Exception in workflow api " + str(e) print("Exception in workflow api ", e) - return msg,500 + return msg,400 return workflowdetail def chart(id,cid): @@ -118,5 +118,5 @@ def chart(id,cid): except Exception as e: msg = "Exception in chart api " + str(e) print("Exception in chart api ", e) - return msg,500 + return msg,400 return chartworkflowdetail \ No newline at end of file diff --git a/web/project/routes.py b/web/project/routes.py index 4b949ce2..37f0314a 100644 --- a/web/project/routes.py +++ b/web/project/routes.py @@ -12,7 +12,7 @@ from flask_cors import CORS -from .paperdao import * +from .paperdao import PaperDAO,MongoDBConnection from .util import * from .views import * from project import app @@ -60,7 +60,7 @@ def index(): """ Fetches the homepage """ #TO BE DELETED - SetLocalHost(str(request.host_url).strip("/")) + #SetLocalHost(str(request.host_url).strip("/")) return render_template('index.html') @app.route('/downloads/') @@ -158,11 +158,12 @@ def uploadFile(): session["tags"] = [form for form in paperform.tags.data] session["collections"] = [form for form in paperform.collections.data] session["workflow"] = paperform.workflow.data - return jsonify(out="Processed text") - except: - return jsonify(error="Could not process json file") - + return jsonify(out="Processed text"), 200 + except Exception as e: + print(e) + return jsonify(error=str(e)), 400 +@csrf.exempt @app.route('/details', methods=['POST', 'GET']) def details(): """ This method helps in storing the details of the person using the curator. @@ -193,12 +194,12 @@ def server(): return redirect(url_for('project')) except Exception as e: flash("Could not connect to server. Plase try again!"+str(e)) - render_template('server.html', form=form) - return render_template('server.html', form=form) + render_template('server.html', form=form), 400 + return render_template('server.html', form=form), 200 @csrf.exempt -@app.route('/project', methods=['POST', 'GET']) +@app.route('/project', methods=['GET']) def project(): """ This method helps in populating the project form. """ @@ -237,11 +238,11 @@ def setproject(): session["ProjectName"] = absPath.rsplit("/",1)[1] else: session["ProjectName"] = absPath - return jsonify(folderAbsolutePath=absPath,isConfigFile=isConfig,services=services) + return jsonify(folderAbsolutePath=absPath,isConfigFile=isConfig,services=services), 200 except Exception as e: print(e) flash("Could not connect to server. Plase try again! " + str(e)) - return jsonify(folderAbsolutePath=absPath, isConfigFile=isConfig, services=services) + return jsonify(folderAbsolutePath=absPath, isConfigFile=isConfig, services=services), 401 @csrf.exempt @@ -252,19 +253,20 @@ def getTreeInfo(): pathDetails = request.get_json() listObjects = [] services = [] - ftpclient = session.get("sftpclient") - ftp = ftp_dict[ftpclient] - if 'folderAbsolutePath' in pathDetails: - content_path = pathDetails['folderAbsolutePath'] - else: - content_path = session.get("folderAbsolutePath") try: + ftpclient = session.get("sftpclient") + ftp = ftp_dict[ftpclient] + if 'folderAbsolutePath' in pathDetails: + content_path = pathDetails['folderAbsolutePath'] + else: + content_path = session.get("folderAbsolutePath") dtree = Dtree(ftp,content_path,session) listObjects = dtree.fetchForTree() services = dtree.fetchServices() except Exception as e: + jsonify({'errors':str(e)}), 400 print(e) - return jsonify({'listObjects': listObjects, 'services': services}) + return jsonify({'listObjects': listObjects, 'services': services}), 200 @app.route('/curate', methods=['POST', 'GET']) def curate(): @@ -318,8 +320,8 @@ def info(): session["collections"] = infoform.collections.data.split(",") session["notebookFile"] = infoform.notebookFile.data msg = "Info Saved" - return jsonify(data=msg) - return jsonify(data=infoform.errors) + return jsonify(data=msg), 200 + return jsonify(data=infoform.errors), 400 @csrf.exempt @app.route('/charts', methods=['POST', 'GET']) @@ -343,8 +345,8 @@ def charts(): chartList = [item for item in chartList if item['saveas'] not in chartform.saveas.data] chartList.append(chartform.data) session["charts"] = deepcopy(chartList) - return jsonify(chartList = chartList) - return jsonify(data=chartform.errors) + return jsonify(chartList = chartList), 200 + return jsonify(data=chartform.errors), 200 @csrf.exempt @app.route('/tools', methods=['POST', 'GET']) @@ -368,8 +370,8 @@ def tools(): toolList = [item for item in toolList if item['saveas'] not in toolform.saveas.data] toolList.append(toolform.data) session["tools"] = deepcopy(toolList) - return jsonify(toolList = toolList) - return jsonify(errors=toolform.errors) + return jsonify(toolList = toolList), 200 + return jsonify(errors=toolform.errors), 200 @csrf.exempt @app.route('/datasets', methods=['POST', 'GET']) @@ -393,8 +395,8 @@ def datasets(): datasetList = [item for item in datasetList if item['saveas'] not in datasetform.saveas.data] datasetList.append(datasetform.data) session["datasets"] = deepcopy(datasetList) - return jsonify(datasetList = datasetList) - return jsonify(data=datasetform.errors) + return jsonify(datasetList = datasetList), 200 + return jsonify(data=datasetform.errors), 200 @csrf.exempt @app.route('/scripts', methods=['POST', 'GET']) @@ -418,8 +420,8 @@ def scripts(): scriptList = [item for item in scriptList if item['saveas'] not in scriptform.saveas.data] scriptList.append(scriptform.data) session["scripts"] = deepcopy(scriptList) - return jsonify(scriptList = scriptList) - return jsonify(data=scriptform.errors) + return jsonify(scriptList = scriptList), 200 + return jsonify(data=scriptform.errors), 200 @csrf.exempt @app.route('/reference', methods=['POST', 'GET']) @@ -430,8 +432,8 @@ def reference(): if request.method == 'POST' and referenceform.validate(): session["reference"] = referenceform.data msg = "Reference Saved" - return jsonify(data=msg) - return jsonify(data=referenceform.errors) + return jsonify(data=msg), 200 + return jsonify(data=referenceform.errors), 200 @csrf.exempt @app.route('/fetchReferenceDOI', methods=['POST', 'GET']) @@ -444,8 +446,8 @@ def fetchReferenceDOI(): refdata = fetchDOI.fetchFromDOI() except Exception as e: print(e) - return jsonify(errors="Recheck your DOI") - return jsonify(fetchDOI=refdata.data) + return jsonify(errors="Recheck your DOI"), 400 + return jsonify(fetchDOI=refdata.data), 200 @csrf.exempt @app.route('/documentation', methods=['POST', 'GET']) @@ -456,8 +458,8 @@ def documentation(): if request.method == 'POST' and docform.validate(): session["documentation"] = docform.data msg = "Documentation Saved" - return jsonify(data=msg) - return jsonify(data=docform.errors) + return jsonify(data=msg), 200 + return jsonify(data=docform.errors), 200 @csrf.exempt @app.route('/workflow', methods=['POST', 'GET']) @@ -559,10 +561,9 @@ def workflow(): session["workflow"] = workflowinfo.data except Exception as e: print(e) - return jsonify({'workflow': workflow.__dict__}) + return jsonify({'workflow': workflow.__dict__}), 200 -# Fetches list of qresp servers for the explorer @csrf.exempt @app.route('/publish', methods=['POST', 'GET']) def publish(): @@ -720,7 +721,7 @@ def download(): with open("papers/"+projectName+"/"+"data.json", "w") as outfile: json.dump(paperdata, outfile, default=lambda o: o.__dict__, separators=(',', ':'), sort_keys=True, indent=3) session["paper"] = paperdata - return jsonify(paper=paperdata) + return jsonify(paper=paperdata), 200 @csrf.exempt @@ -737,15 +738,13 @@ def acknowledgement(): def qrespexplorer(): """ Fetches the explorer homepage """ - dao = PaperDAO() - dao.getDB() form = QrespServerForm() serverslist = Servers() form.serverList = [qrespserver['qresp_server_url'] for qrespserver in serverslist.getServersList()] if request.method == 'POST': - if request.form.get('serversList'): - session["selectedserver"] = request.form.get('serversList') + if request.form.getlist('serversList'): + session["selectedserver"] = request.form.getlist('serversList') else: session['selectedserver'] = form.serverList return redirect(url_for('search')) @@ -760,8 +759,8 @@ def verifypasscode(): confirmpasscode = app.config['passkey'] if request.method == 'POST' and form.validate(): if confirmpasscode == form.passcode.data: - return jsonify(msg="success") - return jsonify(msg="failed") + return jsonify(msg="success"),200 + return jsonify(msg="failed"),401 # Fetches list of qresp admin @csrf.exempt @@ -807,14 +806,23 @@ def search(): :return: template: search.html """ allpaperslist = [] + collectionlist = [] + authorslist = [] + publicationlist = [] + allPapersSize = 0 try: selectedserver = session.get("selectedserver") fetchdata = FetchDataFromAPI(selectedserver) - allpaperslist = fetchdata.fetchOutput('/api/search') - collectionlist = fetchdata.fetchOutput('/api/collections') - authorslist = fetchdata.fetchOutput('/api/authors') - publicationlist = fetchdata.fetchOutput('/api/publications') - allPapersSize = len(allpaperslist) + fetchallpaperslist = fetchdata.fetchOutput('/api/search') + fetchcollectionlist = fetchdata.fetchOutput('/api/collections') + fetchauthorslist = fetchdata.fetchOutput('/api/authors') + fetchpublicationlist = fetchdata.fetchOutput('/api/publications') + if fetchallpaperslist and len(fetchallpaperslist)>0: + allpaperslist = fetchallpaperslist + collectionlist = fetchcollectionlist + authorslist = fetchauthorslist + publicationlist = fetchpublicationlist + allPapersSize = len(allpaperslist) except Exception as e: print(e) flash('Error in search. ' + str(e)) @@ -830,6 +838,7 @@ def searchWord(): """ filtering paper content :return: object: allpaperlist """ + allpaperslist = [] try: dao = PaperDAO() searchWord = request.args.get('searchWord', type=str) @@ -844,10 +853,11 @@ def searchWord(): url = '/api/search'+"?searchWord="+searchWord+"&paperTitle="+paperTitle+"&doi="+doi+"&tags="+tags+"&collectionList="+",".join(collectionList) + \ "&authorsList="+",".join(authorsList)+"&publicationList="+",".join(publicationList) allpaperslist = fetchdata.fetchOutput(url) - return jsonify(allpaperslist=allpaperslist) + return jsonify(allpaperslist=allpaperslist), 200 except Exception as e: print(e) flash('Error in search. ' + str(e)) + return jsonify(allpaperslist=allpaperslist), 400 # Fetches details of chart based on user click @@ -857,6 +867,8 @@ def paperdetails(paperid): """ fetching papers details content :return: template: paperdetails.html """ + paperdetail = [] + workflowdetail = [] try: selectedserver = session.get("selectedserver") fetchdata = FetchDataFromAPI(selectedserver) @@ -868,8 +880,6 @@ def paperdetails(paperid): except Exception as e: print(e) flash('Error in paperdetails. ' + str(e)) - paperdetail = [] - workflowdetail = [] return render_template('paperdetails.html', paperdetail=paperdetail, workflowdetail=workflowdetail) @@ -881,6 +891,7 @@ def chartworkflow(): """ Fetching chart workflow content :return: object: chartworkflow """ + chartworkflowdetail = [] try: dao = PaperDAO() paperid = request.args.get('paperid', 0, type=str) @@ -891,10 +902,11 @@ def chartworkflow(): fetchdata = FetchDataFromAPI(selectedserver) url = '/api/paper/' + paperid + '/chart/' + chartid chartworkflowdetail = fetchdata.fetchOutput(url) - return jsonify(chartworkflowdetail=chartworkflowdetail) + return jsonify(chartworkflowdetail=chartworkflowdetail), 200 except Exception as e: print(e) - flash('Error in paperdetails. ' + str(e)) + flash('Error in chartdetails. ' + str(e)) + return jsonify(chartworkflowdetail=chartworkflowdetail), 400 @csrf.exempt @app.route('/getDescriptor', methods=['POST','GET']) @@ -947,7 +959,7 @@ def insertDOI(): except Exception as e: print(e) content = {'Failed': 'Could Not Insert'} - return jsonify(content), 500 + return jsonify(content), 400 content = {'Success': 'Inserted'} return jsonify(content), 200 diff --git a/web/project/tests/test_paperDAO.py b/web/project/tests/test_paperDAO.py index e56e24ee..8ef9ce81 100644 --- a/web/project/tests/test_paperDAO.py +++ b/web/project/tests/test_paperDAO.py @@ -1,5 +1,5 @@ import unittest -from project.paperdao import * +from project.paperdao import PaperDAO,MongoDBConnection,Paper import os import json def warn(*args, **kwargs): @@ -85,7 +85,6 @@ def test_getFilteredPaperObjectsForTitle(self): """ dao = PaperDAO() allSearchObjects = dao.getAllFilteredSearchObjects(paperTitle='photo') - print(len(allSearchObjects)) self.assertTrue(list(allSearchObjects)) def test_getFilteredPaperObjectsForTags(self): @@ -106,7 +105,7 @@ def test_getFilteredPaperObjectsForCollections(self): def test_insertDOI(self): """ - Tests for isertion of DOI + Tests for insertion of DOI """ dao = PaperDAO() allSearchObjects = dao.getAllFilteredSearchObjects() @@ -114,14 +113,38 @@ def test_insertDOI(self): self.assertEquals(1, paper) - # def test_getPaperDetails(self): - # self.fail() - # - # def test_getWorkflowDetails(self): - # self.fail() - # - # def test_getWorkflowForChartDetails(self): - # self.fail() + def test_getPaperDetails(self): + """ + Tests Paper details given paper id + """ + dao = PaperDAO() + allSearchObjects = dao.getAllFilteredSearchObjects() + paperDetails = dao.getPaperDetails(allSearchObjects[0]['_Search__id']) + self.assertEquals(allSearchObjects[0]['_Search__id'], paperDetails['_PaperDetails__id']) + + + def test_getWorkflowDetails(self): + """ + Tests workflow details given paper id + """ + dao = PaperDAO() + allSearchObjects = dao.getAllFilteredSearchObjects() + workflowdetails = dao.getWorkflowDetails(allSearchObjects[0]['_Search__id']) + self.assertEquals(workflowdetails['paperTitle'],allSearchObjects[0]['_Search__title']) + + + + def test_getWorkflowForChartDetails(self): + """ + Tests workflow details given chart id and paper id + :return: + """ + dao = PaperDAO() + allSearchObjects = dao.getAllFilteredSearchObjects() + paperDetails = dao.getPaperDetails(allSearchObjects[0]['_Search__id']) + chartid = paperDetails['_PaperDetails__charts'][0].id + workflowchartdetails = dao.getWorkflowForChartDetails(paperDetails['_PaperDetails__id'],chartid) + self.assertEquals(workflowchartdetails['paperTitle'],allSearchObjects[0]['_Search__title']) if __name__ == "__main__": unittest.main() \ No newline at end of file diff --git a/web/project/tests/test_routes.py b/web/project/tests/test_routes.py index c0e08f65..7f86f909 100644 --- a/web/project/tests/test_routes.py +++ b/web/project/tests/test_routes.py @@ -1,7 +1,10 @@ import os import unittest - +import json from project import app +from project.views import * +from flask import Flask, request, jsonify + class RouteTests(unittest.TestCase): ############################ @@ -10,19 +13,84 @@ class RouteTests(unittest.TestCase): # executed prior to each test def setUp(self): - self.app = app.test_client() + + self.app = self.create_app() + self.client = app.test_client() + self.ctx = self.app.test_request_context() + self.ctx.push() + + self.pagenames = ['/','qrespcurator','startfromscratch','details','server','project','curate','workflow','publish', + 'acknowledgement','qrespexplorer','admin','search', + 'searchWord?searchWord=&paperTitle=&tags=&doi=&collectionList=[]&authorsList=[]&publicationList=[]', + 'paperdetails/5941869f1bd40fd44db0024a','chartworkflow?paperid=5941869f1bd40fd44db0024a&chartid=c0'] + + postpagedicts = {'details':{'firstName':'John','middleName':'L','lastName':'Doe'}, + 'server':{'serverName':'testServer','username':'testuser','password':'testpassword','isDUOAuth':'No'}, + 'charts':{'caption':'cc','number':'1','files':'a,b','imageFile':'a.png','properties':'1,2','saveas':'c0'}, + 'tools':{'packageName':'West','version':'v0','facilityName':'Xray','measurement':'xray','saveas':'t0'}, + 'datasets':{'files':'a,b','readme':'readdata','saveas':'d0'}, + 'scripts':{'files':'a,b','readme':'readdata','saveas':'s0'}, + 'reference':{'DOI':'doi/10.123','title':'Test','page':'1','publishedAbstract':'Abs','volume':'1','year':2019}, + 'fetchReferenceDOI':{}, + 'documentation':{'readme':'read'}, + 'publish':{}, + 'download':{}} + + nameform = NameForm() + nameform.firstName.data = 'John' + nameform.middleName.data = 'L.' + nameform.lastName.data = 'Doe' + detailsform = DetailsForm(**postpagedicts['details']) + serverform = ServerForm(**postpagedicts['server']) + chartform = ChartForm(**postpagedicts['charts']) + chartform.extraFields.entries = [] + toolform = ToolForm(**postpagedicts['tools']) + toolform.extraFields.entries = [] + datasetform = DatasetForm(**postpagedicts['datasets']) + datasetform.extraFields.entries = [] + scriptform = ScriptForm(**postpagedicts['scripts']) + scriptform.extraFields.entries = [] + referenceform = ReferenceForm(**postpagedicts['reference']) + referenceform.authors.entries = [] + referenceform.journal.abbrevName.data = 'JAbbr' + referenceform.journal.fullName.data = 'JFull' + documentationform = DocumentationForm(**postpagedicts['documentation']) + self.postPageForms = {'details':detailsform,'server':serverform,'charts':chartform,'tools':toolform, + 'datasets':datasetform,'scripts':scriptform,'fetchReferenceDOI':{'doi':'10.1021/nl903868w'}, + 'documentation':documentationform,'workflow':[['to','do'],['to','do']], + 'publish':{},'download':{}} # executed after each test + def create_app(self): + app = Flask(__name__) + app.secret_key = "secret" + return app + def tearDown(self): - pass + self.ctx.pop() + + def request(self,*args, **kwargs): + return self.app.test_request_context(*args, **kwargs) + + def test_getMethods(self): + for pagename in self.pagenames: + response = self.client.get(pagename, follow_redirects=True) + self.assertEquals(response.status_code,200) + + + def test_postMethods(self): + for pagename in self.postPageForms.keys(): + form = self.postPageForms[pagename] + if 'fetchReferenceDOI' in pagename or 'workflow' in pagename or 'publish' in pagename or 'download' in pagename: + response = self.client.post(pagename, data=json.dumps(form), content_type='application/json') + else: + response = self.client.post(pagename,data=form.data) + if 'details' in pagename: + self.assertEqual(response.status_code, 302) + else: + self.assertEqual(response.status_code, 200) - def test_main_page(self): - response = self.app.get('/', follow_redirects=True) - self.assertEqual(response.status_code, 200) - def test_main_page(self): - response = self.app.get('/', follow_redirects=True) - self.assertEqual(response.status_code, 200) if __name__ == "__main__": unittest.main() diff --git a/web/project/util.py b/web/project/util.py index d235b15a..cbb2f4bf 100644 --- a/web/project/util.py +++ b/web/project/util.py @@ -409,7 +409,8 @@ class FetchDataFromAPI(): """ Fetches data for search,paper details and chart details for explorer """ def __init__(self,servernames): - serverList = [] + if isinstance(servernames,str): + servernames = [servernames] if servernames and len(servernames)>0: serverList = servernames else: @@ -430,7 +431,6 @@ def fetchOutput(self,apiname): self.__servernames = list(set(self.__servernames)) for snames in self.__servernames: url = snames + apiname - print("url>",url) headers = {'Application': 'qresp', 'Accept': 'application/json', 'Content-Type': 'application/json'} response = requests.get(url,headers=headers, verify=False) if response.status_code == 200: diff --git a/web/run.py b/web/run.py index a5b9fff3..32ac3525 100644 --- a/web/run.py +++ b/web/run.py @@ -7,5 +7,5 @@ #app.run() if __name__ == "__main__": - connexionapp.run(port=80,debug=True) + connexionapp.run(host='0.0.0.0',port=80,debug=True)