Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions qiita_db/processing_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -850,6 +850,25 @@ def outputs(self):
name: qdb.artifact.Artifact(aid)
for aid, name in qdb.sql_connection.TRN.execute_fetchindex()}

@property
def processing_job_worflow(self):
"""The processing job worflow

Returns
-------
ProcessingWorkflow
The processing job workflow the job
"""
with qdb.sql_connection.TRN:
sql = """SELECT processing_job_workflow_id
FROM qiita.processing_job_workflow_root
WHERE processing_job_id = %s"""
qdb.sql_connection.TRN.add(sql, [self.id])
r = qdb.sql_connection.TRN.execute_fetchindex()

return (qdb.processing_job.ProcessingWorkflow(r[0][0]) if r
else None)


class ProcessingWorkflow(qdb.base.QiitaObject):
"""Models a workflow defined by the user
Expand Down
12 changes: 12 additions & 0 deletions qiita_db/test/test_processing_job.py
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,18 @@ def test_outputs(self):
[afp for _, afp, _ in
qdb.artifact.Artifact(exp_artifact_count).filepaths])

def test_processing_job_worflow(self):
# testing None
job = qdb.processing_job.ProcessingJob(
"063e553b-327c-4818-ab4a-adfe58e49860")
self.assertIsNone(job.processing_job_worflow)

# testing actual workflow
job = qdb.processing_job.ProcessingJob(
"b72369f9-a886-4193-8d3d-f7b504168e75")
self.assertEqual(job.processing_job_worflow,
qdb.processing_job.ProcessingWorkflow(1))


@qiita_test_checker()
class ProcessingWorkflowTests(TestCase):
Expand Down
11 changes: 11 additions & 0 deletions qiita_db/test/test_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -450,5 +450,16 @@ def test_user_artifacts(self):
qdb.artifact.Artifact(7)]}
self.assertEqual(obs, exp)

def test_jobs(self):
PJ = qdb.processing_job.ProcessingJob
# generates expected jobs
jobs = qdb.user.User('shared@foo.bar').jobs()
self.assertEqual(jobs, [
PJ('d19f76ee-274e-4c1b-b3a2-a12d73507c55'),
PJ('b72369f9-a886-4193-8d3d-f7b504168e75')])

# no jobs
self.assertEqual(qdb.user.User('admin@foo.bar').jobs(), [])

if __name__ == "__main__":
main()
22 changes: 22 additions & 0 deletions qiita_db/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class User(qdb.base.QiitaObject):
private_analyses
shared_analyses
unread_messages
jobs

Methods
-------
Expand Down Expand Up @@ -660,6 +661,27 @@ def delete_messages(self, messages):
qdb.sql_connection.TRN.add(sql)
qdb.sql_connection.TRN.execute()

def jobs(self):
"""Return jobs created by the user

Parameters
----------

Returns
-------
list of ProcessingJob

"""
with qdb.sql_connection.TRN:
sql_info = [self._id]
sql = """SELECT processing_job_id
FROM qiita.processing_job
WHERE email = %s
ORDER BY heartbeat DESC"""
qdb.sql_connection.TRN.add(sql, sql_info)
return [qdb.processing_job.ProcessingJob(p[0])
for p in qdb.sql_connection.TRN.execute_fetchindex()]


def validate_email(email):
"""Validates an email
Expand Down
3 changes: 2 additions & 1 deletion qiita_pet/handlers/api_proxy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
list_options_handler_get_req, workflow_handler_post_req,
workflow_handler_patch_req, workflow_run_post_req,
job_ajax_get_req)
from .user import (user_jobs_get_req)

__version__ = "0.2.0-dev"

Expand All @@ -64,4 +65,4 @@
'workflow_handler_patch_req', 'workflow_run_post_req',
'job_ajax_get_req', 'artifact_patch_request',
'sample_template_patch_request',
'get_sample_template_processing_status']
'get_sample_template_processing_status', 'user_jobs_get_req']
71 changes: 71 additions & 0 deletions qiita_pet/handlers/api_proxy/tests/test_user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2014--, The Qiita Development Team.
#
# Distributed under the terms of the BSD 3-clause License.
#
# The full license is in the file LICENSE, distributed with this software.
# -----------------------------------------------------------------------------
from unittest import TestCase, main
from os.path import exists, isdir
from os import remove
from shutil import rmtree

from qiita_core.util import qiita_test_checker
import qiita_db as qdb
from qiita_pet.handlers.api_proxy.user import (user_jobs_get_req)


@qiita_test_checker()
class TestSUserAPI(TestCase):
def setUp(self):
self._clean_up_files = []

def tearDown(self):
for fp in self._clean_up_files:
if exists(fp):
if isdir(fp):
rmtree(fp)
else:
remove(fp)

