Skip to content
This repository was archived by the owner on Dec 11, 2018. It is now read-only.

Commit eb7942a

Browse files
committed
Add aggregated scores report
1 parent 21f2be0 commit eb7942a

File tree

9 files changed

+254
-13
lines changed

9 files changed

+254
-13
lines changed

data/i18n/en.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
"nav-proposals-queue": "Review queue",
1818
"nav-proposals-search": "Search",
1919
"nav-reports": "Reports",
20+
"nav-reports-aggregated": "Aggregated scores",
2021
"nav-admin": "Admin",
2122
"nav-users": "Manage users",
2223
"nav-account": "My Account",
@@ -166,5 +167,15 @@
166167
"summary-list-mean": "Mean",
167168
"summary-list-median": "Median",
168169
"summary-list-range": "Range",
169-
"summary-list-stddev": "Std dev"
170+
"summary-list-stddev": "Std dev",
171+
172+
"report-aggregated-proposal": "Proposal",
173+
"report-aggregated-theme": "Theme",
174+
"report-aggregated-amount": "Amount",
175+
"report-aggregated-impact": "Impact",
176+
"report-aggregated-innovation": "Innovation",
177+
"report-aggregated-ability": "Ability",
178+
"report-aggregated-engagement": "Engagement",
179+
"report-aggregated-recommend": "Recommend",
180+
"report-format-recommend": "$3% ($1/$2)"
170181
}

data/templates/base.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
<li class="dropdown">
4545
<a href="#" class="dropdown-toggle" data-toggle="dropdown">{{ 'nav-reports'|message }} <b class="caret"></b></a>
4646
<ul class="dropdown-menu">
47+
<li><a href="{{ urlFor( 'reports_aggregated' ) }}">{{ 'nav-reports-aggregated'|message }}</a></li>
4748
</ul>
4849
</li>
4950

data/templates/proposals/search.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
{% extends "proposals/base.html" %}
1+
{% extends 'proposals/base.html' %}
22
{% set route = app.router.getCurrentRoute.getName %}
33

44
{% block content %}

data/templates/reports/base.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{% extends "base_auth.html" %}

data/templates/reports/report.html

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{% extends 'reports/base.html' %}
2+
{% set route = app.router.getCurrentRoute.getName %}
3+
{% set suffix = route|split( '_' )|last %}
4+
5+
{% macro format( row, spec ) %}
6+
{% set val = row[spec.column] %}
7+
{% if spec.format == 'number' %}
8+
<td class="text-right">{{ row[spec.column]|number_format( spec.precision ) }}</td>
9+
{% elseif spec.format == 'proposal' %}
10+
<td><a href="{{ urlFor( 'proposals_view', { 'id':row[spec.column] } ) }}">{{ row[spec.text] }}</a></td>
11+
{% elseif spec.format == 'message' %}
12+
{% set vals = [] %}
13+
{% for key in spec.columns %}
14+
{% set vals = vals|merge( [ row[key] ] ) %}
15+
{% endfor %}
16+
<td class="text-center">{{ spec.message|message( vals ) }}</td>
17+
{% else %}
18+
<td>{{ row[spec.column] }}</td>
19+
{% endif %}
20+
{% endmacro %}
21+
22+
{% block content %}
23+
{% spaceless %}
24+
<ol class="breadcrumb">
25+
<li>{{ 'nav-reports'|message }}</li>
26+
<li><a href="{{ urlFor( route ) }}">{{ ( 'nav-reports-' ~ suffix )|message }}</a></li>
27+
</ol>
28+
29+
<table class="table table-striped table-hover table-condensed table-responsive">
30+
<thead>
31+
<tr>
32+
{% for msg in columns|keys %}
33+
<th>{{ msg|message }}</th>
34+
{% endfor %}
35+
</tr>
36+
</thead>
37+
<tbody>
38+
{% for row in report %}
39+
<tr>
40+
{% for col in columns %}
41+
{{ _self.format( row, col ) }}
42+
{% endfor %}
43+
</tr>
44+
{% else %}
45+
<tr><td colspan="8">{{ 'no-results'|message }}</td></tr>
46+
{% endfor %}
47+
</tbody>
48+
</table>
49+
{% endspaceless %}
50+
{% endblock content %}

src/App.php

Lines changed: 28 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ public function __construct( $deployDir ) {
9494

9595
// Slim does not natively understand being behind a proxy
9696
// If not corrected template links created via siteUrl() may use the wrong
97-
// protocol (http instead of https).
97+
// Protocol (http instead of https).
9898
if ( getenv( 'HTTP_X_FORWARDED_PROTO' ) ) {
9999
$proto = getenv( 'HTTP_X_FORWARDED_PROTO' );
100100
$this->slim->environment['slim.url_scheme'] = $proto;
@@ -163,6 +163,15 @@ protected function configureIoc() {
163163
);
164164
} );
165165

