Milimetric has submitted this change and it was merged.

Change subject: finished csv output by timeseries
......................................................................


finished csv output by timeseries

Change-Id: I39c0cb3ab9c02ac4d43624a69ea7c06a3692514c
---
M tests/fixtures.py
M tests/test_controllers/test_reports.py
M wikimetrics/configurables.py
M wikimetrics/controllers/home.py
M wikimetrics/controllers/reports.py
M wikimetrics/models/cohort.py
M wikimetrics/models/cohort_user.py
M wikimetrics/models/report_nodes/run_report.py
M wikimetrics/templates/report.html
M wikimetrics/utils.py
10 files changed, 161 insertions(+), 34 deletions(-)

Approvals:
  Milimetric: Verified; Looks good to me, approved



diff --git a/tests/fixtures.py b/tests/fixtures.py
index 4cb62ef..ba00360 100644
--- a/tests/fixtures.py
+++ b/tests/fixtures.py
@@ -51,6 +51,7 @@
         revisions_per_editor=0,
         revision_timestamps=[],
         revision_lengths=[],
+        owner_user_id=None,
     ):
         """
         Parameters
@@ -63,6 +64,7 @@
             revision_lengths        : two dimensional array indexed same as 
above OR
                                         a single integer so all revisions will
                                         have the same length
+            owner_user_id           : record in the User table that owns this 
cohort
         
         Returns
             Nothing but creates the following, to be accessed in a test:
@@ -137,6 +139,22 @@
                 revision.rev_parent_id = ordered_revisions[i - 1].rev_id
         
         self.mwSession.commit()
+        
+        # establish ownership for this cohort
+        if not owner_user_id:
+            #owner_user = User(username='test cohort owner')
+            #self.session.add(owner_user)
+            #self.session.commit()
+            #owner_user_id = owner_user.id
+            # TODO: break dependency on old complicated setUp
+            owner_user_id = self.test_web_user_id
+        
+        self.session.add(CohortUser(
+            user_id=owner_user_id,
+            cohort_id=self.cohort.id,
+            role=CohortUserRole.OWNER,
+        ))
+        self.session.commit()
     
     # create test data for metric PagesCreated
     @nottest
diff --git a/tests/test_controllers/test_reports.py 
b/tests/test_controllers/test_reports.py
index c9453c9..8aac0ce 100644
--- a/tests/test_controllers/test_reports.py
+++ b/tests/test_controllers/test_reports.py
@@ -16,6 +16,19 @@
 
 class ReportsControllerTest(WebTest):
     
+    def setUp(self):
+        WebTest.setUp(self)
+        self.create_test_cohort(
+            editor_count=2,
+            revisions_per_editor=4,
+            revision_timestamps=[
+                [20130101000001, 20130201010000, 20130201010100, 
20130301020100],
+                [20130101000001, 20130201010000, 20130201010100, 
20130301020100]
+            ],
+            revision_lengths=10,
+            owner_user_id=self.test_web_user_id,
+        )
+    
     def test_index(self):
         response = self.app.get('/reports/', follow_redirects=True)
         assert_true(
@@ -63,7 +76,7 @@
         desired_responses = [{
             'name': 'Edits - test',
             'cohort': {
-                'id': self.test_cohort_id,
+                'id': self.cohort.id,
             },
             'metric': {
                 'name': 'NamespaceEdits',
@@ -141,13 +154,13 @@
         desired_responses = [{
             'name': 'Edits - test',
             'cohort': {
-                'id': self.test_cohort_id,
+                'id': self.cohort.id,
             },
             'metric': {
                 'name': 'NamespaceEdits',
                 'namespaces': [0, 1, 2],
-                'start_date': '2013-06-01 00:00:00',
-                'end_date': '2013-09-01 00:00:00',
+                'start_date': '2013-01-01 00:00:00',
+                'end_date': '2013-05-01 00:00:00',
                 'individualResults': False,
                 'aggregateResults': True,
                 'aggregateSum': False,
@@ -172,7 +185,7 @@
         
         # Check the csv result
         response = self.app.get('/reports/result/{0}.csv'.format(result_key))
-        assert_true(response.data.find('Average') >= 0)
+        assert_true(response.data.find('Average,4.0') >= 0)
 
         # Testing to see if the parameters are also in the CSV
         # (related to Mingle 1089)
@@ -187,13 +200,13 @@
         desired_responses = [{
             'name': 'Edits - test',
             'cohort': {
-                'id': self.test_cohort_id,
+                'id': self.cohort.id,
             },
             'metric': {
                 'name': 'NamespaceEdits',
                 'namespaces': [0, 1, 2],
-                'start_date': '2013-06-01 00:00:00',
-                'end_date': '2013-09-01 00:00:00',
+                'start_date': '2013-01-01 00:00:00',
+                'end_date': '2013-04-01 00:00:00',
                 'individualResults': False,
                 'aggregateResults': True,
                 'aggregateSum': True,
@@ -218,20 +231,20 @@
         
         # Check the csv result
         response = self.app.get('/reports/result/{0}.csv'.format(result_key))
-        assert_true(response.data.find('Sum') >= 0)
+        assert_true(response.data.find('Sum,8.0') >= 0)
     
     def test_report_result_std_dev_only_csv(self):
         # Make the request
         desired_responses = [{
             'name': 'Edits - test',
             'cohort': {
-                'id': self.test_cohort_id,
+                'id': self.cohort.id,
             },
             'metric': {
                 'name': 'NamespaceEdits',
                 'namespaces': [0, 1, 2],
-                'start_date': '2013-06-01 00:00:00',
-                'end_date': '2013-09-01 00:00:00',
+                'start_date': '2013-01-01 00:00:00',
+                'end_date': '2013-04-01 00:00:00',
                 'individualResults': False,
                 'aggregateResults': True,
                 'aggregateSum': False,
@@ -257,3 +270,62 @@
         # Check the csv result
         response = self.app.get('/reports/result/{0}.csv'.format(result_key))
         assert_true(response.data.find('Standard Deviation') >= 0)
+    
+    def test_report_result_timeseries_csv(self):
+        # Make the request
+        desired_responses = [{
+            'name': 'Edits - test',
+            'cohort': {
+                'id': self.cohort.id,
+            },
+            'metric': {
+                'name': 'NamespaceEdits',
+                'timeseries': 'month',
+                'namespaces': [0, 1, 2],
+                'start_date': '2013-01-01 00:00:00',
+                'end_date': '2013-05-01 00:00:00',
+                'individualResults': True,
+                'aggregateResults': True,
+                'aggregateSum': False,
+                'aggregateAverage': True,
+                'aggregateStandardDeviation': False,
+            },
+        }]
+        json_to_post = json.dumps(desired_responses)
+        
+        response = self.app.post('/reports/create/', data=dict(
+            responses=json_to_post
+        ))
+        
+        # Wait a second for the task to get processed
+        time.sleep(1)
+        
+        # Check that the task has been created
+        response = self.app.get('/reports/list/')
+        parsed = json.loads(response.data)
+        result_key = parsed['reports'][-1]['result_key']
+        task, report = get_celery_task(result_key)
+        
+        # Check the csv result
+        response = self.app.get('/reports/result/{0}.csv'.format(result_key))
+        assert_true(response.data.find(
+            'user_id,submetric,'
+            '2013-01-01 00:00:00,2013-02-01 00:00:00,'
+            '2013-03-01 00:00:00,2013-04-01 00:00:00'
+        ) >= 0)
+        assert_true(response.data.find(
+            '{0},edits,1,2,1,0'.format(self.editors[0].user_id)
+        ) >= 0)
+        assert_true(response.data.find(
+            '{0},edits,1,2,1,0'.format(self.editors[1].user_id)
+        ) >= 0)
+        assert_true(response.data.find(
+            'Average,edits,1.0,2.0,1.0,0.0'
+        ) >= 0)
+
+        # Testing to see if the parameters are also in the CSV
+        assert_true(response.data.find('parameters') >= 0)
+        assert_true(response.data.find('start_date') >= 0)
+        assert_true(response.data.find('end_date') >= 0)
+        assert_true(response.data.find('namespaces') >= 0)
+        assert_true(response.data.find('metric/cohort') >= 0)
diff --git a/wikimetrics/configurables.py b/wikimetrics/configurables.py
index 2cebd01..bd6733d 100644
--- a/wikimetrics/configurables.py
+++ b/wikimetrics/configurables.py
@@ -100,12 +100,12 @@
     """
     cmd = ['git', 'log', '--date', 'relative', "--pretty=format:'%an %ar %h'", 
'-n', '1']
     p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE)
