source: meter/meter.module @ 92

Revision 92, 15.1 KB checked in by nigel, 5 years ago (diff)

only show meter methods on meters

Line 
1<?php
2// $Id: meter.module,v 1.195 2006/04/14 09:05:19 killes Exp $
3
4/**
5 * @file
6 * Enables your site to capture votes on different topics in the form of multiple
7 * choice questions.
8 */
9
10/**
11CREATE TABLE `meter_readings` (
12`nid` INT( 10 ) NOT NULL ,
13`time` DATETIME NOT NULL ,
14`value` DECIMAL NOT NULL,
15`comment` VARCHAR( 255 )
16) ENGINE = MYISAM
17*/
18
19/**
20 * Implementation of hook_help().
21 */
22function meter_help($section) {
23  switch ($section) {
24    case 'admin/help#meter':
25      $output = '<p>'. t('Meters help keep track of our usages of resources.').'</p>';
26      return $output;
27    case 'admin/modules#description':
28      return t("Allows users to track their household usages.");
29    case 'node/add#meter':
30      return t("Add a new meter to monitor.");
31  }
32}
33
34/**
35 * Implementation of hook_access().
36 */
37function meter_access($op, $node) {
38  if ($op == 'create') {
39    return user_access('create meter');
40  }
41}
42
43/**
44 * Implementation of hook_block().
45 *
46 * Generates a block containing the latest poll.
47 */
48function meter_block($op = 'list', $delta = 0) {
49  if (user_access('access content')) {
50    if ($op == 'list') {
51      $blocks[0]['info'] = t('Global usage');
52      $blocks[1]['info'] = t('My usage');
53      return $blocks;
54    }
55    else if ($op == 'view') {
56      // Retrieve the latest poll.
57      $sql = db_rewrite_sql("SELECT MAX(n.created) FROM {node} n INNER JOIN {poll} p ON p.nid = n.nid WHERE n.status = 1 AND p.active = 1 AND n.moderate = 0");
58      $timestamp = db_result(db_query($sql));
59      if ($timestamp) {
60        $poll = node_load(array('type' => 'poll', 'created' => $timestamp, 'moderate' => 0, 'status' => 1));
61
62        if ($poll->nid) {
63          // meter_view() dumps the output into $poll->body.
64          meter_view($poll, 1, 0, 1);
65        }
66      }
67      $block['subject'] = t('Poll');
68      $block['content'] = $poll->body;
69      return $block;
70    }
71  }
72}
73
74/**
75 * Implementation of hook_delete().
76 */
77function meter_delete($node) {
78  db_query("DELETE FROM {meter_readings} WHERE nid = %d", $node->nid);
79}
80
81/**
82 * Implementation of hook_submit().
83 */
84function meter_submit(&$node) {
85  // Renumber fields
86  /*
87  $node->choice = array_values($node->choice);
88  $node->teaser = meter_teaser($node);
89  */
90}
91
92/**
93 * Implementation of hook_validate().
94 */
95function meter_validate($node) {
96/*
97  if (isset($node->title)) {
98    // Check for at least two options and validate amount of votes:
99    $realchoices = 0;
100    // Renumber fields
101    $node->choice = array_values($node->choice);
102    foreach ($node->choice as $i => $choice) {
103      if ($choice['chtext'] != '') {
104        $realchoices++;
105      }
106      if ($choice['chvotes'] < 0) {
107        form_set_error("choice][$i][chvotes", t('Negative values are not allowed.'));
108      }
109    }
110
111    if ($realchoices < 2) {
112      form_set_error("choice][$realchoices][chtext", t('You must fill in at least two choices.'));
113    }
114  }
115  */
116}
117
118/**
119 * Implementation of hook_form().
120 */
121function meter_form(&$node) {
122 
123  $admin = user_access('administer nodes');
124
125  $form['title'] = array('#type' => 'textfield', '#title' => t('Meter name'), '#required' => TRUE, '#default_value' => $node->title, '#weight' => -5);
126
127/*  if ($admin) {
128    $form['settings'] = array('#type' => 'fieldset', '#title' => t('Settings'));
129    $form['settings']['active'] = array('#type' => 'radios', '#title' => t('Poll status'), '#default_value' => isset($node->active) ? $node->active : 1, '#options' => $_active, '#description' => t('When a poll is closed, visitors can no longer vote for it.'));
130  }
131  $form['settings']['runtime'] = array('#type' => 'select', '#title' => t('Poll duration'), '#default_value' => $node->runtime ? $node->runtime : 0, '#options' => $_duration, '#description' => t('After this period, the poll will be closed automatically.'));
132*/
133  return $form;
134}
135
136function meter_insert($node) {
137  /*
138  if (!user_access('administer nodes')) {
139    // Make sure all votes are 0 initially
140    foreach ($node->choice as $i => $choice) {
141      $node->choice[$i]['chvotes'] = 0;
142    }
143    $node->active = 1;
144  }
145
146  db_query("INSERT INTO {poll} (nid, runtime, active) VALUES (%d, %d, %d)", $node->nid, $node->runtime, $node->active);
147
148  foreach ($node->choice as $choice) {
149    if ($choice['chtext'] != '') {
150      db_query("INSERT INTO {meter_choices} (nid, chtext, chvotes, chorder) VALUES (%d, '%s', %d, %d)", $node->nid, $choice['chtext'], $choice['chvotes'], $i++);
151    }
152  }
153  */
154}
155
156/**
157 * Implementation of hook_menu().
158 */
159function meter_menu($may_cache) {
160  $items = array();
161
162  if ($may_cache) {
163    $items[] = array('path' => 'node/add/meter', 'title' => t('meter'),
164      'access' => user_access('create meter'));
165    $items[] = array('path' => 'meter', 'title' => t('my meters'),
166      'callback' => 'meter_page',
167      'access' => user_access('access content'),
168      'type' => MENU_SUGGESTED_ITEM);
169
170  }
171  else {
172    if (arg(0) == 'node' && is_numeric(arg(1))) {
173      $node = node_load(arg(1));
174      if ($node->type =='meter'){
175      $items[] = array('path' => 'node/'. arg(1) .'/add_reading',
176          'title' => t('add reading'),
177          'callback' => 'meter_add_reading',
178          'access' => user_access('access content'),
179          'weight' => 3,
180          'type' => MENU_CALLBACK);
181      $items[] = array('path' => 'node/'. arg(1) .'/csv_readings',
182          'title' => t('readings'),
183          'callback' => 'meter_csv_readings',
184          'access' => user_access('access content'),
185          'weight' => 3,
186          'type' => MENU_LOCAL_TASK);
187      $items[] = array('path' => 'node/'. arg(1) .'/edit_readings',
188          'title' => t('edit readings'),
189          'callback' => 'meter_edit_readings',
190          'access' => user_access('access content'),
191          'weight' => 3,
192          'type' => MENU_LOCAL_TASK);
193      $items[] = array('path' => 'node/'. arg(1) .'/edit_reading',
194          'title' => t('edit reading'),
195          'callback' => 'meter_edit_reading',
196          'access' => user_access('access content'),
197          'weight' => 3,
198          'type' => MENU_CALLBACK);
199      }
200    }
201  }
202
203  return $items;
204}
205
206/**
207 * Implementation of hook_load().
208 */
209function meter_load($node) {
210  global $user;
211  // Load the appropriate choices into the $node object
212  $poll = db_fetch_object(db_query("SELECT * FROM {node} WHERE nid = %d", $node->nid));
213
214  return $poll;
215 
216}
217
218/**
219 * Implementation of hook_node_info().
220 */
221function meter_node_info() {
222  return array('meter' => array('name' => t("meter"), 'base' => 'meter'));
223}
224
225function meter_page() {
226  // List all of my meters by category
227  $sql = "SELECT n.nid, n.title FROM {node} n WHERE n.type='meter'";
228  $sql = db_rewrite_sql($sql);
229  $result = db_query($sql);
230  $output = '<ul>';
231  while ($node = db_fetch_object($result)) {
232    $output .= '<li>'. l($node->title, "node/$node->nid") .'</li>';
233  }
234  $output .= '</ul>';
235  $output .= theme("pager", NULL, 15);
236  return $output;
237}
238
239/**
240 * Implementation of hook_perm().
241 */
242function meter_perm() {
243  return array('create meter');
244}
245
246/**
247 * Creates a simple teaser that lists all the choices.
248 */
249function meter_teaser($node) {
250  $teaser = NULL;
251  if (is_array($node->choice)) {
252    foreach ($node->choice as $k => $choice) {
253      $teaser .= '* '. $choice['chtext'] .'\n';
254    }
255  }
256  return $teaser;
257}
258
259/**
260 * Callback for processing a vote
261 */
262function meter_add_reading() {
263  global $user;
264  $nid = arg(1);
265
266  $node = node_load($nid);
267
268  $edit = $_POST['edit'];
269  $date = $edit['date'];
270  $value = $edit['value'];
271  $comment = $edit['comment'];
272
273
274  if ($node->uid == $user->uid) {
275    db_query('INSERT INTO {meter_readings} (nid, time, value, comment) VALUES (%d, "%s", %d, "%s")', $node->nid, $date, $value, $comment);
276    drupal_set_message(t('Your reading was recorded.'));
277  }
278  else {
279    drupal_set_message(t("You're not allowed to add reading for this meter."), 'error');
280  }
281
282  drupal_goto('node/'. $nid);
283}
284
285/**
286 * Implementation of hook_view().
287 *
288 * @param $block
289 *   An extra parameter that adapts the hook to display a block-ready
290 *   rendering of the poll.
291 */
292function meter_view(&$node, $teaser = FALSE, $page = FALSE, $block = FALSE) {
293  global $user;
294
295  $output = '';
296
297  $links = array();
298  $links[] = l(t('edit readings'), 'node/'.$node->nid.'/edit_readings',array('title'=>'Edit meter readings'));
299  $links[] = l(t('export readings'), 'node/'.$node->nid.'/csv_readings',array('title'=>'Export the meter readings as CSV'));
300
301  $form['nid'] = array('#type' => 'hidden', '#value' => $node->nid);
302  $form['date'] = array(
303            '#title' => t('Date'),
304            '#type' => 'textfield',
305            '#default_value' => date("Y-m-d H:i"),
306            '#attributes' => array('class' => 'jscalendar'),
307            '#jscalendar_ifFormat' => '%Y-%m-%d %H:%M',
308            '#jscalendar_showsTime' => 'true',
309            '#jscalendar_timeFormat' => variable_get('event_ampm', '0') == 0 ? '24' : '12',
310            '#size' => 19,
311            '#maxlength' => 19,
312            '#weight' => -14,
313            '#description' => t('YYYY-MM-DD HH:MM'),
314            );
315
316  $form['value'] = array(
317        '#title' => t('Value'),
318        '#type' => 'textfield');
319
320  $form['comment'] = array(
321        '#title' => t('Comment'),
322        '#type' => 'textfield');
323
324  $form['add_reading'] = array('#type' => 'submit', '#value' => t('Add reading'));
325  $form['#action'] = url('node/'. $node->nid.'/add_reading');
326
327  $res = meter_extrapolate(meter_get_values($node), 1,30);
328
329  $output .= meter_graph($res);
330
331  $node->links = $links;
332  // Only show the add readings form when in full view
333  $node->teaser = $output;
334  $node->body = $output.drupal_get_form('meter_add_reading', $form);
335}
336
337/**
338  *
339  */
340function meter_edit_readings() {
341  $nid = arg(1);
342  $node = node_load($nid);
343
344  if ($_GET['action'] == 'delete'){
345    db_query("DELETE FROM {meter_readings} WHERE nid=%d AND time='%s';", $nid, $_GET['time']);
346    drupal_set_message(t('Reading removed.'));
347  }
348
349  $headers = array(
350    array('data'=>t('Date')),
351    array('data'=>t('Reading')),
352    array('data'=>t('Comment')),
353    array('data'=>t('Edit')),
354    array('data'=>t('Delete')),
355    );
356
357  $rows = array();
358  $rows_ = meter_get_values($node);
359  foreach ($rows_ as $r){
360    $rows[] = array(
361        $r->time,
362        $r->value,
363        $r->comment,
364        l(t('edit'), "node/$nid/edit_reading", array(), 'time='.urlencode($r->time)),
365        l(t('delete'), "node/$nid/edit_readings", array(), 'action=delete&time='.urlencode($r->time)),
366        );
367  }
368
369  $output .= theme("table", $headers, $rows);
370  return $output;
371}
372
373/**
374  * Edit the reading
375  */
376function meter_edit_reading() {
377  $nid = arg(1);
378  $node = node_load($nid);
379  $edit = $_REQUEST['edit'];
380
381  if ($_REQUEST['op'] == t('Save')){
382    db_query("UPDATE {meter_readings} SET time='%s', value=%d, comment='%s' WHERE nid=%d AND time='%s';", $edit['time'], $edit['value'], $edit['comment'], $edit['nid'], $edit['orig_time']);
383    drupal_set_message("Saved reading.");
384    drupal_goto("node/$nid/edit_readings");
385  } else if ($_REQUEST['op'] == t('Cancel')){
386    drupal_set_message("Cancelled editing.");
387    drupal_goto("node/$nid/edit_readings");
388  }
389
390  $r = db_query("SELECT * FROM {meter_readings} WHERE nid=%d AND time='%s';", $nid, $_GET['time']);
391  $r = db_fetch_object($r);
392
393  $form['nid'] = array('#type' => 'hidden', '#value' => $node->nid);
394  $form['orig_time'] = array('#type' => 'hidden', '#value' => $r->time);
395  $form['time'] = array(
396            '#title' => t('Date'),
397            '#type' => 'textfield',
398            '#default_value' => $r->time,
399            '#attributes' => array('class' => 'jscalendar'),
400            '#jscalendar_ifFormat' => '%Y-%m-%d %H:%M',
401            '#jscalendar_showsTime' => 'true',
402            '#jscalendar_timeFormat' => variable_get('event_ampm', '0') == 0 ? '24' : '12',
403            '#size' => 19,
404            '#maxlength' => 19,
405            '#weight' => -14,
406            '#description' => t('YYYY-MM-DD HH:MM'),
407            );
408
409  $form['value'] = array(
410        '#title' => t('Value'),
411        '#type' => 'textfield',
412        '#default_value' => $r->value);
413
414  $form['comment'] = array(
415        '#title' => t('Comment'),
416        '#type' => 'textfield',
417        '#default_value' => $r->comment);
418
419  $form['action_save'] = array('#type' => 'submit', '#value' => t('Save'));
420  $form['action_cancel'] = array('#type' => 'submit', '#value' => t('Cancel'));
421  $form['#action'] = url('node/'. $node->nid.'/edit_reading');
422
423  return drupal_get_form('meter_edit_reading', $form);
424}
425
426
427/**
428  * Export the meter readings as a CSV file
429  */
430function meter_csv_readings() {
431  $nid = arg(1);
432  $node = node_load($nid);
433  $results = meter_get_values($node);
434  drupal_set_header('Content-Type: text/csv; charset=utf-8');
435  drupal_set_header('Content-Disposition: attachment; filename="meter.csv";');
436  print "Time, Value\n";
437  foreach($results as $row){
438    print '"'.$row->time.'",'.$row->value."\n";
439  }
440}
441
442/**
443  * Query the DB for the reading values
444  */
445function meter_get_values($node){
446  $res = array();
447  $results = db_query("SELECT time, value, comment FROM {meter_readings} WHERE nid = %d ORDER BY time DESC", $node->nid);
448
449  while ($row = db_fetch_object($results)){
450    $res[] = $row;
451  }
452  return $res;
453}
454
455/**
456 * Implementation of hook_update().
457 */
458function meter_update($node) {
459  // Do nothing
460}
461
462/**
463 * Implementation of hook_user().
464 */
465function meter_user($op, &$edit, &$user) {
466  if ($op == 'delete') {
467    db_query('UPDATE {meter_readings} SET uid = 0 WHERE uid = %d', $user->uid);
468  }
469}
470
471/**
472  * Average usage over $days, from midday before the last reading
473  * @param values - object with date and value
474  * @param days - the number of days to average over
475  * @param count - the number of entries to calculate
476  */
477function meter_extrapolate($values, $days, $count){
478  $return = array();
479
480  $last = reset($values);
481  $row = $last;
482  $target = strtotime($row->time); // Set the first target
483  $value = $row->value; // We know what the first reading is!
484
485  for ($i = 0 ; $i < $count ; $i++){
486    // Increment target, and store old value
487    $oldValue = $value;
488    $oldTarget = $target;
489    $target -= $days*60*60*24;
490
491    while ( !($row === false) && strtotime($row->time) > $target){
492      $last = $row;
493      $row = next($values);
494    }
495    if ($row === false) {
496      break;
497    } else {
498      $timeBetween = (strtotime( $last->time ) - strtotime( $row->time ));
499      $timeToTarget = $target - strtotime( $row->time );
500      $value = $row->value + (($last->value - $row->value)/$timeBetween*$timeToTarget);
501      $entry = new StdClass();
502      $entry->from = date("d/m/Y H:i", $target);
503      $entry->to = date("d/m/Y H:i", $oldTarget);
504      $entry->value = $oldValue - $value;
505      $return[$i] = $entry;
506    }
507  }
508
509  return $return;
510}
511
512
513/**
514  * Draw a CSS/HTML graph
515  */
516function meter_graph($entries){
517  if (count($entries) == 0)
518    return "No entries";
519  $height = 200;
520  $width = 500;
521  $output = '<div style="background: white; border: 1px solid; position: relative; width: '.$width.'px; height:'.$height.'px">';
522
523  $w = floor($width/count($entries));
524  $offset = ($width-($w*count($entries)))/2;
525  $i = 0;
526  $max = 20;
527  foreach ($entries as $e){
528    $output .= "<div title='".$e->from." to ".$e->to." value: ".sprintf("%1.2f",$e->value)."' style='width:${w}px; height:".floor($e->value/20*$height)."px; bottom: 0px; position: absolute; right:".($i*$w+$offset)."px; background: grey; border: solid 1px;'>".round($e->value)."</div>\n";
529    $i++;
530  }
531  $output .= '</div>';
532  return $output;
533}
Note: See TracBrowser for help on using the repository browser.