def test_user_jobs_get_req(self):
obs = user_jobs_get_req(qdb.user.User('shared@foo.bar'))
exp = {
'status': 'success',
'message': '',
'jobs': [
{'id': 'd19f76ee-274e-4c1b-b3a2-a12d73507c55',
'status': 'error',
'heartbeat': '2015-11-22 21:30:00',
'params': {
'reference': 1,
'similarity': 0.97,
'sortmerna_e_value': 1,
'sortmerna_max_pos': 10000,
'input_data': 2,
'threads': 1,
'sortmerna_coverage': 0.97},
'name': 'Pick closed-reference OTUs',
'processing_job_workflow_id': ''},
{'id': 'b72369f9-a886-4193-8d3d-f7b504168e75',
'status': 'success',
'heartbeat': '2015-11-22 21:15:00',
'params': {
'max_barcode_errors': 1.5,
'sequence_max_n': 0,
'max_bad_run_length': 3,
'phred_offset': u'auto',
'rev_comp': False,
'phred_quality_threshold': 3,
'input_data': 1,
'rev_comp_barcode': False,
'rev_comp_mapping_barcodes': True,
'min_per_read_length_fraction': 0.75,
'barcode_type': u'golay_12'},
'name': 'Split libraries FASTQ',
'processing_job_workflow_id': 1}]}
self.assertEqual(obs, exp)


if __name__ == '__main__':
main()
53 changes: 53 additions & 0 deletions qiita_pet/handlers/api_proxy/user.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
# -----------------------------------------------------------------------------
# Copyright (c) 2014--, The Qiita Development Team.
#
# Distributed under the terms of the BSD 3-clause License.
#
# The full license is in the file LICENSE, distributed with this software.
# -----------------------------------------------------------------------------
from __future__ import division

from qiita_core.util import execute_as_transaction


@execute_as_transaction
def user_jobs_get_req(user):
"""Gets the json of jobs

Parameters
----------
prep_id : int
PrepTemplate id to get info for
user_id : str
User requesting the sample template info

Returns
-------
dict of objects
{'status': status,
'message': message,
'template': {sample: {column: value, ...}, ...}
"""

response = []
cmds = {}
for j in user.jobs():
cmd = j.command
if cmd not in cmds:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is there a valid situation in which the same command will appear multiple times?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, multiple and the idea of cache; basically, the command is the definition of the command (split libs, demux, etc) and the job is the command with the specific params.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see how this caches anything though? cmds is reset prior to the forloop so the only compute that is avoided is the assignment of a value to a key in a dict?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

...on closer inspection, it doesn't look like cmds is needed at all

cmds[cmd] = cmd
name = cmds[cmd].name
hb = j.heartbeat
hb = "" if hb is None else hb.strftime("%Y-%m-%d %H:%M:%S")
pjw = j.processing_job_worflow
wid = '' if pjw is None else pjw.id
response.append({
'id': j.id,
'name': name,
'params': j.parameters.values,
'status': j.status,
'heartbeat': hb,
'processing_job_workflow_id': wid})

return {'status': 'success',
'message': '',
'jobs': response}
10 changes: 10 additions & 0 deletions qiita_pet/handlers/user_handlers.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
from tornado.web import authenticated, HTTPError
from tornado.gen import coroutine
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are flake8 errors because this import is not being used.

from future.utils import viewitems
from wtforms import Form, StringField, validators

from qiita_pet.handlers.base_handlers import BaseHandler
from qiita_pet.handlers.api_proxy import user_jobs_get_req
from qiita_db.user import User
from qiita_db.logger import LogEntry
from qiita_db.exceptions import QiitaDBUnknownIDError, QiitaDBError
Expand Down Expand Up @@ -185,3 +187,11 @@ def post(self):

self.render("user_messages.html",
messages=self.current_user.messages())


class UserJobs(BaseHandler):
@authenticated
@coroutine
def get(self):
response = user_jobs_get_req(self.current_user)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this executes as a transaction and has the potential to block as it is performing IO. Should this method be a tornado coroutine instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure, changing

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this needs take the result of a yield?

http://www.tornadoweb.org/en/stable/gen.html

self.write(response)
6 changes: 6 additions & 0 deletions qiita_pet/test/test_user_handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,11 @@ def test_post_profile(self):
response = self.post('/profile/', post_args)
self.assertEqual(response.code, 200)


class TestUserJobsHandler(TestHandlerBase):
def test_get(self):
response = self.get('/user/jobs/')
self.assertEqual(response.code, 200)

if __name__ == "__main__":
main()
3 changes: 2 additions & 1 deletion qiita_pet/webserver.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
AuthCreateHandler, AuthLoginHandler, AuthLogoutHandler, AuthVerifyHandler)
from qiita_pet.handlers.user_handlers import (
ChangeForgotPasswordHandler, ForgotPasswordHandler, UserProfileHandler,
UserMessagesHander)
UserMessagesHander, UserJobs)
from qiita_pet.handlers.analysis_handlers import (
SelectCommandsHandler, AnalysisWaitHandler, AnalysisResultsHandler,
ShowAnalysesHandler, ResultsHandler, SelectedSamplesHandler,
Expand Down Expand Up @@ -88,6 +88,7 @@ def __init__(self):
(r"/auth/reset/(.*)", ChangeForgotPasswordHandler),
(r"/profile/", UserProfileHandler),
(r"/user/messages/", UserMessagesHander),
(r"/user/jobs/", UserJobs),
(r"/results/(.*)", ResultsHandler,
{"path": RES_PATH}),
(r"/static/(.*)", tornado.web.StaticFileHandler,
Expand Down