[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