166+
$container->singleton( 'reportsDao', function ( $c ) {
167+
$uid = $c->authManager->getUserId();
168+
return new \Wikimedia\IEGReview\Dao\Reports(
169+
$c->settings['db.dsn'],
170+
$c->settings['db.user'], $c->settings['db.pass'],
171+
$uid, $c->log
172+
);
173+
} );
174+
166175
$container->singleton( 'authManager', function ( $c ) {
167176
return new \Wikimedia\IEGReview\AuthManager( $c->usersDao );
168177
} );
@@ -217,7 +226,7 @@ protected function configureIoc() {
217226
* Configure view behavior.
218227
*/
219228
protected function configureView() {
220-
// configure twig views
229+
// Configure twig views
221230
$view = $this->slim->view;
222231

223232
$view->parserOptions = array(
@@ -229,7 +238,7 @@ protected function configureView() {
229238
'autoescape' => true,
230239
);
231240

232-
// install twig parser extensions
241+
// Install twig parser extensions
233242
$view->parserExtensions = array(
234243
new \Slim\Views\TwigExtension(),
235244
new TwigExtension(),
@@ -239,7 +248,7 @@ protected function configureView() {
239248
),
240249
);
241250

242-
// set default view data
251+
// Set default view data
243252
$view->replace( array(
244253
'app' => $this->slim,
245254
'i18nCtx' => $this->slim->i18nContext,
@@ -276,7 +285,7 @@ protected function configureRoutes() {
276285

277286
'require-user' => function () use ( $slim ) {
278287
if ( $slim->authManager->isAnonymous() ) {
279-
// redirect to login form if not authenticated
288+
// Redirect to login form if not authenticated
280289
if ( $slim->request->isGet() ) {
281290
$uri = $slim->request->getUrl() . $slim->request->getPath();
282291
$qs = \Wikimedia\IEGReview\Form::qsMerge();
@@ -298,7 +307,7 @@ protected function configureRoutes() {
298307

299308
'require-admin' => function () use ( $slim ) {
300309
if ( !$slim->authManager->isAdmin() ) {
301-
// redirect to login form if not an admin user
310+
// Redirect to login form if not an admin user
302311
if ( $slim->request->isGet() ) {
303312
$uri = $slim->request->getUrl() . $slim->request->getPath();
304313
$qs = \Wikimedia\IEGReview\Form::qsMerge();
@@ -346,7 +355,7 @@ function () use ( $slim ) {
346355

347356
} );
348357

349-
// routes for authenticated users
358+
// Routes for authenticated users
350359
$slim->group( '/user/',
351360
$middleware['must-revalidate'], $middleware['require-user'],
352361
function () use ( $slim, $middleware ) {
@@ -367,7 +376,7 @@ function () use ( $slim, $middleware ) {
367376
} )->name( 'user_changepassword_post' );
368377
} );
369378

370-
// routes for proposals
379+
// Routes for proposals
371380
$slim->group( '/proposals/',
372381
$middleware['must-revalidate'], $middleware['require-user'],
373382
function () use ( $slim, $middleware ) {
@@ -412,7 +421,17 @@ function () use ( $slim, $middleware ) {
412421
$page->setReviewsDao( $slim->reviewsDao );
413422
$page( $id );
414423
} )->name( 'proposals_view' );
424+
} );
415425

426+
// Routes for reports
427+
$slim->group( '/reports/',
428+
$middleware['must-revalidate'], $middleware['require-user'],
429+
function () use ( $slim, $middleware ) {
430+
$slim->get( 'aggregated', function () use ( $slim ) {
431+
$page = new Controllers\Reports\Aggregated( $slim );
432+
$page->setDao( $slim->reportsDao );
433+
$page();
434+
} )->name( 'reports_aggregated' );
416435
} );
417436

418437
$slim->group( '/admin/',
@@ -457,5 +476,4 @@ public static function template( $slim, $name, $routeName = null ) {
457476
$slim->render( "{$name}.html" );
458477
} )->name( $routeName );
459478
}
460-
461-
} //end App
479+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
<?php
2+
/**
3+
* @section LICENSE
4+
* This file is part of Wikimedia IEG Grant Review application.
5+
*
6+
* Wikimedia IEG Grant Review application is free software: you can
7+
* redistribute it and/or modify it under the terms of the GNU General Public
8+
* License as published by the Free Software Foundation, either version 3 of
9+
* the License, or (at your option) any later version.
10+
*
11+
* Wikimedia IEG Grant Review application is distributed in the hope that it
12+
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13+
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License along
17+
* with Wikimedia IEG Grant Review application. If not, see
18+
* <http://www.gnu.org/licenses/>.
19+
*
20+
* @file
21+
* @copyright © 2014 Bryan Davis, Wikimedia Foundation and contributors.
22+
*/
23+
24+
namespace Wikimedia\IEGReview\Controllers\Reports;
25+
26+
use Wikimedia\IEGReview\Controller;
27+
28+
/**
29+
* Aggregated scores.
30+
*
31+
* @author Bryan Davis <bd808@wikimedia.org>
32+
* @copyright © 2014 Bryan Davis, Wikimedia Foundation and contributors.
33+
*/
34+
class Aggregated extends Controller {
35+
36+
protected function handleGet() {
37+
$this->view->setData( 'columns', array(
38+
'report-aggregated-proposal' => array(
39+
'column' => 'id',
40+
'format' => 'proposal',
41+
'text' => 'title',
42+
),
43+
'report-aggregated-theme' => array(
44+
'column' => 'theme',
45+
),
46+
'report-aggregated-amount' => array(
47+
'column' => 'amount',
48+
'format' => 'number',
49+
'precision' => 0,
50+
),
51+
'report-aggregated-impact' => array(
52+
'column' => 'impact',
53+
'format' => 'number',
54+
'precision' => 2,
55+
),
56+
'report-aggregated-innovation' => array(
57+
'column' => 'innovation',
58+
'format' => 'number',
59+
'precision' => 2,
60+
),
61+
'report-aggregated-ability' => array(
62+
'column' => 'ability',
63+
'format' => 'number',
64+
'precision' => 2,
65+
),
66+
'report-aggregated-engagement' => array(
67+
'column' => 'engagement',
68+
'format' => 'number',
69+
'precision' => 2,
70+
),
71+
'report-aggregated-recommend' => array(
72+
'format' => 'message',
73+
'message' => 'report-format-recommend',
74+
'columns' => array( 'recommend', 'rcnt', 'pcnt' ),
75+
),
76+
) );
77+
$this->view->setData( 'report', $this->dao->aggregatedScores() );
78+
$this->render( 'reports/report.html' );
79+
}
80+
81+
}

src/Dao/Reports.php

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
<?php
2+
/**
3+
* @section LICENSE
4+
* This file is part of Wikimedia IEG Grant Review application.
5+
*
6+
* Wikimedia IEG Grant Review application is free software: you can
7+
* redistribute it and/or modify it under the terms of the GNU General Public
8+
* License as published by the Free Software Foundation, either version 3 of
9+
* the License, or (at your option) any later version.
10+
*
11+
* Wikimedia IEG Grant Review application is distributed in the hope that it
12+
* will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty
13+
* of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14+
* General Public License for more details.
15+
*
16+
* You should have received a copy of the GNU General Public License along
17+
* with Wikimedia IEG Grant Review application. If not, see
18+
* <http://www.gnu.org/licenses/>.
19+
*
20+
* @file
21+
* @copyright © 2014 Bryan Davis, Wikimedia Foundation and contributors.
22+
*/
23+
24+
namespace Wikimedia\IEGReview\Dao;
25+
26+
/**
27+
* Data access object for reports.
28+
*
29+
* @author Bryan Davis <bd808@wikimedia.org>
30+
* @copyright © 2014 Bryan Davis, Wikimedia Foundation and contributors.
31+
*/
32+
class Reports extends AbstractDao {
33+
34+
/**
35+
* @var int|bool $userId
36+
*/
37+
protected $userId;
38+
39+
40+
/**
41+
* @param string $dsn PDO data source name
42+
* @param string $user Database user
43+
* @param string $pass Database password
44+
* @param int|bool $uid Authenticated user
45+
* @param LoggerInterface $logger Log channel
46+
*/
47+
public function __construct(
48+
$dsn, $user, $pass, $uid = false, $logger = null
49+
) {
50+
parent::__construct( $dsn, $user, $pass, $logger );
51+
$this->userId = $uid;
52+
}
53+
54+
public function aggregatedScores() {
55+
$sql = self::concat(
56+
'SELECT p.id, p.title, p.theme, p.amount,',
57+
'r.impact,',
58+
'r.innovation,',
59+
'r.ability,',
60+
'r.engagement,',
61+
'r.recommend,',
62+
'r.cnt AS rcnt,',
63+
'ROUND((r.recommend / r.cnt) * 100, 2) AS pcnt',
64+
'FROM proposals p',
65+
'INNER JOIN (',
66+
'SELECT COUNT(*) AS cnt,',
67+
'AVG(impact) AS impact,',
68+
'AVG(innovation) AS innovation,',
69+
'AVG(ability) AS ability,',
70+
'AVG(engagement) AS engagement,',
71+
'SUM(recommendation) AS recommend,',
72+
'proposal',
73+
'FROM reviews',
74+
'GROUP BY proposal',
75+
') r ON p.id = r.proposal',
76+
'ORDER BY pcnt DESC, rcnt DESC, p.id'
77+
);
78+
return $this->fetchAll( $sql );
79+
}
80+
}

src/Dao/Reviews.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,5 +140,4 @@ public function getReviews( $proposal ) {
140140
);
141141
return $this->fetchAll( $sql, array( $proposal ) );
142142
}
143-
144143
}

0 commit comments

Comments
 (0)