/[drupal]/contributions/modules/metrics/metrics.module
ViewVC logotype

Contents of /contributions/modules/metrics/metrics.module

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


Revision 1.5 - (show annotations) (download) (as text)
Mon Aug 20 18:58:56 2007 UTC (2 years, 3 months ago) by drewish
Branch: MAIN
CVS Tags: DRUPAL-5--0-0-SOC_FINAL, HEAD
Changes since 1.4: +2 -6 lines
File MIME type: text/x-php
Removing some commented out code and commenting out the cron debug message.
1 <?php
2 // $Id: metrics.module,v 1.4 2007/08/20 18:52:19 drewish Exp $
3
4 /**
5 * Implementation of hook_help().
6 */
7 function metrics_help($section) {
8 switch ($section) {
9 case 'admin/help#metrics':
10 return t("<p>Overview of the parts of Metrics.</p>
11 <dl>
12 <dt>Metric</dt>
13 <dt>A metric is a function that takes a node and computes a numeric score and an explanatory string.</dt>
14 <dt>Ranking</dt>
15 <dt>A ranking has a name, a numeric threshold, and a description. The ranking's purpose is to help a user interpret a property's score. Is a score of 30 is good or bad?</dt>
16 <dt>Property</dt>
17 <dt>A property has a name and a description to explain what it measures. The property is made up of metrics and rankings. Each metric within the property can have options and a multiplier. The multiplier is used to adjust the importance of each metric in the overall score.</dt>
18 <dt>Property result</dt>
19 <dt>When a property is computed for a node, each metrics is evaluated, the numeric value multiplied by the multiplier then added to the total, and the explanatory strings concatenated. This is stored in the database and displayed in a block on the node's page.</dt>
20 </dl>
21 <p>The property results are computed during a cron run by first looking for missing results and then updating the most out of date. The administrator can determine how frequently the properties are updated and how many are updated in a single cron run.</p>
22 ");
23 }
24 }
25
26 /**
27 * Implementation of hook_menu().
28 */
29 function metrics_menu($may_cache) {
30 $items = array();
31
32 if ($may_cache) {
33 $items[] = array(
34 'path' => 'admin/content/metrics',
35 'title' => t('Metrics'),
36 'callback' => 'metrics_admin_overview',
37 'description' => t('Configure the settings used to rate node quality.'),
38 );
39 $items[] = array(
40 'path' => 'admin/content/metrics/list',
41 'title' => t('List'),
42 'type' => MENU_DEFAULT_LOCAL_TASK,
43 'weight' => '-10',
44 );
45 $items[] = array(
46 'path' => 'admin/content/metrics/add',
47 'callback' => 'drupal_get_form',
48 'callback arguments' => array('metrics_property_form'),
49 'title' => t('Add'),
50 'type' => MENU_LOCAL_TASK,
51 );
52 $items[] = array(
53 'path' => 'admin/settings/metrics',
54 'title' => t('Metrics'),
55 'callback' => 'drupal_get_form',
56 'callback arguments' => array('metrics_settings_form'),
57 'description' => t('Configure the settings used to rate node quality.'),
58 );
59 }
60 else {
61 if (arg(0) == 'admin' && arg(1) == 'content' && arg(2) == 'metrics' && is_numeric(arg(3))) {
62 $property_id = (int) arg(3);
63 if ($property = metrics_get_property($property_id)) {
64
65 $items[] = array(
66 'path' => 'admin/content/metrics/'. $property_id,
67 'title' => t('Property'),
68 'callback' => 'metrics_property_overview',
69 'callback arguments' => array($property_id),
70 'type' => MENU_CALLBACK,
71 );
72 $items[] = array(
73 'path' => 'admin/content/metrics/'. $property_id .'/view',
74 'title' => t('View'),
75 'type' => MENU_DEFAULT_LOCAL_TASK,
76 'weight' => -10,
77 );
78 $items[] = array(
79 'path' => 'admin/content/metrics/'. $property_id .'/edit',
80 'title' => t('Edit'),
81 'callback' => 'metrics_property_edit',
82 'callback arguments' => array($property_id),
83 'type' => MENU_LOCAL_TASK,
84 );
85 $items[] = array(
86 'path' => 'admin/content/metrics/'. $property_id .'/metrics',
87 'title' => t('Metrics'),
88 'callback' => 'drupal_get_form',
89 'callback arguments' => array('metrics_metrics_form', $property),
90 'type' => MENU_LOCAL_TASK,
91 );
92 $items[] = array(
93 'path' => 'admin/content/metrics/'. $property_id .'/rankings',
94 'title' => t('Rankings'),
95 'callback' => 'drupal_get_form',
96 'callback arguments' => array('metrics_rankings_form', $property),
97 'type' => MENU_LOCAL_TASK,
98 );
99 $items[] = array(
100 'path' => 'admin/content/metrics/'. $property_id .'/delete',
101 'title' => t('Delete'),
102 'callback' => 'drupal_get_form',
103 'callback arguments' => array('metrics_metric_delete_form', $property_id),
104 'type' => MENU_CALLBACK,
105 );
106 }
107 }
108 }
109
110 return $items;
111 }
112
113
114
115 /**
116 * Implementation of hook_block().
117 */
118 function metrics_block($op = 'list', $delta = 0, $edit = array()) {
119 switch ($op) {
120 case 'list':
121 $blocks[0] = array(
122 'info' => t('Project Metrics'),
123 'status' => TRUE,
124 'weight' => 0,
125 'visibility' => 1,
126 'pages' => 'node/*',
127 );
128 return $blocks;
129
130 case 'configure':
131 return $form;
132
133 case 'view':
134 default:
135 switch ($delta) {
136 case 0:
137 if ($output = metrics_block_display((int) arg(1))) {
138 return array(
139 'subject' => t('Project metrics...'),
140 'content' => $output,
141 );
142 }
143 break;
144
145 default:
146 return array();
147 }
148 }
149 }
150
151
152 function metrics_block_display($nid) {
153 if ($node = node_load($nid)) {
154 $items = array();
155 foreach ((array) $node->metrics as $property_id => $result) {
156 $items[] = t('@metric<br /><strong>Score: @score</strong><br />@description', array('@metric' => $result['name'], '@score' => $result['value'], '@description' => $result['description']));
157 }
158 if ($items) {
159 return theme('item_list', $items);
160 }
161 }
162 }
163
164 /**
165 * Implementation of hook_cron().
166 */
167 function metrics_cron() {
168 $limit = variable_get('metrics_cron_limit', 50);
169 $timestamp = time() - variable_get('metrics_recompute_after', 604800);
170
171 // Locate nodes without {metrics_property_result} records.
172 $result = db_query_range('SELECT mpnt.property_id, n.nid FROM {metrics_property_node_types} mpnt INNER JOIN {node} n ON mpnt.type = n.type AND n.status = 1 WHERE NOT EXISTS
173 ( SELECT * FROM {metrics_property_result} mpr WHERE mpr.property_id = mpnt.property_id AND mpr.nid = n.nid )', 0, $limit);
174 while ($row = db_fetch_object($result)) {
175 if ($node = node_load($row->nid)) {
176 metrics_compute_property($row->property_id, $node);
177 }
178 }
179
180 // Count the preceding rows against the limit.
181 $limit -= db_num_rows($result);
182 if ($limit < 1) {
183 return;
184 }
185
186 // Update stale records.
187 $result = db_query_range('SELECT mpr.nid, mpr.property_id FROM {metrics_property_result} mpr INNER JOIN {node} n ON mpr.nid = n.nid WHERE mpr.timestamp < %d AND n.status = 1', $timestamp, 0, $limit);
188 while ($row = db_fetch_object($result)) {
189 if ($node = node_load($row->nid)) {
190 metrics_compute_property($row->property_id, $node);
191 }
192 }
193 }
194
195
196 /**
197 * Implementation of hook_nodeapi().
198 */
199 function metrics_nodeapi(&$node, $op, $teaser) {
200 switch ($op) {
201 case 'load':
202 if ($result = db_query('SELECT mp.property_id, mp.name, mpr.value, mpr.description FROM {metrics_property} mp INNER JOIN {metrics_property_result} mpr ON mp.property_id = mpr.property_id WHERE mpr.nid = %d', $node->nid)) {
203 $output = array();
204 while ($o = db_fetch_array($result)) {
205 $output['metrics'][$o['property_id']] = $o;
206 }
207 return $output;
208 }
209 break;
210
211 case 'insert':
212 case 'update':
213 // Mark all of our results as stale so they'll be re-computed.
214 db_query("UPDATE {metrics_property_result} SET timestamp = 0 WHERE nid = %d", $node->nid);
215 break;
216
217 case 'delete':
218 db_query('DELETE FROM {metrics_property_result} WHERE nid = %d', $node->nid);
219 break;
220 }
221 }
222
223
224
225
226
227 /**
228 * Return an array of all metric objects.
229 *
230 * TODO: this should take node type as a parameter.
231 */
232 function metrics_get_properties() {
233 $properties = array();
234
235 // Ensure that the .inc files are loaded.
236 _metrics_get_metrics_functions();
237
238 $result = db_query('SELECT * FROM {metrics_property} mp ORDER BY mp.name');
239 while ($property = db_fetch_array($result)) {
240 $property['node_types'] = array();
241 $sub_result = db_query('SELECT type FROM {metrics_property_node_types} mpnt WHERE property_id = %d', $property['property_id']);
242 while ($type = db_fetch_object($sub_result)) {
243 $property['node_types'][$type->type] = $type->type;
244 }
245
246 $property['metrics'] = array();
247 $sub_result = db_query('SELECT metric_id, function_name, options, displayed, multiplier FROM {metrics_metric} mm WHERE property_id = %d ORDER BY function_name', $property['property_id']);
248 while ($metric = db_fetch_array($sub_result)) {
249 if (isset($metric['options']) && $options = unserialize($metric['options'])) {
250 $metric['options'] = $options;
251 }
252 else {
253 $metric['options'] = array();
254 }
255 $property['metrics'][$metric['metric_id']] = $metric;
256 }
257
258 $property['rankings'] = array();
259 $sub_result = db_query('SELECT ranking_id, name, threshold, description FROM {metrics_ranking} mr WHERE property_id = %d ORDER BY threshold DESC', $property['property_id']);
260 while ($ranking = db_fetch_array($sub_result)) {
261 $property['rankings'][$ranking['ranking_id']] = $ranking;
262 }
263
264 $properties[$property['property_id']] = $property;
265 }
266
267 return $properties;
268 }
269
270 /**
271 * Return the property with a given ID.
272 *
273 * @param $property_id
274 * The metric's ID
275 *
276 * @return Object
277 * The metric object with all of its metadata.
278 * Results are statically cached.
279 */
280 function metrics_get_property($property_id) {
281 static $properties = array();
282
283 if (!array_key_exists($property_id, $properties)) {
284 $properties = metrics_get_properties();
285 }
286
287 return $properties[$property_id];
288 }
289
290 /**
291 * Recompute the
292 *
293 * @param $property_id
294 * Integer property ID.
295 * @param $node
296 * Node object.
297 * @return
298 * The numeric result of the property, or FALSE on error.
299 */
300 function metrics_compute_property($property_id, $node) {
301 if ($property = metrics_get_property($property_id)) {
302 // Go ahead and delete first because if we can't compute it there shouldn't
303 // be a row in the database for it.
304 db_query('DELETE FROM {metrics_property_result} WHERE property_id = %d AND nid = %d', $property_id, $node->nid);
305
306 if (isset($property['node_types'][$node->type])) {
307 $total = 0.0;
308 $description = '';
309 foreach ($property['metrics'] as $metric_id => $metric) {
310 if ($result = metrics_call_metric($metric, 'compute', $node)) {
311 // Add up the numeric part.
312 $total += $result['value'] * $metric['multiplier'];
313 // If the metric is displayed include the string part.
314 $description .= $metric['displayed'] ? ($result['description'] . ' ') : '';
315 }
316 }
317
318 // Determine which ranking this value falls into. We assume that
319 // metrics_get_property() returns the rankings sorted by descending
320 // threshold value.
321 $last_ranking_id = NULL;
322 foreach ($property['rankings'] as $ranking_id => $ranking) {
323 if ($ranking['threshold'] < $total) {
324 break;
325 }
326 }
327
328 db_query("INSERT INTO {metrics_property_result} (property_id, nid, ranking_id, value, description, timestamp) VALUES (%d, %d, %d, %d, '%s', %d)", $property_id, $node->nid, $ranking_id, $total, $description, time());
329 # drupal_set_message(t("%property metrics was computed for %node (@score): @description", array('%property' => $property['name'], '%node' => $node->title, '@score' => $total, '@description' => $description)));
330 return $total;
331 }
332 }
333 return FALSE;
334 }
335
336 /**
337 * Return an array of the metrics computation functions.
338 *
339 * @param $refresh
340 * Boolean indicating if the list should be fully recomputed.
341 * @return
342 * An array of function names.
343 */
344 function _metrics_get_metrics_functions($refresh = FALSE) {
345 static $_functions;
346
347 if ($refresh || !isset($_functions)) {
348 $_functions = array();
349
350 // Load our .inc files that provide metrics for other projects.
351 $path = drupal_get_path('module', 'metrics') .'/modules';
352 $files = drupal_system_listing('.inc$', $path, 'name', 0);
353 foreach ($files as $file) {
354 // Only load the file if a module of that same name exists.
355 if (module_exists($file->name)) {
356 require_once('./' . $file->filename);
357 $function = 'metrics_'. $file->name . '_metrics_functions';
358 $result = $function();
359 if (isset($result) && is_array($result)) {
360 $_functions = array_merge($_functions, $result);
361 }
362 }
363 }
364
365 // Invoke the hook and return the results.
366 $_functions = array_merge($_functions, module_invoke_all('metrics_functions'));
367
368 // Only store functions that exist.
369 $_functions = array_filter($_functions, 'function_exists');
370 }
371
372 return $_functions;
373 }
374
375 /**
376 * Helper function to call metrics. This is used rather than
377 * $metric['function_name']() because it makes it easier to locate calls in
378 * code.
379 *
380 * @param $metric
381 * Metric array.
382 * @param $op
383 * String indicating the operation to call. Possible values are:
384 * - 'info' returns an array with a name and description.
385 * - 'compute' returns an array with a numeric and string value.
386 * - 'options' returns an array of form elements defining the metric's
387 * options.
388 * @return
389 * Mixed depending on the $op or FALSE if the function doesn't exist.
390 */
391 function metrics_call_metric($metric, $op, $node = NULL) {
392 if (function_exists($metric['function_name'])) {
393 return $metric['function_name']($op, $metric['options'], $node);
394 }
395 return FALSE;
396 }
397
398 /**
399 * Module settings form.
400 */
401 function metrics_settings_form() {
402 $times = array(6048000, 5443200, 4838400, 4233600, 3628800, 3024000, 2419200, 1814400, 1209600, 604800, 518400, 432000, 345600, 259200, 172800, 86400);
403 $ageoptions = drupal_map_assoc($times, 'format_interval');
404 $form['metrics_recompute_after'] = array(
405 '#type' => 'select',
406 '#title' => t('Update after'),
407 '#default_value' => variable_get('metrics_recompute_after', 604800),
408 '#options' => $ageoptions,
409 '#description' => t("Once the metrics are older than this, they will be updated during a cron run."),
410 );
411
412 $limit = drupal_map_assoc(array(5, 10, 15, 20, 25, 50, 100, 200, 300, 500));
413 $form['metrics_cron_limit'] = array(
414 '#type' => 'select',
415 '#title' => t('Cron limit'),
416 '#default_value' => variable_get('metrics_cron_limit', 50),
417 '#options' => $limit,
418 '#description' => t("The number of nodes to recompute each cron run."),
419 );
420
421 return system_settings_form($form);
422 }
423
424
425 /**
426 * Project metrics overview settings, display a list of metrics.
427 */
428 function metrics_admin_overview() {
429 $properties = metrics_get_properties();
430 $node_types = node_get_types('names');
431
432 $rows = array();
433 foreach ($properties as $property_id => $property) {
434 $types = array();
435 foreach ($property['node_types'] as $type) {
436 if (isset($node_types[$type])) {
437 $types[] = $node_types[$type];
438 }
439 }
440 $rows[] = array(
441 'name' => l(check_plain($property['name']), "admin/content/metrics/$property_id"),
442 'type' => implode(', ', $types),
443 'edit' => l(t('edit metric'), "admin/content/metrics/$property_id/edit"),
444 );
445 }
446 if (empty($rows)) {
447 $rows[] = array(array('data' => t('No Properties exist.'), 'colspan' => '3'));
448 }
449 $header = array(t('Name'), t('Type'), t('Operations'));
450
451 return theme('table', $header, $rows);
452 }
453
454
455 function metrics_property_overview($property_id) {
456 $property = metrics_get_property($property_id);
457 if (!$property) {
458 return drupal_not_found();
459 }
460
461 drupal_set_title($property['name']);
462
463 $content['name'] = array(
464 '#type' => 'item',
465 '#title' => t('Property name'),
466 '#value' => check_plain($property['name']),
467 );
468 $content['description'] = array(
469 '#type' => 'item',
470 '#title' => t('Description'),
471 '#value' => check_plain($property['description']),
472 );
473 $types = array();
474 $node_types = node_get_types('names');
475 foreach ($property['node_types'] as $type) {
476 if (isset($node_types[$type])) {
477 $types[] = $node_types[$type];
478 }
479 }
480 $content['node_types'] = array(
481 '#type' => 'item',
482 '#title' => t('Node types'),
483 '#value' => check_plain(implode(', ', $types)),
484 );
485
486 $rows = array();
487 foreach ((array) $property['metrics'] as $metric_id => $metric) {
488 $info = metrics_call_metric($metric, 'info');
489 $rows[] = array(
490 $info['name'],
491 $metric['multiplier'],
492 $metric['displayed'] ? t('Yes') : '',
493 );
494 }
495 if (!$rows) {
496 $rows[][] = array('data' => t('No metrics found.'), 'colspan' => 3);
497 }
498 $header = array(t('Name'), t('Multiplier'), t('Displayed'));
499 $content['metrics'] = array(
500 '#type' => 'item',
501 '#title' => t('Metrics'),
502 '#value' => theme('table', $header, $rows),
503 '#weight' => 5,
504 );
505
506 $rows = array();
507 foreach ((array) $property['rankings'] as $ranking_id => $ranking) {
508 $rows[] = array(
509 $ranking['name'],
510 $ranking['threshold'],
511 $ranking['description'],
512 );
513 }
514 if (!$rows) {
515 $rows[][] = array('data' => t('No rankings found.'), 'colspan' => 4);
516 }
517 $header = array(t('Name'), t('Threshold'), t('Description'));
518 $content['rankings'] = array(
519 '#type' => 'item',
520 '#title' => t('Rankings'),
521 '#value' => theme('table', $header, $rows),
522 '#weight' => 6,
523 );
524
525 return drupal_render($content);
526 }
527
528 function metrics_property_edit($property_id = NULL) {
529 if ($_POST['op'] == t('Delete') || $_POST['confirm']) {
530 drupal_goto('admin/content/metrics/'. $property_id .'/delete');
531 }
532 if ($property = metrics_get_property($property_id)) {
533 return drupal_get_form('metrics_property_form', $property);
534 }
535 return drupal_not_found();
536 }
537
538 /**
539 * Property edit form.
540 */
541 function metrics_property_form($edit = array()) {
542 if (isset($edit['name'])) {
543 drupal_set_title(check_plain($edit['name']));
544 }
545
546 $form['name'] = array(
547 '#type' => 'textfield',
548 '#title' => t('Property name'),
549 '#default_value' => $edit['name'],
550 '#maxlength' => 255,
551 '#required' => TRUE,
552 );
553 $form['description'] = array(
554 '#type' => 'textarea',
555 '#title' => t('Description'),
556 '#default_value' => $edit['description'],
557 );
558 $form['node'] = array(
559 '#type' => 'fieldset',
560 '#title' => t('Node types'),
561 '#collapsible' => TRUE,
562 '#collapsed' => FALSE,
563 '#description' => t('The property will be measured for these node types.'),
564 );
565 $form['node']['node_types'] = array(
566 '#type' => 'checkboxes',
567 '#options' => node_get_types('names'),
568 '#default_value' => $edit['node_types'],
569 );
570
571 $form['submit'] = array('#type' => 'submit', '#value' => t('Submit'));
572 if ($edit['property_id']) {
573 $form['delete'] = array('#type' => 'submit', '#value' => t('Delete'));
574 $form['property_id'] = array('#type' => 'value', '#value' => $edit['property_id']);
575 }
576 return $form;
577 }
578
579
580 function metrics_property_form_submit($form_id, $form_values) {
581 if (!empty($form_values['property_id'])) {
582 db_query("UPDATE {metrics_property} SET name = '%s', description = '%s' WHERE property_id = %d", $form_values['name'], $form_values['description'], $form_values['property_id']);
583
584 // Figure out which node types have been added and removed.
585 $new_types = array_keys(array_filter($form_values['node_types']));
586 $old_types = array();
587 $result = db_query('SELECT type FROM {metrics_property_node_types} mpnt WHERE property_id = %d', $form_values['property_id']);
588 while ($o = db_fetch_object($result)) {
589 $old_types[] = $o->type;
590 }
591 // Removals.
592 foreach (array_diff($old_types, $new_types) as $type) {
593 // When removing node type we should remove any {metrics_property_result} records for nodes of that type.
594 db_query("DELETE FROM {metrics_property_result} WHERE property_id = %d AND nid IN (SELECT nid FROM {node} n WHERE n.type = '%s')", $form_values['property_id'], $type);
595 db_query("DELETE FROM {metrics_property_node_types} WHERE property_id = %d AND type = '%s'", $form_values['property_id'], $type);
596 }
597 // Additions.
598 foreach (array_diff($new_types, $old_types) as $type) {
599 db_query("INSERT INTO {metrics_property_node_types} (property_id, type) VALUES (%d, '%s')", $form_values['property_id'], $type);
600 }
601 }
602 else {
603 $form_values['property_id'] = db_next_id('{metrics_property}_property_id');
604 db_query("INSERT INTO {metrics_property} (property_id, name, description) VALUES (%d, '%s', '%s')", $form_values['property_id'], $form_values['name'], $form_values['description']);
605 foreach (array_filter($form_values['node_types']) as $type => $selected) {
606 db_query("INSERT INTO {metrics_property_node_types} (property_id, type) VALUES (%d, '%s')", $form_values['property_id'], $type);
607 }
608 }
609 return 'admin/content/metrics/'. $form_values['property_id'];
610 }
611
612 function metrics_metric_delete_form($property_id) {
613 $property = metrics_get_property($property_id);
614
615 $form['property_id'] = array('#type' => 'value', '#value' => $property_id);
616 $form['name'] = array('#type' => 'value', '#value' => $property['name']);
617 return confirm_form(
618 $form,
619 t('Are you sure you want to delete the property %title?', array('%title' => $property['name'])),
620 'admin/content/metrics',
621 t('Deleting a property will delete all the metrics, results and rankings associated with it. This action cannot be undone.'),
622 t('Delete'),
623 t('Cancel')
624 );
625 }
626
627 function metrics_metric_delete_form_submit($form_id, $form_values) {
628 metrics_metric_delete($form_values['property_id']);
629
630 drupal_set_message(t('Deleted metric %name.', array('%name' => $form_values['name'])));
631 watchdog('metrics', t('Deleted metric %name.', array('%name' => $form_values['name'])), WATCHDOG_NOTICE);
632 return 'admin/content/metrics';
633 }
634
635 /**
636 * Delete a metric.
637 *
638 * @param $property_id
639 * The metric's ID.
640 */
641 function metrics_metric_delete($property_id) {
642 db_query('DELETE FROM {metrics_property} WHERE property_id = %d', $property_id);
643 db_query('DELETE FROM {metrics_property_node_types} WHERE property_id = %d', $property_id);
644 db_query('DELETE FROM {metrics_property_result} WHERE property_id = %d', $property_id);
645 db_query('DELETE FROM {metrics_metric} WHERE property_id = %d', $property_id);
646 db_query('DELETE FROM {metrics_ranking} WHERE property_id = %d', $property_id);
647 }
648
649
650
651
652 /**
653 * Property metrics form.
654 */
655 function metrics_metrics_form($edit = array()) {
656 if (isset($edit['name'])) {
657 drupal_set_title(check_plain($edit['name']));
658 }
659
660 $function_options = array(0 => '<Select One>');
661 foreach (_metrics_get_metrics_functions() as $function) {
662 $info = $function('info');
663 $function_options[$function] = $info['name'];
664 }
665
666 $form['property_id'] = array(
667 '#type' => 'value',
668 '#value' => $edit['property_id'],
669 );
670 $form['metrics'] = array(
671 '#tree' => TRUE,
672 );
673 foreach ((array) $edit['metrics'] as $key => $metric) {
674 $form['metrics'][$key]['delete'] = array(
675 '#type' => 'checkbox',
676 );
677
678 if ($info = metrics_call_metric($metric, 'info')) {
679 $form['metrics'][$key]['function_name'] = array(
680 '#type' => 'item',
681 '#value' => $info['name'],
682 '#description' => $info['description'],
683 );
684 }
685 else {
686 $form['metrics'][$key]['function_name'] = array(
687 '#type' => 'item',
688 '#value' => t('Missing function: %name', array('%name' => $metric['function_name'])),
689 );
690 }
691
692 $form['metrics'][$key]['multiplier'] = array(
693 '#type' => 'textfield',
694 '#default_value' => $metric['multiplier'],
695 '#size' => 10,
696 '#required' => TRUE,
697 );
698 $form['metrics'][$key]['displayed'] = array(
699 '#type' => 'checkbox',
700 '#default_value' => $metric['displayed'],
701 );
702 // Allow the metric to put options on to the form.
703 $form['metrics'][$key]['options'] = metrics_call_metric($metric, 'options');
704 }
705
706 // If there's nothing in the select other than <Select One> then don't bother
707 // letting them add a new function.
708 if (count($function_options) > 1) {
709 $form['metrics']['new']['function_name'] = array(
710 '#type' => 'select',
711 '#options' => $function_options,
712 );
713 $form['metrics']['new']['multiplier'] = array(
714 '#type' => 'textfield',
715 '#default_value' => '1.0',
716 '#size' => 10,
717 '#required' => TRUE,
718 );
719 $form['metrics']['new']['displayed'] = array(
720 '#type' => 'checkbox',
721 '#default_value' => TRUE,
722 );
723 }
724
725 $form['submit'] = array(
726 '#type' => 'submit',
727 '#value' => t('Submit'),
728 );
729 return $form;
730 }
731
732 function theme_metrics_metrics_form(&$form) {
733 $header = array(t('Delete'), t('Function'), t('Multiplier'), t('Displayed'), t('Options'));
734 foreach (element_children($form['metrics']) as $key) {
735 $row = array();
736 $row[] = drupal_render($form['metrics'][$key]['delete']);
737 $row[] = drupal_render($form['metrics'][$key]['function_name']);
738 $row[] = drupal_render($form['metrics'][$key]['multiplier']);
739 $row[] = drupal_render($form['metrics'][$key]['displayed']);
740 $row[] = drupal_render($form['metrics'][$key]['options']);
741 $rows[] = $row;
742 }
743 $output .= theme('table', $header, $rows);
744 $output .= drupal_render($form);
745
746 return $output;
747 }
748
749 function metrics_metrics_form_submit($form_id, $form_values) {
750 if (empty($form_values['property_id'])) {
751 return;
752 }
753
754 $recompute = FALSE;
755
756 // Add the metrics.
757 foreach ($form_values['metrics'] as $metric_id => $metric) {
758 if ($metric_id == 'new') {
759 if (!empty($metric['function_name'])) {
760 $metric_id = db_next_id('{metrics_metric}_metric_id');
761 db_query("INSERT INTO {metrics_metric} (metric_id, property_id, function_name, options, displayed, multiplier) VALUES (%d, %d, '%s', '%s', %d, %f)", $metric_id, $form_values['property_id'], $metric['function_name'], serialize(array()), $metric['displayed'], $metric['multiplier']);
762 $recompute = TRUE;
763 }
764 }
765 else if ($metric['delete']) {
766 db_query("DELETE FROM {metrics_metric} WHERE metric_id = %d", $metric_id);
767 $recompute = TRUE;
768 }
769 else {
770 db_query("UPDATE {metrics_metric} SET displayed = %d, options = '%s', multiplier = %f WHERE metric_id = %d", $metric['displayed'], serialize($metric['options']), $metric['multiplier'], $metric_id);
771 $recompute = TRUE;
772 }
773 }
774
775 if ($recompute) {
776 // Mark all these results as stale so they're recomputed.
777 db_query("UPDATE {metrics_property_result} SET timestamp = 0 WHERE property_id = %d", $form_values['property_id']);
778 drupal_set_message(t('On the next cron run @count rows will need to be re-computed.', array('@count' => db_affected_rows())));
779 }
780 }
781
782
783 /**
784 * Property ranking form.
785 */
786 function metrics_rankings_form($edit = array()) {
787 if (isset($edit['name'])) {
788 drupal_set_title(check_plain($edit['name']));
789 }
790
791 $form['property_id'] = array(
792 '#type' => 'value',
793 '#value' => $edit['property_id'],
794 );
795 $form['rankings']['#tree'] = TRUE;
796 foreach ((array) $edit['rankings'] as $ranking_id => $ranking) {
797 $form['rankings'][$ranking_id]['delete'] = array(
798 '#type' => 'checkbox',
799 );
800 $form['rankings'][$ranking_id]['name'] = array(
801 '#type' => 'textfield',
802 '#default_value' => $ranking['name'],
803 '#size' => 15,
804 '#required' => TRUE,
805 );
806 $form['rankings'][$ranking_id]['threshold'] = array(
807 '#type' => 'textfield',
808 '#default_value' => $ranking['threshold'],
809 '#size' => 5,
810 '#required' => TRUE,
811 );
812 $form['rankings'][$ranking_id]['description'] = array(
813 '#type' => 'textfield',
814 '#default_value' => $ranking['description'],
815 '#size' => 20,
816 );
817 }
818
819 $form['rankings']['new']['name'] = array(
820 '#type' => 'textfield',
821 '#size' => 15,
822 );
823 $form['rankings']['new']['threshold'] = array(
824 '#type' => 'textfield',
825 '#size' => 5,
826 );
827 $form['rankings']['new']['description'] = array(
828 '#type' => 'textfield',
829 '#size' => 20,
830 );
831
832 $form['submit'] = array(
833 '#type' => 'submit',
834 '#value' => t('Submit')
835 );
836 return $form;
837 }
838
839 function theme_metrics_rankings_form(&$form) {
840 $header = array(t('Remove'), t('Name'), t('Threshold'), t('Description'));
841 foreach (element_children($form['rankings']) as $key) {
842 $row = array();
843 $row[] = drupal_render($form['rankings'][$key]['delete']);
844 $row[] = drupal_render($form['rankings'][$key]['name']);
845 $row[] = drupal_render($form['rankings'][$key]['threshold']);
846 $row[] = drupal_render($form['rankings'][$key]['description']);
847 $rows[] = $row;
848 }
849 $output .= theme('table', $header, $rows);
850 $output .= drupal_render($form);
851
852 return $output;
853 }
854
855 function metrics_rankings_form_submit($form_id, $form_values) {
856 if (empty($form_values['property_id'])) {
857 return;
858 }
859
860 $recompute = FALSE;
861
862 // Add the rankings.
863 foreach ($form_values['rankings'] as $ranking_id => $ranking) {
864 if ($ranking_id == 'new') {
865 if (!empty($ranking['name'])) {
866 $ranking_id = db_next_id('{metrics_ranking}_ranking_id');
867 db_query("INSERT INTO {metrics_ranking} (ranking_id, property_id, name, threshold, description) VALUES (%d, %d, '%s', %d, '%s')", $ranking_id, $form_values['property_id'], $ranking['name'], $ranking['threshold'], $ranking['description']);
868 $recompute = TRUE;
869 }
870 }
871 else if ($ranking['delete']) {
872 db_query("DELETE FROM {metrics_ranking} WHERE ranking_id = %d", $ranking_id);
873 $recompute = TRUE;
874 }
875 else {
876 db_query("UPDATE {metrics_ranking} SET name = '%s', threshold = %d, description = '%s' WHERE ranking_id = %d", $ranking['name'], $ranking['threshold'], $ranking['description'], $ranking_id);
877 $recompute = TRUE;
878 }
879 }
880
881 if ($recompute) {
882 // Mark all these results as stale so they're recomputed.
883 db_query("UPDATE {metrics_property_result} SET timestamp = 0 WHERE property_id = %d", $form_values['property_id']);
884 drupal_set_message(t('On the next cron run @count rows will need to be re-computed.', array('@count' => db_affected_rows())));
885 }
886 }
887
888
889 function metrics_metrics_functions() {
890 return array(
891 '_metrics_body_word_count',
892 );
893 }
894
895
896 function _metrics_body_word_count($op, $options = NULL, $node = NULL) {
897 switch ($op) {
898 case 'info':
899 return array(
900 'name' => t('Metrics: Node body length'),
901 'description' => t('If there are more than the specified number of words, returns 1.'),
902 );
903
904 case 'compute':
905 $wc = explode(' ', $node->body);
906 if ($wc > $options['words']) {
907 return array(
908 'value' => 1,
909 'description' => t('The node body is longer than @count words.', array('@count' => $options['words'])),
910 );
911 }
912 else {
913 return array(
914 'value' => 0,
915 'description' => t('The node description is too short.', array('@count' => $options['words'])),
916 );
917 }
918
919 case 'options':
920 $limit = drupal_map_assoc(array(5, 10, 15, 20, 25, 50, 100, 200, 300, 500));
921 $form['words'] = array(
922 '#type' => 'select',
923 '#title' => t('Word count'),
924 '#default_value' => isset($options['words']) ? $options['words'] : 100,
925 '#options' => $limit,
926 );
927 return $form;
928 }
929 }

  ViewVC Help
Powered by ViewVC 1.1.2