Milimetric has submitted this change and it was merged.

Change subject: Controller and front end changes for async upload
......................................................................


Controller and front end changes for async upload

First round of tests are all passing, going onto UI changes
Change-Id: I9cdf44864043e60342e1f4113d2fa3310f71b08e
Card: analytics 818.2
Card: analytics 818.4
Card: analytics 818.5
---
M tests/test_controllers/test_cohorts.py
M tests/test_metrics/test_survivors.py
M tests/test_models/test_cohort.py
M tests/test_models/test_run_report.py
M wikimetrics/config/db_config.yaml
M wikimetrics/controllers/cohorts.py
M wikimetrics/models/cohort.py
M wikimetrics/models/report_nodes/run_report.py
A wikimetrics/models/report_nodes/validate_report.py
9 files changed, 252 insertions(+), 182 deletions(-)



diff --git a/tests/test_controllers/test_cohorts.py 
b/tests/test_controllers/test_cohorts.py
index 2ab599d..43f6418 100644
--- a/tests/test_controllers/test_cohorts.py
+++ b/tests/test_controllers/test_cohorts.py
@@ -1,6 +1,6 @@
 # -*- coding:utf-8 -*-
 import json
-from nose.tools import assert_equal, raises, assert_true
+from nose.tools import assert_equal, raises, assert_true, assert_false
 from tests.fixtures import WebTest
 from wikimetrics.controllers.cohorts import (
     parse_username,
@@ -30,8 +30,13 @@
         assert_equal(parsed['cohorts'][0]['name'], self.cohort.name)
     
     def test_list_includes_only_validated(self):
-        cohorts = [Cohort(name='c1', validated=False), Cohort(name='c2', 
validated=True)]
+        # There is already one cohort, add one more validated and one not 
validated
+        cohorts = [
+            Cohort(name='c1', enabled=True, validated=False),
+            Cohort(name='c2', enabled=True, validated=True)
+        ]
         self.session.add_all(cohorts)
+        self.session.commit()
         owners = [
             CohortUser(
                 cohort_id=c.id,
@@ -41,12 +46,13 @@
             for c in cohorts
         ]
         self.session.add_all(owners)
+        self.session.commit()
         
         response = self.app.get('/cohorts/list', follow_redirects=True)
         parsed = json.loads(response.data)
         assert_equal(len(parsed['cohorts']), 2)
-        assert_equal(parsed['cohorts'][0].validated, True)
-        assert_equal(parsed['cohorts'][1].validated, True)
+        assert_false('c1' in [c['name'] for c in parsed['cohorts']])
+        assert_true('c2' in [c['name'] for c in parsed['cohorts']])
     
     def test_detail(self):
         response = self.app.get('/cohorts/detail/{0}'.format(self.cohort.id))
@@ -79,14 +85,6 @@
         valid_user = normalize_user(parsed_user, 'enwiki')
         assert_equal(valid_user[0], self.editors[0].user_id)
         assert_equal(valid_user[1], 'Editor test-specific-0')
-    
-    def test_get_cohort_by_name_not_found(self):
-        response = self.app.get('/cohorts/detail/1-OI--LASJLI---LIJSL$EIOJ')
-        assert_equal(response.status_code, 404)
-    
-    def test_get_cohort_by_id_not_found(self):
-        response = self.app.get('/cohorts/detail/133715435033')
-        assert_equal(response.status_code, 404)
     
     def test_cohort_upload_finish_existing_name(self):
         response = self.app.post('/cohorts/create', data=dict(
@@ -164,6 +162,12 @@
         assert_equal(cohort.default_project, 'enwiki')
     
     def test_validate_cohort_name_allowed(self):
+        response = self.app.get('/cohorts/validate/name?name=sleijslij')
+        
+        assert_equal(response.status_code, 200)
+        assert_equal(json.loads(response.data), True)
+    
+    def test_validate_cohort_name_not_allowed(self):
         response = self.app.get('/cohorts/validate/name?name={0}'.format(
             self.cohort.name)
         )
diff --git a/tests/test_metrics/test_survivors.py 
b/tests/test_metrics/test_survivors.py
index 8319057..bb35aea 100644
--- a/tests/test_metrics/test_survivors.py
+++ b/tests/test_metrics/test_survivors.py
@@ -1,8 +1,8 @@
-from datetime import datetime
+from datetime import datetime, timedelta
 from time import time
 from nose.tools import assert_equal
 
-from tests.fixtures import DatabaseWithSurvivorCohortTest
+from tests.fixtures import DatabaseTest, i
 from wikimetrics.metrics import Survival
 from wikimetrics.models import (
     Cohort, MetricReport, WikiUser, CohortWikiUser, MediawikiUser,
@@ -10,113 +10,112 @@
 )
 
 
-class SurvivalTest(DatabaseWithSurvivorCohortTest):
+class SurvivalTest(DatabaseTest):
+    
+    def setUp(self):
+        one_hour = timedelta(hours=1)
+        reg = datetime.now() - one_hour * 48
+        
+        DatabaseTest.setUp(self)
+        self.create_test_cohort(
+            editor_count=3,
+            revisions_per_editor=2,
+            user_registrations=i(reg),
+            revision_timestamps=[
+                [i(reg + one_hour)     , i(reg + one_hour * 25)],
+                [i(reg + one_hour * 20), i(reg + one_hour * 40)],
+                [i(reg + one_hour * 30), i(reg + one_hour * 73)],
+            ],
+            revision_lengths=10
+        )
+        
+        self.e1 = self.editors[0].user_id
+        self.e2 = self.editors[1].user_id
+        self.e3 = self.editors[2].user_id
     
     def test_case1_24h_count1(self):
         m = Survival(
-            namespaces=[self.survivors_namespace],
-            survival_hours=1 * 24
+            namespaces=[0],
+            survival_hours=24,
         )
         results = m(list(self.cohort), self.mwSession)
-
-        assert_equal(results[self.mw_dan_id][Survival.id], True)
-        assert_equal(results[self.mw_evan_id][Survival.id], True)
-        assert_equal(results[self.mw_andrew_id][Survival.id] , True)
+        
+        assert_equal(results[self.e1][Survival.id], True)
+        assert_equal(results[self.e2][Survival.id], True)
+        assert_equal(results[self.e3][Survival.id] , True)
     
     def test_case1_72h_count1(self):
         m = Survival(
-            namespaces=[self.survivors_namespace],
-            survival_hours=3 * 24
+            namespaces=[0],
+            survival_hours=72,
         )
         results = m(list(self.cohort), self.mwSession)
-
-        assert_equal(results[self.mw_dan_id][Survival.id], False)
-        assert_equal(results[self.mw_evan_id][Survival.id], False)
-        assert_equal(results[self.mw_andrew_id][Survival.id] , True)
+        
+        assert_equal(results[self.e1][Survival.id], False)
+        assert_equal(results[self.e2][Survival.id], False)
+        assert_equal(results[self.e3][Survival.id] , True)
     
     def test_case1_24h_count3(self):
         m = Survival(
-            namespaces=[self.survivors_namespace],
-            survival_hours=1 * 24,
-            number_of_edits=3
-        )
-        results = m(list(self.cohort), self.mwSession)
-
-        assert_equal(results[self.mw_dan_id][Survival.id], False)
-        assert_equal(results[self.mw_evan_id][Survival.id], False)
-        assert_equal(results[self.mw_andrew_id][Survival.id] , True)
-    
-    def test_case2_24h_count3_sunset72h(self):
-        m = Survival(
-            namespaces=[self.survivors_namespace],
+            namespaces=[0],
             survival_hours=1 * 24,
             number_of_edits=3,
-            sunset_in_hours=3 * 24
         )
         results = m(list(self.cohort), self.mwSession)
-
-        assert_equal(results[self.mw_dan_id][Survival.id], False)
-        assert_equal(results[self.mw_evan_id][Survival.id], False)
-        assert_equal(results[self.mw_andrew_id][Survival.id] , True)
+        
+        assert_equal(results[self.e1][Survival.id], False)
+        assert_equal(results[self.e2][Survival.id], False)
+        assert_equal(results[self.e3][Survival.id], False)
+    
+    def test_case2_24h_count2_sunset72h(self):
+        m = Survival(
+            namespaces=[0],
+            survival_hours=24,
+            number_of_edits=2,
+            sunset_in_hours=72,
+        )
+        results = m(list(self.cohort), self.mwSession)
+        
+        assert_equal(results[self.e1][Survival.id], False)
+        assert_equal(results[self.e2][Survival.id], False)
+        assert_equal(results[self.e3][Survival.id] , True)
     
     def test_default(self):
         m = Survival(
-            namespaces=[self.survivors_namespace],
+            namespaces=[0],
         )
         results = m(list(self.cohort), self.mwSession)
         #self.debug_query = m.debug_query
-
-        assert_equal(results[self.mw_dan_id][Survival.id], True)
-        assert_equal(results[self.mw_evan_id][Survival.id], True)
-        assert_equal(results[self.mw_andrew_id][Survival.id] , True)
+        
+        assert_equal(results[self.e1][Survival.id], True)
+        assert_equal(results[self.e2][Survival.id], True)
+        assert_equal(results[self.e3][Survival.id] , True)
     
     def test_censored1(self):
-        
-        # NOTE: setting sunset_in_hours 10000 days in the future
-        # This means that in 82 years, this test will break
         m = Survival(
-            namespaces=[self.survivors_namespace],
-            number_of_edits=6,
-            survival_hours=30000 * 24,
+            namespaces=[0],
+            number_of_edits=3,
+            survival_hours=24,
             sunset_in_hours=30000 * 24
         )
         results = m(list(self.cohort), self.mwSession)
         assert_equal(results, {
-            self.mw_dan_id: {'censored': 1, Survival.id: 0},
-            self.mw_evan_id: {'censored': 1, Survival.id: 0},
-            self.mw_andrew_id: {'censored': 1, Survival.id: 0},
-            self.mw_diederik_id: {'censored': 0, Survival.id: 0},
+            self.e1: {'censored': 1, Survival.id: 0},
+            self.e2: {'censored': 1, Survival.id: 0},
+            self.e3: {'censored': 1, Survival.id: 0},
         })
-
+    
     def test_censored2(self):
-        
-        # NOTE: setting sunset_in_hours 10000 days in the future
-        # This means that in 82 years, this test will break
-        andrew_user = self.mwSession.query(MediawikiUser) \
-            .filter(MediawikiUser.user_id == self.mw_andrew_id) \
-            .first()
-        andrew_revs = self.mwSession.query(Revision) \
-            .join(MediawikiUser) \
-            .filter(MediawikiUser.user_id == self.mw_andrew_id) \
-            .order_by(Revision.rev_timestamp) \
-            .all()
-
         m = Survival(
-            namespaces=[self.survivors_namespace],
+            namespaces=[0],
             number_of_edits=2,
-            survival_hours=int(
-                ((andrew_revs[2].rev_timestamp - andrew_user.user_registration)
-                    .total_seconds())
-                /
-                (3600)
-            ),
-            sunset_in_hours=2 * 48
+            survival_hours=0,
+            sunset_in_hours=26
         )
         results = m(list(self.cohort), self.mwSession)
-
+        
         assert_equal(results, {
-            self.mw_dan_id: {'censored': 0, Survival.id: 0},
-            self.mw_evan_id: {'censored': 0, Survival.id: 0},
-            self.mw_andrew_id: {'censored': 0, Survival.id: 1},
-            self.mw_diederik_id: {'censored': 0, Survival.id: 0},
+            self.e1: {'censored': 0, Survival.id: 1},
+            self.e2: {'censored': 0, Survival.id: 0},
+            self.e3: {'censored': 0, Survival.id: 0},
         })
diff --git a/tests/test_models/test_cohort.py b/tests/test_models/test_cohort.py
index bd94059..008f968 100644
--- a/tests/test_models/test_cohort.py
+++ b/tests/test_models/test_cohort.py
@@ -1,5 +1,5 @@
 from nose.tools import assert_equal
-from wikimetrics.models import Cohort
+from wikimetrics.models import Cohort, WikiUser
 from ..fixtures import DatabaseTest
 
 
@@ -9,26 +9,42 @@
         self.common_cohort_1()
     
     def test_iter(self):
-        self.editors[0].valid = False
-        self.editors[1].valid = None
+        user_ids = list(self.cohort)
+        assert_equal([e.user_id for e in self.editors], user_ids)
+    
+    def test_empty_iter(self):
+        self.cohort.validated = False
         self.session.commit()
         
         user_ids = list(self.cohort)
-        assert_equal(self.editors[0].user_id in user_ids, False)
-        assert_equal(self.editors[1].user_id in user_ids, False)
-        assert_equal(self.editors[2].user_id in user_ids, True)
-        assert_equal(self.editors[3].user_id in user_ids, True)
+        assert_equal(user_ids, [])
+    
+    def test_iter_with_invalid(self):
+        wikiusers = self.session.query(WikiUser).all()
+        wikiusers[0].valid = False
+        wikiusers[1].valid = None
+        self.session.commit()
+        
+        user_ids = list(self.cohort)
+        assert_equal(wikiusers[0].mediawiki_userid in user_ids, False)
+        assert_equal(wikiusers[1].mediawiki_userid in user_ids, False)
+        assert_equal(wikiusers[2].mediawiki_userid in user_ids, True)
+        assert_equal(wikiusers[3].mediawiki_userid in user_ids, True)
         assert_equal(len(user_ids), 2)
     
     def test_group_by_project(self):
-        self.editors[0].valid = False
-        self.editors[1].valid = None
+        wikiusers = self.session.query(WikiUser).all()
+        print [(w.mediawiki_userid, w.valid, w.mediawiki_username) for w in 
wikiusers]
+        wikiusers[0].valid = False
+        wikiusers[1].valid = None
         self.session.commit()
         
-        grouped = self.cohort.group_by_project()
-        user_ids = list(list(grouped)[0])
-        assert_equal(self.editors[0].user_id in user_ids, False)
-        assert_equal(self.editors[1].user_id in user_ids, False)
-        assert_equal(self.editors[2].user_id in user_ids, True)
-        assert_equal(self.editors[3].user_id in user_ids, True)
+        user_ids = []
+        for project, uids in self.cohort.group_by_project():
+            user_ids += list(uids)
+        
+        assert_equal(wikiusers[0].mediawiki_userid in user_ids, False)
+        assert_equal(wikiusers[1].mediawiki_userid in user_ids, False)
+        assert_equal(wikiusers[2].mediawiki_userid in user_ids, True)
+        assert_equal(wikiusers[3].mediawiki_userid in user_ids, True)
         assert_equal(len(user_ids), 2)
diff --git a/tests/test_models/test_run_report.py 
b/tests/test_models/test_run_report.py
index eb6010a..908dcce 100644
--- a/tests/test_models/test_run_report.py
+++ b/tests/test_models/test_run_report.py
@@ -55,6 +55,8 @@
         self.session.commit()
         
         for name, metric in metric_classes.iteritems():
+            if not metric.show_in_ui: continue
+            
             desired_responses = [{
                 'name': '{0} - test'.format(name),
                 'cohort': {
@@ -74,11 +76,11 @@
             }]
             jr = RunReport(desired_responses, user_id=self.owner_user_id)
             celery_task = jr.task.delay(jr)
-            # return value is not used from this .get() call
-            celery_task.get()
-            error_message = '{0} ran with invalid cohort'.format(name)
-            assert_equals(celery_task.ready(), True, error_message)
-            assert_equals(celery_task.failed(), True, error_message)
+            results = celery_task.get()
+            assert_true(
+                results['FAILURE'].find('ran with invalid cohort') >= 0,
+                '{0} ran with invalid cohort'.format(name)
+            )
     
     def test_aggregated_response_namespace_edits(self):
         desired_responses = [{
diff --git a/wikimetrics/config/db_config.yaml 
b/wikimetrics/config/db_config.yaml
index 2be115e..4a22149 100644
--- a/wikimetrics/config/db_config.yaml
+++ b/wikimetrics/config/db_config.yaml
@@ -1,4 +1,4 @@
-SQL_ECHO                        : True
+SQL_ECHO                        : False
 #WIKIMETRICS_ENGINE_URL          : 'sqlite:///test.db'
 #MEDIAWIKI_ENGINE_URL_TEMPLATE   : 'sqlite:///{0}.db'
 # For testing with mysql locally (useful for manual connection survival tests)
diff --git a/wikimetrics/controllers/cohorts.py 
b/wikimetrics/controllers/cohorts.py
index 83d1e29..ab7173e 100644
--- a/wikimetrics/controllers/cohorts.py
+++ b/wikimetrics/controllers/cohorts.py
@@ -3,7 +3,9 @@
 from flask import url_for, flash, render_template, redirect, request
 from flask.ext.login import current_user
 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
-from ..utils import json_response, json_error, json_redirect, 
deduplicate_by_key
+from ..utils import (
+    json_response, json_error, json_redirect, deduplicate_by_key, Unauthorized
+)
 from ..configurables import app, db
 from ..models import (
     Cohort, CohortUser, CohortUserRole,
@@ -28,8 +30,9 @@
         .join(CohortUser)\
         .join(User)\
         .filter(User.id == current_user.id)\
-        .filter(CohortUser.role.in_([CohortUserRole.OWNER, 
CohortUserRole.VIEWER]))\
+        .filter(CohortUser.role.in_(CohortUserRole.SAFE_ROLES))\
         .filter(Cohort.enabled)\
+        .filter(Cohort.validated)\
         .all()
     
     db_session.close()
@@ -54,61 +57,43 @@
     full_detail = request.args.get('full_detail', 0)
     
     cohort = None
-    if str(name_or_id).isdigit():
-        cohort = get_cohort_by_id(int(name_or_id))
-    else:
-        cohort = get_cohort_by_name(name_or_id)
-    
-    if cohort:
-        limit = None if full_detail == 'true' else 3
-        cohort_with_wikiusers = populate_cohort_wikiusers(cohort, limit)
-        return json_response(cohort_with_wikiusers)
-    
-    return '{}', 404
-
-
-def get_cohort_query():
-    allowed_roles = [CohortUserRole.OWNER, CohortUserRole.VIEWER]
     db_session = db.get_session()
-    return (
-        db_session.query(Cohort)
-                  .join(CohortUser)
-                  .join(User)
-                  .filter(User.id == current_user.id)
-                  .filter(CohortUser.role.in_(allowed_roles))
-                  .filter(Cohort.enabled),
-        db_session
-    )
-
-
-def get_cohort_by_id(id):
     try:
-        query, db_session = get_cohort_query()
-        cohort = query.filter(Cohort.id == id).one()
+        if str(name_or_id).isdigit():
+            cohort = Cohort.get_safely(db_session, current_user.id, 
by_id=int(name_or_id))
+        else:
+            cohort = Cohort.get_safely(db_session, current_user.id, 
by_name=name_or_id)
+    except Unauthorized:
+        return 'You are not allowed to access this Cohort', 401
+    except NoResultFound:
+        return 'Could not find this Cohort', 404
+    finally:
         db_session.close()
-        return cohort
-    except (MultipleResultsFound, NoResultFound):
-        return None
+    
+    limit = None if full_detail == 'true' else 3
+    cohort_with_wikiusers = populate_cohort_wikiusers(cohort, limit)
+    return json_response(cohort_with_wikiusers)
 
 
 def get_cohort_by_name(name):
+    """
+    Gets a cohort by name, without checking access or worrying about duplicates
+    """
     try:
-        query, db_session = get_cohort_query()
-        cohort = query.filter(Cohort.name == name).one()
+        db_session = db.get_session()
+        return db_session.query(Cohort).filter(Cohort.name == name).first()
+    finally:
         db_session.close()
-        return cohort
-    except (MultipleResultsFound, NoResultFound):
-        return None
 
 
 def populate_cohort_wikiusers(cohort, limit):
+    """
+    Fetches up to <limit> WikiUser records belonging to <cohort>
+    """
     db_session = db.get_session()
-    wikiusers = db_session.query(WikiUser)\
-        .join(CohortWikiUser)\
-        .filter(CohortWikiUser.cohort_id == cohort.id)\
-        .limit(limit)\
-        .all()
-    
+    wikiusers = cohort.filter_wikiuser_query(
+        db_session.query(WikiUser)
+    ).limit(limit).all()
     db_session.close()
     cohort_dict = cohort._asdict()
     cohort_dict['wikiusers'] = [wu._asdict() for wu in wikiusers]
diff --git a/wikimetrics/models/cohort.py b/wikimetrics/models/cohort.py
index 86edd83..e8a3b30 100644
--- a/wikimetrics/models/cohort.py
+++ b/wikimetrics/models/cohort.py
@@ -46,13 +46,11 @@
     def __iter__(self):
         """ returns list of user_ids """
         db_session = db.get_session()
-        tuples_with_ids = db_session\
-            .query(WikiUser.mediawiki_userid)\
-            .join(CohortWikiUser)\
-            .filter(CohortWikiUser.cohort_id == self.id)\
-            .all()
+        wikiusers = self.filter_wikiuser_query(
+            db_session.query(WikiUser.mediawiki_userid)
+        ).all()
         db_session.close()
-        return (t[0] for t in tuples_with_ids)
+        return (r.mediawiki_userid for r in wikiusers)
     
     def group_by_project(self):
         """
@@ -68,49 +66,67 @@
         into a set of project-homogenous cohorts, which can be
         analyzed using a single database connection
         """
-        
         db_session = db.get_session()
-        user_id_projects = db_session\
-            .query(WikiUser.mediawiki_userid, WikiUser.project)\
-            .join(CohortWikiUser)\
-            .filter(CohortWikiUser.cohort_id == self.id)\
-            .order_by(WikiUser.project)\
-            .all()
+        user_id_projects = self.filter_wikiuser_query(
+            db_session.query(WikiUser.mediawiki_userid, WikiUser.project)
+        ).order_by(WikiUser.project).all()
         db_session.close()
         # TODO: push this logic into sqlalchemy.  The solution
         # includes subquery(), but I can't seem to get anything working
         groups = itertools.groupby(user_id_projects, key=itemgetter(1))
-
-        # note: the below line is more concise but harder to read
-        #return ((project, (r[0] for r in users)) for project, users in groups)
-        for project, users in groups:
-            yield project or self.default_project, (r[0] for r in users)
+        
+        return (
+            (project or self.default_project, (r[0] for r in users))
+            for project, users in groups
+        )
+    
+    def filter_wikiuser_query(self, wikiusers_query):
+        """
+        Parameters:
+            wikiusers_query : a sqlalchemy query object asking for one or more
+                                properties of WikiUser
+        
+        Return:
+            the query object passed in, filtered and joined to the
+            appropriate tables, and restricted to this cohort
+        """
+        return wikiusers_query\
+            .join(CohortWikiUser)\
+            .join(Cohort)\
+            .filter(Cohort.id == self.id)\
+            .filter(Cohort.validated)\
+            .filter(WikiUser.valid)
     
     @staticmethod
-    def get_safely(db_session, user_id, cohort_id):
+    def get_safely(db_session, user_id, by_id=None, by_name=None):
         """
         Gets a cohort but first checks permissions on it
         
         Parameters
             db_session  : the database session to query
             user_id     : the user that should have access to the cohort
-            cohort_id   : the cohort to get
+            by_id       : the cohort id to get.  <by_id> or <by_name> is True
+            by_name     : the cohort name to get.  <by_id> or <by_name> is True
         
         Returns
-            If found, a Cohort instance with id == cohort_id
+            If found, a Cohort instance with id == cohort_id or name == 
cohort_name
         
         Raises
             NoResultFound   : cohort did not exist
             Unauthorized    : user_id is not allowed to access this cohort
         """
-        cohort, role = db_session.query(Cohort, CohortUser.role)\
+        query = db_session.query(Cohort, CohortUser.role)\
             .join(CohortUser)\
             .join(User)\
             .filter(User.id == user_id)\
-            .filter(Cohort.id == cohort_id)\
-            .filter(Cohort.enabled)\
-            .one()
+            .filter(Cohort.enabled)
         
+        if by_id:
+            query = query.filter(Cohort.id == by_id)
+        if by_name:
+            query = query.filter(Cohort.name == by_name)
+        
+        cohort, role = query.one()
         if role in CohortUserRole.SAFE_ROLES:
             return cohort
         else:
diff --git a/wikimetrics/models/report_nodes/run_report.py 
b/wikimetrics/models/report_nodes/run_report.py
index e7cb01c..f28accd 100644
--- a/wikimetrics/models/report_nodes/run_report.py
+++ b/wikimetrics/models/report_nodes/run_report.py
@@ -9,6 +9,7 @@
 from wikimetrics.utils import deduplicate
 from report import ReportNode
 from aggregate_report import AggregateReport
+from validate_report import ValidateReport
 
 
 __all__ = ['RunReport']
@@ -44,7 +45,7 @@
             # get cohort
             cohort_dict = cohort_metric_dict['cohort']
             db_session = db.get_session()
-            cohort = Cohort.get_safely(db_session, self.user_id, 
cohort_dict['id'])
+            cohort = Cohort.get_safely(db_session, self.user_id, 
by_id=cohort_dict['id'])
             db_session.close()
             
             # construct metric
@@ -54,7 +55,12 @@
             metric = metric_class(**metric_dict)
             # TODO: don't think csrf can work here, but see if there's another 
way
             metric.fake_csrf()
-            if metric.validate():
+            
+            metric_names.append(metric.label)
+            cohort_names.append(cohort.name)
+            
+            validate_report = ValidateReport(metric, cohort)
+            if validate_report.valid():
                 # construct and start RunReport
                 output_child = AggregateReport(
                     cohort,
@@ -68,10 +74,8 @@
                     user_id=self.user_id,
                 )
                 children.append(output_child)
-                metric_names.append(metric.label)
-                cohort_names.append(cohort.name)
             else:
-                raise Exception('{0} was incorrectly 
configured'.format(metric.label))
+                children.append(validate_report)
         
         metric_names = deduplicate(metric_names)
         cohort_names = deduplicate(cohort_names)
diff --git a/wikimetrics/models/report_nodes/validate_report.py 
b/wikimetrics/models/report_nodes/validate_report.py
new file mode 100644
index 0000000..889387f
--- /dev/null
+++ b/wikimetrics/models/report_nodes/validate_report.py
@@ -0,0 +1,44 @@
+from report import ReportLeaf
+from wikimetrics.utils import stringify
+
+
+class ValidateReport(ReportLeaf):
+    """
+    Checks if a metric and cohort are valid.  If they're not, this ReportLeaf
+    can be added to a Report hierarchy and will report the appropriate errors
+    """
+    
+    show_in_ui = True
+    
+    def __init__(self, metric, cohort):
+        """
+        Parameters
+            metric  : an instance of a wikimetrics.metrics.metric.Metric
+            cohort  : an instance of a wikimetrics.models.cohort.Cohort
+        """
+        super(ValidateReport, self).__init__(parameters=stringify(metric.data))
+        self.metric_valid = metric.validate()
+        self.cohort_valid = cohort.validated
+        self.metric_label = metric.label
+        self.cohort_name = cohort.name
+    
+    def valid(self):
+        return self.metric_valid and self.cohort_valid
+    
+    def run(self):
+        """
+        This will get executed if the instance is added into a Report node 
hierarchy
+        It outputs failure messages due to any invalid configuration.  None of 
these
+        failures should happen unless the user tries to hack the system.
+        """
+        message = ''
+        if not self.cohort_valid:
+            message += '{0} ran with invalid cohort {1}\n'.format(
+                self.metric_label,
+                self.cohort_name,
+            )
+        if not self.metric_valid:
+            message += '{0} was incorrectly configured\n'.format(
+                self.metric_label,
+            )
+        return {'FAILURE': message or 'False'}

-- 
To view, visit https://gerrit.wikimedia.org/r/92581
To unsubscribe, visit https://gerrit.wikimedia.org/r/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: I9cdf44864043e60342e1f4113d2fa3310f71b08e
Gerrit-PatchSet: 7
Gerrit-Project: analytics/wikimetrics
Gerrit-Branch: master
Gerrit-Owner: Milimetric <[email protected]>

_______________________________________________
MediaWiki-commits mailing list
[email protected]
https://lists.wikimedia.org/mailman/listinfo/mediawiki-commits

Reply via email to