/[drupal]/contributions/report.module
ViewVC logotype

Contents of /contributions/report.module

Parent Directory Parent Directory | Revision Log Revision Log | View Revision Graph Revision Graph


Revision 1.1 - (show annotations) (download) (as text)
Mon Nov 2 17:38:25 2009 UTC (3 weeks ago) by ki
Branch: MAIN
File MIME type: text/x-php
Initial check-in.
1 <?php
2 // $Id$
3
4 /**
5 * @file
6 * Keep track of site activities and report
7 */
8
9 define('REPORT_PERIOD_DAY', 0);
10 define('REPORT_PERIOD_WEEK', 1);
11 define('REPORT_PERIOD_MONTH', 2);
12 define('REPORT_PERIOD_YEAR', 3);
13
14 /**
15 * Implementation of hook_init().
16 */
17 function report_init() {
18 drupal_add_css(drupal_get_path('module', 'report') .'/report.css');
19 }
20
21 /**
22 * Implementation of hook_menu().
23 */
24 function report_menu() {
25 $items['report'] = array(
26 'title' => 'Report',
27 'description' => 'Report',
28 'page callback' => 'drupal_get_form',
29 'page arguments' => array('report_summary'),
30 'access arguments' => array('access report'),
31 'type' => MENU_NORMAL_ITEM,
32 'weight' => 0,
33 );
34 $items['report/summary'] = array(
35 'title' => 'Summary',
36 'type' => MENU_DEFAULT_LOCAL_TASK,
37 'weight' => 0,
38 );
39 $items['report/settings'] = array(
40 'title' => 'Settings',
41 'description' => 'Setting report.',
42 'page callback' => 'drupal_get_form',
43 'page arguments' => array('report_settings'),
44 'access arguments' => array('administer report'),
45 'type' => MENU_LOCAL_TASK,
46 'weight' => 50,
47 );
48
49 return $items;
50 }
51
52 /**
53 * Implementation of hook_perm().
54 */
55 function report_perm() {
56 return array('administer report', 'access report');
57 }
58
59 function report_settings() {
60 $form['report'] = array(
61 '#type' => 'fieldset',
62 '#title' => t('Settings'),
63 '#collapsible' => TRUE,
64 '#weight' => -10,
65 );
66 $form['report']['report_cron_time'] = array(
67 '#type' => 'textfield',
68 '#title' => t('Time for daily snapshot'),
69 '#default_value' => variable_get('report_cron_time', '1:00am'),
70 '#size' => 10,
71 '#maxlength' => 7,
72 '#description' => t('Daily snapshot will run at the first cron after the specified time. e.g. 11:00pm'),
73 );
74 $form['report']['report_use_chart'] = array(
75 '#type' => 'checkbox',
76 '#title' => t('Display chart'),
77 '#default_value' => variable_get('report_use_chart', 1),
78 '#description' => t('If checked, some reports will display charts when appropriate. Requires !chart module.', array('!chart' => l('chart', 'http://drupal.org/project/chart'))),
79 );
80
81 $response_yes = t('I understand that the existing report data will be lost.');
82 $response_no = t('I do not understand that the existing report data will be lost.');
83 $prompt = t('Are you sure to reset and rebuild the data? Note that you will lose all historically collected data, if any, and replace with ones that are known at this time, which may not be accurate. Type \\\'!response\\\' to confirm. The process will take some time.', array('!response' => $response_yes));
84 $form['report']['reset'] = array(
85 '#type' => 'submit',
86 '#value' => t('Reset Data - Warning'),
87 '#attributes' => array('onclick' => "if (prompt('$prompt', '$response_no') == '$response_yes') return true; else return false;"),
88 '#submit' => array('report_reset_submit'),
89 );
90
91 return system_settings_form($form);
92 }
93
94 function report_reset_submit($form, &$form_state) {
95 if ($form_state['clicked_button']['#value'] == t('Reset Data - Warning')) {
96 // Allow to run for several minutes.
97 set_time_limit(1000);
98
99 db_query('TRUNCATE TABLE {report}');
100
101 $date_first = date('Y-m-d', db_result(db_query('SELECT created FROM {node} ORDER BY nid LIMIT 1')));
102 // Set the last date to be yesterday or the day before depending on the cron time to avoid duplicate rows.
103 if (time() > strtotime(variable_get('report_cron_time', '1:00am'))) {
104 $date_last = date('Y-m-d', time() - 86400);
105 }
106 else {
107 $date_last = date('Y-m-d', time() - 86400 * 2);
108 }
109
110 $date = $date_first;
111
112 /* TEST TEST */
113 //$date = '2009-10-01';
114 //$date_last = '2009-01-03';
115 /* TEST TEST */
116
117 while ($date <= $date_last) {
118 module_invoke_all('report_cron', $date);
119
120 $date = date('Y-m-d', strtotime($date .' +1 day'));
121 }
122
123 drupal_set_message(t('Resetting data ran successfully.'));
124 }
125 }
126
127 function report_summary() {
128 // No content. Placeholder for other submodules.
129 }
130
131 function report_insert_data($realm, $data, $date) {
132 $sql = "INSERT INTO {report} (date, realm, data) VALUES ('%s', '%s', '%s')";
133 db_query($sql, $date, $realm, serialize($data));
134 }
135
136 function report_chart($report) {
137 $range = isset($_POST['range']) ? $_POST['range'] : variable_get('report_chart_range', '10');
138
139 if (!report_use_chart() || $range > 30) {
140 $out = drupal_get_form('report_chart_form');
141 $out = '<div class="report-chart-wrapper">'. $out .'</div>';
142
143 return $out;
144 }
145
146 $chart = array(
147 '#chart_id' => 'report_chart',
148 //'#title' => chart_title(t('User'), '006666', 15),
149 '#type' => CHART_TYPE_LINE,
150 '#size' => chart_size(isset($_POST['width']) ? $_POST['width'] : variable_get('report_chart_width', '800'), isset($_POST['height']) ? $_POST['height'] : variable_get('report_chart_height', '200')),
151 '#adjust_resolution' => TRUE,
152 );
153
154 // Set minimum to marginal value to avoid broken lines. Goolge chart does not connect lines when value is zero
155 // The marginal value is dependent of the scale.
156 $plot_min = 0.017 * $report['#y_range_max'];
157 foreach ($report['#data'] as $key => $data) {
158 foreach ($data as $value) {
159 if ($value < $plot_min) {
160 $value = $plot_min;
161 }
162 $chart['#data'][$key][] = $value;
163 }
164 }
165
166 $chart['#legends'] = $report['#legends'];
167 if (isset($report['#data_colors'])) {
168 $chart['#data_colors'] = $report['#data_colors'];
169 }
170 else {
171 $data_colors = array('00ff00', 'ff0000', '0000ff', 'ffff00', '00ffff', 'ff00ff', '333333', '666666', '999999', 'aaaaaa');
172 $chart['#data_colors'] = array_slice($data_colors, 0, count($report['#legends']));
173 }
174
175 foreach ($report['#x_label'] as $label) {
176 $chart['#mixed_axis_labels'][CHART_AXIS_X_BOTTOM][1][] = chart_mixed_axis_label($label);
177 }
178
179 if (isset($report['#x_label_caption'])) {
180 $chart['#mixed_axis_labels'][CHART_AXIS_X_BOTTOM][2][] = chart_mixed_axis_label($report['#x_label_caption'], 50);
181 }
182
183 $chart['#mixed_axis_labels'][CHART_AXIS_Y_LEFT][0][] = chart_mixed_axis_range_label($report['#y_range_min'], $report['#y_range_max']);
184 if (isset($report['#y_label_caption'])) {
185 $chart['#mixed_axis_labels'][CHART_AXIS_Y_LEFT][1][] = chart_mixed_axis_label($report['#y_label_caption'], 95);
186 }
187
188 $out = drupal_get_form('report_chart_form');
189
190 $out .= chart_render($chart);
191
192 $out = '<div class="report-chart-wrapper">'. $out .'</div>';
193
194 return $out;
195 }
196
197 function report_chart_form() {
198 if (report_use_chart()) {
199 $form['width'] = array(
200 '#type' => 'select',
201 '#title' => t('Width'),
202 '#default_value' => isset($_POST['width']) ? $_POST['width'] : variable_get('report_chart_width', '800'),
203 '#options' => array(400 => 400, 500 => 500, 600 => 600, 700 => 700, 800 => 800, 900 => 900, 999 => 1000),
204 '#attributes' => array('onchange' => 'this.form.submit()'),
205 );
206
207 $form['height'] = array(
208 '#type' => 'select',
209 '#title' => t('Height'),
210 '#default_value' => isset($_POST['height']) ? $_POST['height'] : variable_get('report_chart_height', '200'),
211 '#options' => array(100 => 100, 150 => 150, 200 => 200, 250 => 250, 300 => 300),
212 '#attributes' => array('onchange' => 'this.form.submit()'),
213 );
214 }
215
216 $form['period'] = array(
217 '#type' => 'select',
218 '#title' => t('Period'),
219 '#default_value' => isset($_POST['period']) ? $_POST['period'] : variable_get('report_chart_period', '1'),
220 '#options' => array('Day', 'Week', 'Month', 'Year'),
221 '#attributes' => array('onchange' => 'this.form.submit()'),
222 );
223
224 $form['range'] = array(
225 '#type' => 'select',
226 '#title' => t('Range'),
227 '#default_value' => isset($_POST['range']) ? $_POST['range'] : variable_get('report_chart_range', '10'),
228 '#options' => array(5 => 5, 10 => 10, 20 => 20, 30 => 30, 40 => 40, 50 => 50, 60 => 60, 90 => 90, 120 => 120),
229 '#attributes' => array('onchange' => 'this.form.submit()'),
230 );
231
232 if (report_use_chart()) {
233 $form['info'] = array(
234 '#value' => '<div class="info-wrapper">'. t('Chart supports range up to 30.') .'</div>',
235 );
236 }
237
238 $form['clear'] = array(
239 '#value' => '<div style="clear: both"></div>'
240 );
241
242 if (isset($_POST['range']) && $_POST['range']) {
243 if (report_use_chart()) {
244 variable_set('report_chart_width', $_POST['width']);
245 variable_set('report_chart_height', $_POST['height']);
246 }
247 variable_set('report_chart_period', $_POST['period']);
248 variable_set('report_chart_range', $_POST['range']);
249 }
250
251 return $form;
252 }
253
254 function report_flatten_period($rows, $period) {
255 $size = count($rows[0]['data']);
256
257 switch ($period) {
258 case REPORT_PERIOD_DAY:
259 foreach ($rows as $row) {
260 $out[] = $row['data'];
261 }
262 break;
263
264 case REPORT_PERIOD_WEEK:
265 foreach ($rows as $row) {
266 if ($week != date('W', strtotime($row['data'][0]))) {
267 if ($r) {
268 $out[] = array($week .' ('. date('Y-m-d', strtotime($row['data'][0] .' +1 day')) .')') + $r;
269 }
270 $r = array();
271 $week = date('W', strtotime($row['data'][0]));
272 }
273
274 for ($i = 1; $i < $size; $i++) {
275 $r[$i] += $row['data'][$i];
276 }
277 }
278 // Process left over unfinished days
279 if ($r) {
280 $out[] = array($week .' ('. date('Y-m-d', strtotime($row['data'][0])) .')') + $r;
281 }
282 break;
283
284 case REPORT_PERIOD_MONTH:
285 foreach ($rows as $row) {
286 if ($year_month != date('Y-m', strtotime($row['data'][0]))) {
287 if ($r) {
288 $out[] = array(date('M Y', strtotime($row['data'][0] .' +1 day'))) + $r;
289 }
290 $r = array();
291 $year_month = date('Y-m', strtotime($row['data'][0]));
292 }
293
294 for ($i = 1; $i < $size; $i++) {
295 $r[$i] += $row['data'][$i];
296 }
297 }
298 // Process left over unfinished days
299 if ($r) {
300 $out[] = array(date('M Y', strtotime($row['data'][0]))) + $r;
301 }
302 break;
303
304 case REPORT_PERIOD_YEAR:
305 foreach ($rows as $row) {
306 if ($year != date('Y', strtotime($row['data'][0]))) {
307 if ($r) {
308 $out[] = array(date('Y', strtotime($row['data'][0] .' +1 year'))) + $r;
309 }
310 $r = array();
311 $year = date('Y', strtotime($row['data'][0]));
312 }
313
314 for ($i = 1; $i < $size; $i++) {
315 $r[$i] += $row['data'][$i];
316 }
317 }
318 // Process left over unfinished days
319 if ($r) {
320 $out[] = array(date('Y', strtotime($row['data'][0]))) + $r;
321 }
322 }
323
324 return $out;
325 }
326
327 /**
328 * Replacement for core's pager_query() to handle results queried by week, month, year as well as day.
329 */
330 function report_pager_query($query, $limit = 10, $period = REPORT_PERIOD_DAY, $element = 0, $count_query = NULL) {
331 global $pager_page_array, $pager_total, $pager_total_items;
332 $page = isset($_GET['page']) ? $_GET['page'] : '';
333
334 // Reset to first page if period changes to avoid error
335 // where page # is not available for next period when it was for previous period.
336 if (isset($_POST['period']) && $_POST['period'] != variable_get('report_chart_period', '1')) {
337 $page = '';
338 }
339
340 // Substitute in query arguments.
341 $args = func_get_args();
342 $args = array_slice($args, 5);
343 // Alternative syntax for '...'
344 if (isset($args[0]) && is_array($args[0])) {
345 $args = $args[0];
346 }
347
348 // If REPORT_PERIOD_DAY call core's pager_query since no special treatment is needed.
349 if ($period == REPORT_PERIOD_DAY) {
350 return pager_query($query, $limit, $element, $content_query, $args);
351 }
352
353 // Convert comma-separated $page to an array, used by other functions.
354 $pager_page_array = explode(',', $page);
355
356 $first_day = db_result(db_query(preg_replace(array('/SELECT.*?FROM /As', '/ORDER BY .*/'), array('SELECT date FROM ', 'ORDER BY date LIMIT 1'), $query), $args));
357 $last_day = db_result(db_query(preg_replace(array('/SELECT.*?FROM /As', '/ORDER BY .*/'), array('SELECT date FROM ', 'ORDER BY date DESC LIMIT 1'), $query), $args));
358
359 // Custom paging per day, week, month, year.
360 switch ($period) {
361 case REPORT_PERIOD_WEEK:
362 // Get total number of weeks inclusive.
363 $pager_total_items[$element] = report_date_diff($first_day, $last_day, 'week');
364
365 // Get the monday $limit weeks ago.
366 if ($pager_page_array[$element]) {
367 $date_from = date('Y-m-d', strtotime($last_day. ' -'. (date('N', strtotime($last_day)) - 1) . ' day') - 86400 * 7 * ($limit - 1 + $limit * $pager_page_array[$element]) + 10000); // Add 10,000 to cope with possible error due to daylight saving time.
368 $date_to = date('Y-m-d', strtotime($last_day. ' -'. (date('N', strtotime($last_day)) - 1) . ' day') - 86400 * 7 * ($limit - 1 + $limit * ($pager_page_array[$element] - 1)) - 86400 + 10000); // Sunday
369 }
370 else {
371 $date_from = date('Y-m-d', strtotime($last_day. ' -'. (date('N', strtotime($last_day)) - 1) . ' day') - 86400 * 7 * ($limit - 1) + 10000);
372 $date_to = $last_day;
373 }
374 break;
375
376 case REPORT_PERIOD_MONTH:
377 // Get total number of months inclusive.
378 $pager_total_items[$element] = report_date_diff($first_day, $last_day, 'month');
379
380 if ($pager_page_array[$element]) {
381 $date_from = date('Y-m-d', strtotime(substr($last_day, 0, 8) .'01 -'. ($limit - 1 + $limit * $pager_page_array[$element]) . ' month'));
382 $date_to = date('Y-m-d', strtotime(substr($last_day, 0, 8) .'01 -'. ($limit - 1 + $limit * ($pager_page_array[$element] - 1)) . ' month') - 86400); // Last day of month
383 }
384 else {
385 $date_from = date('Y-m-d', strtotime(substr($last_day, 0, 8) .'01 -'. ($limit - 1) . ' month'));
386 $date_to = $last_day;
387 }
388
389 break;
390
391 case REPORT_PERIOD_YEAR:
392 // Get total number of months inclusive.
393 $pager_total_items[$element] = report_date_diff($first_day, $last_day, 'year');
394
395 if ($pager_page_array[$element]) {
396 $date_from = date('Y-m-d', strtotime(substr($last_day, 0, 5) .'01-01 -'. ($limit - 1 + $limit * $pager_page_array[$element]) . ' year'));
397 $date_to = date('Y-m-d', strtotime(substr($last_day, 0, 5) .'01-01 -'. ($limit - 1 + $limit * ($pager_page_array[$element] - 1)) . ' year') - 86400); // Last day of month
398 }
399 else {
400 $date_from = date('Y-m-d', strtotime(substr($last_day, 0, 5) .'01-01 -'. ($limit - 1) . ' year'));
401 $date_to = $last_day;
402 }
403 }
404
405 $main_query = str_replace('WHERE ', "WHERE (date BETWEEN '$date_from' AND '$date_to') AND ", $query);
406
407 $pager_total[$element] = ceil($pager_total_items[$element] / $limit);
408 $pager_page_array[$element] = max(0, min((int)$pager_page_array[$element], ((int)$pager_total[$element]) - 1));
409
410 return db_query($main_query, $args);
411 }
412
413 /**
414 * Helper function to get the difference between two dates in terms of day, week, month and year.
415 */
416 function report_date_diff($start_date, $end_date, $mode = 'day', $inclusive = TRUE) {
417 // Get Y-m-d part only if there was time part.
418 $start_date = date('Y-m-d', strtotime($start_date));
419 $end_date = date('Y-m-d', strtotime($end_date));
420
421 // Switch if start date is later than end date.
422 if ($start_date > $end_date) {
423 $temp = $start_date;
424 $start_date = $end_date;
425 $end_date = $temp;
426 }
427
428 switch (strtolower($mode)) {
429 case 'd':
430 case 'day':
431 return round((strtotime($end_date) - strtotime($start_date)) / 86400) + (int)$inclusive; // Round for non-integer due to daylight saving time.
432
433 case 'w':
434 case 'week':
435 // Monday as the first day of the week
436 $monday1 = date('Y-m-d', strtotime($start_date. ' -'. (date('N', strtotime($start_date)) - 1) . ' day'));
437 $monday2 = date('Y-m-d', strtotime($end_date. ' -'. (date('N', strtotime($end_date)) - 1) . ' day'));
438 return round((strtotime($monday2) - strtotime($monday1)) / (86400 * 7)) + (int)$inclusive;
439
440 case 'm':
441 case 'month':
442 return (substr($end_date, 0, 4) - substr($start_date, 0, 4)) * 12 + (substr($end_date, 5, 2) - substr($start_date, 5, 2)) + (int)$inclusive;
443
444 case 'y':
445 case 'year':
446 return substr($end_date, 0, 4) - substr($start_date, 0, 4) + (int)$inclusive;
447 }
448 }
449
450 function report_period_name($period) {
451 switch ($period) {
452 case REPORT_PERIOD_DAY: return t('Date');
453 case REPORT_PERIOD_WEEK: return t('Week');
454 case REPORT_PERIOD_MONTH: return t('Month');
455 case REPORT_PERIOD_YEAR: return t('Year');
456 default: return NULL;
457 }
458 }
459
460 function report_use_chart() {
461 return variable_get('report_use_chart', 1) && module_exists('chart');
462 }
463
464 function report_time_for_cron() {
465 return time() >= strtotime(variable_get('report_cron_time', '1:00am'));
466 }
467
468 /**
469 * Implementation of hook_cron().
470 */
471 function report_cron() {
472 $report_last_date = variable_get('report_last_date', '');
473
474 // It should run only the very first time when 'report_last_date' is not set yet.
475 if (empty($report_last_date)) {
476 $report_last_date = db_result(db_query('SELECT MAX(date) FROM {report}'));
477 }
478
479 $date_for_report = date('Y-m-d', strtotime(date('Y-m-d', time()) .' -1 day')); // Yesterday
480
481 if ($date_for_report > $report_last_date && report_time_for_cron()) {
482 $date = date('Y-m-d', strtotime($report_last_date .' +1 day'));
483
484 // If cron ran daily successfully, the loop should run only once for the day before.
485 // Otherwise it will iterate until it fills data between last cron date and the day before.
486 while ($date <= $date_for_report) {
487 module_invoke_all('report_cron', $date);
488
489 $date = date('Y-m-d', strtotime($date .' +1 day'));
490 }
491
492 watchdog('report', 'Report cron ran successfully.');
493 variable_set('report_last_date', $date_for_report);
494 }
495 }

  ViewVC Help
Powered by ViewVC 1.1.2