[LNT] r305851 - Sample API for fast Data export
Chris Matthews via llvm-commits
llvm-commits at lists.llvm.org
Tue Jun 20 14:06:48 PDT 2017
Author: cmatthews
Date: Tue Jun 20 16:06:48 2017
New Revision: 305851
URL: http://llvm.org/viewvc/llvm-project?rev=305851&view=rev
Log:
Sample API for fast Data export
Plus fixups to Run API and order API.
Modified:
lnt/trunk/lnt/server/ui/api.py
lnt/trunk/tests/server/ui/test_api.py
Modified: lnt/trunk/lnt/server/ui/api.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/lnt/server/ui/api.py?rev=305851&r1=305850&r2=305851&view=diff
==============================================================================
--- lnt/trunk/lnt/server/ui/api.py (original)
+++ lnt/trunk/lnt/server/ui/api.py Tue Jun 20 16:06:48 2017
@@ -1,13 +1,14 @@
import json
+import sqlalchemy
from flask import current_app, g
from flask import request
from flask_restful import Resource, reqparse, fields, marshal_with, abort
from sqlalchemy.orm import joinedload
from sqlalchemy.orm.exc import NoResultFound
-from lnt.testing import PASS
from lnt.server.ui.util import convert_revision
+from lnt.testing import PASS
parser = reqparse.RequestParser()
parser.add_argument('db', type=str)
@@ -56,6 +57,9 @@ def with_ts(obj):
new_obj['db'] = g.db_name
new_obj['ts'] = g.testsuite_name
+ for key in ['machine', 'order']:
+ if new_obj.get(key):
+ new_obj[key] = with_ts(new_obj[key])
return new_obj
@@ -86,6 +90,7 @@ order_fields = {
'name': fields.String,
'next_order_id': fields.Integer,
'previous_order_id': fields.Integer,
+ 'parts': fields.List(fields.Integer),
}
@@ -94,28 +99,30 @@ class ParameterItem(fields.Raw):
return dict(json.loads(value['parameters_data']))
-run_fields = {
- 'id': fields.Integer,
- 'start_time': fields.DateTime(dt_format=DATE_FORMAT),
- 'end_time': fields.DateTime(dt_format=DATE_FORMAT),
- 'machine_id': fields.Integer,
- 'machine': fields.Url("machine"),
- 'order_id': fields.Integer,
- 'order_url': fields.Url("order"),
- 'parameters': ParameterItem
-}
+machine_run_fields = {'id': fields.Integer,
+ 'start_time': fields.DateTime(dt_format=DATE_FORMAT),
+ 'end_time': fields.DateTime(dt_format=DATE_FORMAT),
+ 'machine_id': fields.Integer,
+ 'machine': fields.Url("machine"),
+ 'order_id': fields.Integer,
+ 'order_name': fields.String,
+ 'order_url': fields.Url("order"),
+ 'parameters': ParameterItem}
-run_fields['order'] = fields.Nested(order_fields)
+machine_run_fields['order'] = fields.Nested(order_fields)
machine_fields = {
'id': fields.Integer,
'name': fields.String,
'os': fields.String,
'hardware': fields.String,
- 'runs': fields.List(fields.Nested(run_fields)),
+ 'runs': fields.List(fields.Nested(machine_run_fields)),
'parameters': ParameterItem
}
+run_fields = {'run': fields.Nested(machine_run_fields),
+ 'samples': fields.List(fields.Raw)}
+
class Runs(Resource):
method_decorators = [in_db]
@@ -123,19 +130,38 @@ class Runs(Resource):
@marshal_with(run_fields)
def get(self, run_id):
ts = request.get_testsuite()
+ full_run = dict()
+
try:
run = ts.query(ts.Run) \
.join(ts.Machine) \
.join(ts.Order) \
.filter(ts.Run.id == run_id) \
.options(joinedload('order')) \
- .options(joinedload('machine')) \
.one()
- except NoResultFound:
- abort(404, message="Invalid run.")
+ except sqlalchemy.orm.exc.NoResultFound:
+ abort(404, msg="Did not find run " + str(run_id))
+
+ full_run['run'] = with_ts(run)
+ full_run['run']['order']['parts'] = convert_revision(run.order.name)
+ full_run['run']['order']['name'] = run.order.name
+ full_run['run']['parts'] = run.order.name
+
+ to_get = [ts.Sample.id, ts.Sample.run_id, ts.Test.name,
+ ts.Order.fields[0].column]
+ for f in ts.sample_fields:
+ to_get.append(f.column)
- run = with_ts(run)
- return run
+ q = ts.query(*to_get) \
+ .join(ts.Test) \
+ .join(ts.Run) \
+ .join(ts.Order) \
+ .filter(ts.Sample.run_id.is_(run_id))
+
+ ret = [sample._asdict() for sample in q.all()]
+ print ret
+ full_run['samples'] = ret
+ return with_ts(full_run)
class Machine(Resource):
@@ -161,7 +187,6 @@ class Machine(Resource):
.all()
machine['runs'] = with_ts(machine_runs)
- machine['a'] = [m.parameters for m in machine_runs]
return machine
@@ -176,7 +201,44 @@ class Order(Resource):
order = ts.query(ts.Order).filter(ts.Order.id == order_id).one()
except NoResultFound:
abort(404, message="Invalid order.")
- return order
+ order_output = with_ts(order)
+ order_output['parts'] = convert_revision(order.name)
+ order_output['name'] = order.name
+ return order_output
+
+
+class SampleData(Resource):
+ """List all the machines and give summary information."""
+ method_decorators = [in_db]
+
+ def get(self):
+ """Get the data for a particular line in a graph."""
+ ts = request.get_testsuite()
+ args = request.args.to_dict(flat=False)
+ # Maybe we don't need to do this?
+ run_ids = [int(r) for r in args.get('runid', [])]
+
+ if not run_ids:
+ abort(400,
+ msg='No runids found in args. Should be "samples?runid=1&runid=2" etc.')
+
+ to_get = [ts.Sample.id,
+ ts.Sample.run_id,
+ ts.Test.name,
+ ts.Order.fields[0].column]
+
+ for f in ts.sample_fields:
+ to_get.append(f.column)
+
+ q = ts.query(*to_get) \
+ .join(ts.Test) \
+ .join(ts.Run) \
+ .join(ts.Order) \
+ .filter(ts.Sample.run_id.in_(run_ids))
+
+ ret = [sample._asdict() for sample in q.all()]
+
+ return ret
class Graph(Resource):
@@ -216,7 +278,8 @@ class Graph(Resource):
if limit:
q = q.limit(limit)
- samples = [[convert_revision(rev), val, {'label': rev, 'date': str(time), 'runID': str(rid)}] for val, rev, time, rid in
+ samples = [[convert_revision(rev), val, {'label': rev, 'date': str(time), 'runID': str(rid)}] for
+ val, rev, time, rid in
q.all()[::-1]]
samples.sort(key=lambda x: x[0])
return samples
@@ -269,6 +332,7 @@ def load_api_resources(api):
api.add_resource(Machines, ts_path("machines"))
api.add_resource(Machine, ts_path("machine/<int:machine_id>"))
api.add_resource(Runs, ts_path("run/<int:run_id>"))
+ api.add_resource(SampleData, ts_path("samples"))
api.add_resource(Order, ts_path("order/<int:order_id>"))
graph_url = "graph/<int:machine_id>/<int:test_id>/<int:field_index>"
api.add_resource(Graph, ts_path(graph_url))
Modified: lnt/trunk/tests/server/ui/test_api.py
URL: http://llvm.org/viewvc/llvm-project/lnt/trunk/tests/server/ui/test_api.py?rev=305851&r1=305850&r2=305851&view=diff
==============================================================================
--- lnt/trunk/tests/server/ui/test_api.py (original)
+++ lnt/trunk/tests/server/ui/test_api.py Tue Jun 20 16:06:48 2017
@@ -33,7 +33,8 @@ machines_expected_response = [{u'hardwar
order_expected_response = {u'id': 1,
u'name': "154331",
u'next_order_id': 0,
- u'previous_order_id': 2}
+ u'previous_order_id': 2,
+ u'parts': [154331]}
graph_data = [[[152292], 1.0,
{u'date': u'2012-05-01 16:28:23',
@@ -68,7 +69,8 @@ class JSONAPITester(unittest.TestCase):
j = check_json(client, 'api/db_default/v4/nts/machines')
self.assertEquals(j, machines_expected_response)
j = check_json(client, 'api/db_default/v4/nts/machine/1')
- self.assertEqual(j.keys(), [u'runs', u'name', u'parameters', u'hardware', u'os', u'id'])
+ self.assertEqual(j.keys(), [u'runs', u'name', u'parameters',
+ u'hardware', u'os', u'id'])
expected = {"hardware": "x86_64", "os": "Darwin 11.3.0", "id": 1}
self.assertDictContainsSubset(expected, j)
@@ -79,25 +81,25 @@ class JSONAPITester(unittest.TestCase):
expected = {"machine": "/api/db_default/v4/nts/machine/1",
"order_url": "/api/db_default/v4/nts/order/1",
"end_time": "2012-04-11T16:28:58",
-
"order_id": 1,
"start_time": "2012-04-11T16:28:23",
"machine_id": 1,
"id": 1,
- "order": {
- "previous_order_id": 2,
- "next_order_id": 0,
- "id": 1,
- "name": "154331"
- }
+ "order": {u'previous_order_id': 2, u'next_order_id': 0,
+ u'parts': [154331], u'name': u'154331', u'id': 1}
+
}
- self.assertDictContainsSubset(expected, j)
+ self.assertDictContainsSubset(expected, j['run'])
+ self.assertEqual(len(j['samples']), 2)
+ # This should not be a run.
+ check_json(client, 'api/db_default/v4/nts/run/100', expected_code=404)
def test_order_api(self):
""" Check /order/n returns the expected order information."""
client = self.client
j = check_json(client, 'api/db_default/v4/nts/order/1')
self.assertEquals(j, order_expected_response)
+ check_json(client, 'api/db_default/v4/nts/order/100', expected_code=404)
def test_graph_api(self):
"""Check that /graph/x/y/z returns what we expect."""
@@ -110,6 +112,43 @@ class JSONAPITester(unittest.TestCase):
j2 = check_json(client, 'api/db_default/v4/nts/graph/2/4/3?limit=1')
self.assertEqual(graph_data2, j2)
+ def test_samples_api(self):
+ """Samples API."""
+ client = self.client
+ # Run IDs must be passed, so 400 if they are not.
+ check_json(client, 'api/db_default/v4/nts/samples',
+ expected_code=400)
+
+ # Simple single run.
+ j = check_json(client, 'api/db_default/v4/nts/samples?runid=1')
+ expected = [
+ {u'compile_time': 0.007, u'llvm_project_revision': u'154331',
+ u'hash': None,
+ u'name': u'SingleSource/UnitTests/2006-12-01-float_varg',
+ u'run_id': 1, u'execution_time': 0.0003,
+ u'mem_bytes': None, u'compile_status': None,
+ u'execution_status': None, u'score': None,
+ u'hash_status': None, u'code_size': None, u'id': 1},
+ {u'compile_time': 0.0072, u'llvm_project_revision': u'154331',
+ u'hash': None,
+ u'name': u'SingleSource/UnitTests/2006-12-04-DynAllocAndRestore',
+ u'run_id': 1,
+ u'execution_time': 0.0003, u'mem_bytes': None,
+ u'compile_status': None, u'execution_status': None,
+ u'score': None, u'hash_status': None, u'code_size': None,
+ u'id': 2}]
+
+ self.assertEqual(j, expected)
+
+ # Check that other args are ignored.
+ extra_param = check_json(client,
+ 'api/db_default/v4/nts/samples?runid=1&foo=bar')
+ self.assertEqual(j, extra_param)
+ # There is only one run in the DB.
+ two_runs = check_json(client,
+ 'api/db_default/v4/nts/samples?runid=1&runid=2')
+ self.assertEqual(j, two_runs)
+
if __name__ == '__main__':
unittest.main(argv=[sys.argv[0], ])
More information about the llvm-commits
mailing list