-    version, err =  p.communicate()
+    version, err = p.communicate()
     if err is not None:
         version = 'Unknown version'
     cmd = ['git', 'log', '--date', 'relative', "--pretty=format:%h", '-n', '1']
     p = subprocess.Popen(cmd, shell=False, stdout=subprocess.PIPE)
-    latest, err =  p.communicate()
+    latest, err = p.communicate()
     if err is not None:
         latest = 'unknown'
     
diff --git a/wikimetrics/controllers/home.py b/wikimetrics/controllers/home.py
index 86b10dc..dcdf088 100644
--- a/wikimetrics/controllers/home.py
+++ b/wikimetrics/controllers/home.py
@@ -40,6 +40,7 @@
 def override_url_for():
     return dict(url_for=dated_url_for)
 
+
 def dated_url_for(endpoint, **values):
     """
     Cache-busting version of url_for, works only for static files
diff --git a/wikimetrics/controllers/reports.py 
b/wikimetrics/controllers/reports.py
index 63c1fc9..e2797f0 100644
--- a/wikimetrics/controllers/reports.py
+++ b/wikimetrics/controllers/reports.py
@@ -107,13 +107,12 @@
     
     if celery_task.ready():
         task_result = get_celery_task_result(celery_task, pj)
-        parameters = json.loads(pj.parameters)
+        p = json.loads(pj.parameters)
         
-        if 'timeseries' in parameters\
-            and parameters['timeseries'] != TimeseriesChoices.NONE:
-            csv_io = get_timeseries_csv(task_result, pj, parameters)
+        if 'timeseries' in p and p['timeseries'] != TimeseriesChoices.NONE:
+            csv_io = get_timeseries_csv(task_result, pj, p)
         else:
-            csv_io = get_simple_csv(task_result, pj, parameters)
+            csv_io = get_simple_csv(task_result, pj, p)
         
         res = Response(csv_io.getvalue(), mimetype='text/csv')
         res.headers['Content-Disposition'] =\
diff --git a/wikimetrics/models/cohort.py b/wikimetrics/models/cohort.py
index 2fea4d6..5bb919e 100644
--- a/wikimetrics/models/cohort.py
+++ b/wikimetrics/models/cohort.py
@@ -1,9 +1,13 @@
 import itertools
 from operator import itemgetter
 from sqlalchemy import Column, Integer, Boolean, DateTime, String, func
+
+from wikimetrics.utils import Unauthorized
 from wikimetrics.configurables import db
 from wikiuser import WikiUser
 from cohort_wikiuser import CohortWikiUser
+from cohort_user import CohortUser, CohortUserRole
+from user import User
 
 
 __all__ = ['Cohort']
@@ -80,3 +84,33 @@
         #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)
+    
+    @staticmethod
+    def get_safely(db_session, user_id, cohort_id):
+        """
+        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
+        
+        Returns
+            If found, a Cohort instance with id == cohort_id
+        
+        Raises
+            NoResultFound   : cohort did not exist
+            Unauthorized    : user_id is not allowed to access this cohort
+        """
+        cohort, role = db_session.query(Cohort, CohortUser.role)\
+            .join(CohortUser)\
+            .join(User)\
+            .filter(User.id == user_id)\
+            .filter(Cohort.id == cohort_id)\
+            .filter(Cohort.enabled)\
+            .one()
+        
+        if role in CohortUserRole.SAFE_ROLES:
+            return cohort
+        else:
+            raise Unauthorized('You are not allowed to use this cohort')
diff --git a/wikimetrics/models/cohort_user.py 
b/wikimetrics/models/cohort_user.py
index a5fbf91..42b31b0 100644
--- a/wikimetrics/models/cohort_user.py
+++ b/wikimetrics/models/cohort_user.py
@@ -10,6 +10,7 @@
 class CohortUserRole(object):
     OWNER = 'OWNER'
     VIEWER = 'VIEWER'
+    SAFE_ROLES = [OWNER, VIEWER]
 
 
 class CohortUser(db.WikimetricsBase):
diff --git a/wikimetrics/models/report_nodes/run_report.py 
b/wikimetrics/models/report_nodes/run_report.py
index 8328399..e7cb01c 100644
--- a/wikimetrics/models/report_nodes/run_report.py
+++ b/wikimetrics/models/report_nodes/run_report.py
@@ -1,3 +1,5 @@
+from sqlalchemy.orm.exc import NoResultFound
+
 from wikimetrics.configurables import db
 from wikimetrics.models.cohort_user import CohortUserRole
 from wikimetrics.models.cohort import Cohort
@@ -36,20 +38,13 @@
         children = []
         metric_names = []
         cohort_names = []
-        allowed_roles = [CohortUserRole.OWNER, CohortUserRole.VIEWER]
+        
         for cohort_metric_dict in desired_responses:
             
             # get cohort
             cohort_dict = cohort_metric_dict['cohort']
             db_session = db.get_session()
-            cohort = db_session.query(Cohort)\
-                .join(CohortUser)\
-                .join(User)\
-                .filter(User.id == self.user_id)\
-                .filter(Cohort.id == cohort_dict['id'])\
-                .filter(Cohort.enabled)\
-                .filter(CohortUser.role.in_(allowed_roles))\
-                .one()
+            cohort = Cohort.get_safely(db_session, self.user_id, 
cohort_dict['id'])
             db_session.close()
             
             # construct metric
diff --git a/wikimetrics/templates/report.html 
b/wikimetrics/templates/report.html
index 107c1d1..3938bdf 100644
--- a/wikimetrics/templates/report.html
+++ b/wikimetrics/templates/report.html
@@ -82,12 +82,12 @@
                                     <input type="checkbox" data-bind="checked: 
aggregateAverage, attr: {id: tabId() + '-a-avg'}"/>
                                 </div>
                             </div>
-                            <div class="control-group">
-                                <label class="control-label" data-bind="attr: 
{for: tabId() + '-a-std'}">Standard Deviation</label>
-                                <div class="controls">
-                                    <input type="checkbox" data-bind="checked: 
aggregateStandardDeviation, attr: {id: tabId() + '-a-std'}"/>
-                                </div>
-                            </div>
+                            <!--<div class="control-group">-->
+                                <!--<label class="control-label" 
data-bind="attr: {for: tabId() + '-a-std'}">Standard Deviation</label>-->
+                                <!--<div class="controls">-->
+                                    <!--<input type="checkbox" 
data-bind="checked: aggregateStandardDeviation, attr: {id: tabId() + 
'-a-std'}"/>-->
+                                <!--</div>-->
+                            <!--</div>-->
                         </div>
                     </div>
                 </div>
diff --git a/wikimetrics/utils.py b/wikimetrics/utils.py
index 738226d..8e7f2e6 100644
--- a/wikimetrics/utils.py
+++ b/wikimetrics/utils.py
@@ -109,3 +109,10 @@
             uniques[key] = o
     
     return uniques.values()
+
+
+class Unauthorized(Exception):
+    """
+    Different exception type to separate "unauthorized" errors from the rest
+    """
+    pass

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

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

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

Reply via email to