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

Contents of /contributions/modules/legislature/legislature.module

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


Revision 1.71 - (show annotations) (download) (as text)
Thu Oct 22 21:32:03 2009 UTC (5 weeks ago) by drumm
Branch: MAIN
CVS Tags: HEAD
Changes since 1.70: +2 -2 lines
File MIME type: text/x-php
Use gmdate instead fo date
1 <?php
2 // $Id: legislature.module,v 1.70 2009/10/21 03:09:35 drumm Exp $
3
4 /**
5 * @file
6 *
7 * To build a jurisdiction's import:
8 * - Fill out the @link /api/group/metadata metadata @endlink functions.
9 * - For each data source or type, create a PHP file,
10 * imports/{jusrisdiction}/{name}.inc. Define all functions as
11 * legislature_{jurisdiction}_{...}().
12 * - Import functions which should be visible in the UI for importing need an
13 * entry in legislature_imports().
14 * - Long imports can be queued to run later with job_queue_add().
15 * - Call @link /api/group/import import @endlink functions for saving data.
16 * - Call @link /api/group/info info @endlink functions for getting information about the
17 * database.
18 */
19
20 define('LEGISLATURE_SPONSOR', 1);
21 define('LEGISLATURE_COSPONSOR', 2);
22
23 define('LEGISLATURE_LAST_FIRST', 1);
24 define('LEGISLATURE_FIRST_LAST', 2);
25
26 function legislature_perm() {
27 return array('run legislature imports', 'configure legislatures');
28 }
29
30 function legislature_menu() {
31 return array(
32 'admin/settings/legislature' => array(
33 'title' => 'Legislatures',
34 'page callback' => 'drupal_get_form',
35 'page arguments' => array('legislature_configure_form'),
36 'access arguments' => array('configure legislatures'),
37 ),
38 'legislature-ids.csv' => array(
39 'title' => 'Legislature IDs',
40 'page callback' => 'legislature_ids_csv',
41 'access arguments' => array('access content'),
42 'type' => MENU_CALLBACK,
43 ),
44 );
45 }
46
47 /**
48 * @defgroup import Importing data
49 * Import functions will either update existing records or save new data.
50 *
51 * Most data structures should be saved via the API instead of directly
52 * accessing the database. Execptions which have not been implemented in the
53 * API yet include the following tables:
54 * - {legislature_action_politician}, individual politician votes on a action.
55 * - {legislature_bill_committee}, committees which have worked on a bill.
56 * - {legislature_bill_sponsor}, politicians which have sponsored or
57 * co-sponsored a bill.
58 * - {legislature_bill_title}, alternate titles for bills.
59 * - {legislature_committee_membership}, politician membership to committees.
60 *
61 * The full database structure is described in legislature_install().
62 */
63
64 /**
65 * @defgroup info Information retrieval
66 * Retrieve information from the legislature database, such as ID numbers.
67 */
68
69 /**
70 * @defgroup metadata Jurisdiction metadata
71 *
72 * Information about each jurisdiction.
73 *
74 * @{
75 */
76
77 /**
78 * A list of supported sessions.
79 */
80 function legislature_sessions() {
81 return array(
82 'us' => array( // Top-level key is jusisdiction.
83 '108' => t('108th U.S. Congress'), // Number => Title
84 '109' => t('109th U.S. Congress'),
85 '110' => t('110th U.S. Congress'),
86 '111' => t('111th U.S. Congress'),
87 ),
88 'ca' => array(
89 '2003' => t('2003-2004 California State Legislature'),
90 '2009' => t('2009-2010 California State Legislature'),
91 ),
92 'ia' => array(
93 '82' => t('82nd Iowa General Assembly'),
94 ),
95 'lax' => array(
96 '2009' => t('2009 City of Los Angeles'),
97 ),
98 );
99 }
100
101 /**
102 * A list of start and end years for each session.
103 */
104 function legislature_session_years() {
105 return array(
106 'us' => array(
107 '108' => array('start' => 2003, 'end' => 2004),
108 '109' => array('start' => 2005, 'end' => 2006),
109 '110' => array('start' => 2007, 'end' => 2008),
110 '111' => array('start' => 2009, 'end' => 2010),
111 ),
112 'ca' => array(
113 '2003' => array('start' => 2003, 'end' => 2004),
114 '2009' => array('start' => 2009, 'end' => 2010),
115 ),
116 'ia' => array(
117 '82' => array('start' => 2007, 'end' => 2008),
118 ),
119 'lax' => array(
120 '2009' => array('start' => 2009, 'end' => 2009),
121 ),
122 );
123 }
124
125 /**
126 * A list of offices.
127 */
128 function legislature_offices() {
129 return array(
130 'us' => array(
131 t('Senate') => array( // Office title
132 'length' => 6, // Years
133 'count_limit' => 0, // Sessions, 0 is unlimited
134 ),
135 t('House') => array(
136 'length' => 2,
137 'count_limit' => 0,
138 ),
139 ),
140 'ca' => array(
141 t('Assembly') => array(
142 'length' => 2,
143 'count_limit' => 3,
144 ),
145 t('Senate') => array(
146 'length' => 4,
147 'count_limit' => 2,
148 ),
149 t('Governor') => array(
150 'length' => 4,
151 'count_limit' => 2,
152 ),
153 ),
154 'ia' => array(
155 t('Senate') => array(
156 'length' => 4,
157 'count_limit' => 0,
158 ),
159 t('House') => array(
160 'length' => 2,
161 'count_limit' => 0,
162 ),
163 ),
164 'lax' => array(
165 t('Mayor') => array(
166 'length' => 4,
167 'count_limit' => 2,
168 ),
169 t('Controller') => array(
170 'length' => 4,
171 'count_limit' => 0,
172 ),
173 t('Attorney') => array(
174 'length' => 4,
175 'count_limit' => 0,
176 ),
177 t('Council') => array(
178 'length' => 4,
179 'count_limit' => 0,
180 ),
181 ),
182 );
183 }
184
185 /**
186 * A list of bill prefixes.
187 *
188 * @return an array of system prefix => display prefix.
189 */
190 function legislature_prefixes($jurisdiction) {
191 switch ($jurisdiction) {
192 case 'us':
193 // Put short prefixes last.
194 return array(
195 'HR' => 'H.Res.',
196 'HJ' => 'H.J.Res.',
197 'HC' => 'H.Con.Res.',
198 'H' => 'H.R.',
199 'SR' => 'S.Res.',
200 'SJ' => 'S.J.Res.',
201 'SC' => 'S.Con.Res.',
202 'S' => 'S.',
203 );
204
205 case 'ca':
206 return array(
207 'AB' => 'AB',
208 'SB' => 'SB',
209 );
210 }
211 }
212
213 /**
214 * A list of full chamber names corresponding to the first letter of the bill
215 * prefix.
216 *
217 * @return An array of prefix{0} => house name.
218 */
219 function legislature_prefix_chamber($jurisdiction) {
220 switch ($jurisdiction) {
221 case 'us':
222 return array(
223 'H' => t('House'),
224 'S' => t('Senate'),
225 );
226
227 case 'ca':
228 return array(
229 'A' => t('Assembly'),
230 'S' => t('Senate'),
231 );
232 }
233 }
234
235 /**
236 * @}
237 */
238
239 /**
240 * Parses measures into valid prefixes and numbers. If the prefix is not valid,
241 * the returned prefix is NULL.
242 */
243 function legislature_parse_measure($measure, $jurisdiction) {
244 $measure = str_replace(' ', '', str_replace('.', '', drupal_strtoupper($measure)));
245
246 foreach (legislature_prefixes($jurisdiction) as $system => $prefix) {
247 $prefix = str_replace('.', '', drupal_strtoupper($prefix));
248 if (strpos($measure, $prefix) === 0) {
249 return array($system, intval(trim(drupal_substr($measure, strlen($prefix)))));
250 }
251 }
252 return array(NULL, intval($measure));
253 }
254
255 /**
256 * Define import functions which will be visible in the UI.
257 */
258 function legislature_imports() {
259 $imports = array();
260
261 if (count(legislature_get_enabled_sessions('us')) > 0) { // Only show imports if the jurisdiction is enabled
262 $imports[t('GovTrack')] = array( // Section
263 'legislature_govtrack_politicians' => array( // Function name
264 'title' => t('Politicians'),
265 'file path' => drupal_get_path('module', 'legislature') .'/imports/us/govtrack.inc',
266 'access' => user_access('run legislature imports'),
267 ),
268 'legislature_govtrack_committees' => array(
269 'title' => t('Committees'),
270 'file path' => drupal_get_path('module', 'legislature') .'/imports/us/govtrack.inc',
271 'access' => user_access('run legislature imports'),
272 ),
273 'legislature_govtrack_bills' => array(
274 'title' => t('Bills'),
275 'file path' => drupal_get_path('module', 'legislature') .'/imports/us/govtrack.inc',
276 'access' => user_access('run legislature imports'),
277 ),
278 'legislature_govtrack_update_bill' => array(
279 'title' => t('Specific bill'),
280 'file path' => drupal_get_path('module', 'legislature') .'/imports/us/govtrack.inc',
281 'access' => user_access('run legislature imports'),
282 ),
283 'legislature_govtrack_roll' => array(
284 'title' => t('Specific roll'),
285 'file path' => drupal_get_path('module', 'legislature') .'/imports/us/govtrack.inc',
286 'access' => user_access('run legislature imports'),
287 ),
288 'legislature_govtrack_recent_votes' => array(
289 'title' => t('Recent votes'),
290 'file path' => drupal_get_path('module', 'legislature') .'/imports/us/govtrack.inc',
291 'access' => user_access('run legislature imports'),
292 ),
293 'legislature_govtrack_undervotes' => array(
294 'title' => t('Schedule updates for under or over-voted bills'),
295 'file path' => drupal_get_path('module', 'legislature') .'/imports/us/govtrack.inc',
296 'access' => user_access('run legislature imports'),
297 ),
298 );
299
300 $imports[t('Sunlight')] = array(
301 'legislature_sunlight_politicians' => array(
302 'title' => t('Politician contact information'),
303 'file path' => drupal_get_path('module', 'legislature') .'/imports/us/sunlight.inc',
304 'access' => user_access('run legislature imports') && (variable_get('legislature_sunlight_api_key', '') !== ''),
305 ),
306 );
307
308 $imports[t('Thomas')] = array(
309 'legislature_us_thomas_committees' => array(
310 'title' => t('Committees'),
311 'file path' => drupal_get_path('module', 'legislature') .'/imports/us/thomas.inc',
312 'access' => user_access('run legislature imports'),
313 ),
314 );
315 }
316
317 if (count(legislature_get_enabled_sessions('ia')) > 0) {
318 $imports[t('Iowa')] = array(
319 'legislature_ia_politicians' => array(
320 'title' => t('Politicians'),
321 'file path' => drupal_get_path('module', 'legislature') .'/imports/ia/legis_politicians.inc',
322 'access' => user_access('run legislature imports') && legislature_html_parser_installed(),
323 ),
324 'legislature_ia_bills' => array(
325 'title' => t('Bills'),
326 'file path' => drupal_get_path('module', 'legislature') .'/imports/ia/legis_bills.inc',
327 'access' => user_access('run legislature imports') && legislature_html_parser_installed(),
328 ),
329 );
330 }
331
332 if (count(legislature_get_enabled_sessions('ca')) > 0) {
333 $imports[t('California')] = array(
334 'legislature_ca_load_state_data' => array(
335 'title' => t('Load State Data (load files and move records)'),
336 'file path' => drupal_get_path('module', 'legislature') .'/imports/ca/leginfo.inc',
337 'access' => user_access('run legislature imports'),
338 ),
339 'legislature_ca_load_files' => array(
340 'title' => t('Load State Files'),
341 'file path' => drupal_get_path('module', 'legislature') .'/imports/ca/leginfo.inc',
342 'access' => user_access('run legislature imports'),
343 ),
344 'legislature_ca_move_legislators' => array(
345 'title' => t('Move State Legislators'),
346 'file path' => drupal_get_path('module', 'legislature') .'/imports/ca/leginfo.inc',
347 'access' => user_access('run legislature imports'),
348 ),
349 'legislature_ca_move_committees' => array(
350 'title' => t('Move State Committees'),
351 'file path' => drupal_get_path('module', 'legislature') .'/imports/ca/leginfo.inc',
352 'access' => user_access('run legislature imports'),
353 ),
354 'legislature_ca_move_bills' => array(
355 'title' => t('Move State Bills'),
356 'file path' => drupal_get_path('module', 'legislature') .'/imports/ca/leginfo.inc',
357 'access' => user_access('run legislature imports'),
358 ),
359 'legislature_ca_move_actions' => array(
360 'title' => t('Move State Actions'),
361 'file path' => drupal_get_path('module', 'legislature') .'/imports/ca/leginfo.inc',
362 'access' => user_access('run legislature imports'),
363 ),
364 'legislature_ca_move_nimsp' => array(
365 'title' => t('Move NIMSP Data'),
366 'file path' => drupal_get_path('module', 'legislature') .'/imports/ca/nimsp.inc',
367 'access' => user_access('run legislature imports'),
368 ),
369 );
370 }
371
372 $imports[t('CSV')] = array(
373 'legislature_import_politicians' => array(
374 'title' => t('Politicians'),
375 'file path' => drupal_get_path('module', 'legislature') .'/imports/politicians.inc',
376 'access' => user_access('run legislature imports'),
377 ),
378 'legislature_import_districts' => array(
379 'title' => t('Districts'),
380 'file path' => drupal_get_path('module', 'legislature') .'/imports/districts.inc',
381 'access' => user_access('run legislature imports'),
382 ),
383 'legislature_import_jobs' => array(
384 'title' => t('Jobs'),
385 'file path' => drupal_get_path('module', 'legislature') .'/imports/jobs.inc',
386 'access' => user_access('run legislature imports'),
387 ),
388 );
389
390 return $imports;
391 }
392
393 function legislature_help($page = NULL) {
394 switch ($page) {
395 case 'admin/content/import_manager/legislature/GovTrack':
396 case 'admin/content/import_manager/legislature/Sunlight':
397 return '<p>'. t('These can take awhile. Let the browser load and check the <a href="!logs">logs</a>. Some actions add <a href="!queue">queued jobs</a> to be executed later.', array('!logs' => url('admin/logs/watchdog'), '!queue' => url('admin/logs/job_queue'))) .'</p>';
398 }
399 }
400
401 function legislature_form_alter(&$form, &$form_state, $form_id) {
402 switch ($form_id) {
403 case 'legislature_govtrack_bills':
404 foreach (legislature_get_enabled_sessions('us') as $session) {
405 $form['data']['clear'][$session] = array(
406 '#type' => 'checkbox',
407 '#title' => t('Reload all %session bills', array('%session' => $session)),
408 );
409 }
410 break;
411
412 case 'legislature_govtrack_update_bill':
413 $form['data']['bill_id'] = array(
414 '#type' => 'textfield',
415 '#title' => t('Bill ID'),
416 );
417 break;
418
419 case 'legislature_govtrack_roll':
420 $form['data']['action_id'] = array(
421 '#type' => 'textfield',
422 '#title' => t('Action ID'),
423 );
424 break;
425
426 case 'legislature_ia_politicians':
427 if (!legislature_html_parser_installed()) {
428 $form['data'][] = array(
429 '#value' => '<p>'. t('Requires <a href="@url">htmlparser.inc</a>', array('@url' => 'http://php-html.cvs.sourceforge.net/php-html/phphtmlparser/src/htmlparser.inc?view=log')) .'</p>',
430 );
431 }
432 break;
433
434 case 'legislature_import_politicians':
435 $form['#attributes']['enctype'] = 'multipart/form-data';
436 $form['data']['file'] = array(
437 '#type' => 'file',
438 '#value' => t('CSV file'),
439 '#description' => t('With columns politician_id, last_name, full_name, roster_name, phone, fax, email, party, gender, middle_name, nick_name, first_name, name_modifier.'),
440 );
441 break;
442
443 case 'legislature_import_districts':
444 $form['#attributes']['enctype'] = 'multipart/form-data';
445 $form['data']['file'] = array(
446 '#type' => 'file',
447 '#value' => t('CSV file'),
448 '#description' => t('With columns jurisdiction, office, number, state, name_display.'),
449 );
450 break;
451
452 case 'legislature_import_jobs':
453 $form['#attributes']['enctype'] = 'multipart/form-data';
454 $form['data']['file'] = array(
455 '#type' => 'file',
456 '#value' => t('CSV file'),
457 '#description' => t('With columns politician_id, jurisdiction, state, office, number, start, end, is_candidate.'),
458 );
459 break;
460
461 case 'legislature_ca_load_state_data':
462 $sessions = legislature_sessions();
463 // Session menu
464 $form['data']['session'] = array(
465 '#type' => 'select',
466 '#title' => t('Session'),
467 '#options' => $sessions['ca'],
468 );
469 // Day menu
470 $form['data']['day'] = array(
471 '#type' => 'select',
472 '#title' => t('Day'),
473 '#options' => array(
474 'Sun' => 'Sun',
475 'Mon' => 'Mon',
476 'Tue' => 'Tue',
477 'Wed' => 'Wed',
478 'Thu' => 'Thu',
479 'Fri' => 'Fri',
480 'Sat' => 'Sat',
481 ),
482 );
483 break;
484
485 case 'legislature_ca_load_files':
486 $sessions = legislature_sessions();
487 // Session menu
488 $form['data']['session'] = array(
489 '#type' => 'select',
490 '#title' => t('Session'),
491 '#options' => $sessions['ca'],
492 );
493 // Day menu
494 $form['data']['day'] = array(
495 '#type' => 'select',
496 '#title' => t('Day'),
497 '#options' => array(
498 'Sun' => 'Sun',
499 'Mon' => 'Mon',
500 'Tue' => 'Tue',
501 'Wed' => 'Wed',
502 'Thu' => 'Thu',
503 'Fri' => 'Fri',
504 'Sat' => 'Sat',
505 ),
506 );
507 // Table menu
508 $tables['all'] = 'All';
509 include_once './' . drupal_get_path('module', 'legislature') .'/imports/ca/leginfo_mapping.inc';
510 foreach(legislature_ca_table_file_mapping() as $key => $value) {
511 $tables[$key] = $key;
512 }
513 $form['data']['table'] = array(
514 '#type' => 'select',
515 '#title' => t('Table(s)'),
516 '#options' => $tables,
517 );
518 break;
519
520 case 'legislature_ca_move_legislators':
521 $sessions = legislature_sessions();
522 // Session menu
523 $form['data']['session'] = array(
524 '#type' => 'select',
525 '#title' => t('Session'),
526 '#options' => $sessions['ca'],
527 );
528 break;
529
530 case 'legislature_ca_move_committees':
531 $sessions = legislature_sessions();
532 // Session menu
533 $form['data']['session'] = array(
534 '#type' => 'select',
535 '#title' => t('Session'),
536 '#options' => $sessions['ca'],
537 );
538 break;
539
540 case 'legislature_ca_move_bills':
541 $sessions = legislature_sessions();
542 // Session menu
543 $form['data']['session'] = array(
544 '#type' => 'select',
545 '#title' => t('Session'),
546 '#options' => $sessions['ca'],
547 );
548 break;
549 case 'legislature_ca_move_actions':
550 $sessions = legislature_sessions();
551 // Session menu
552 $form['data']['session'] = array(
553 '#type' => 'select',
554 '#title' => t('Session'),
555 '#options' => $sessions['ca'],
556 );
557 break;
558
559 }
560 }
561
562 function legislature_configure_form() {
563 $session_options = array();
564 $session_value = array();
565 foreach (legislature_sessions() as $jurisdiction => $sessions) {
566 foreach ($sessions as $session => $name) {
567 $session_options[$jurisdiction .'-'. $session] = $name;
568 $session = legislature_get_session($jurisdiction, $session);
569 if (!is_null($session) && $session->enabled) {
570 $session_value[] = $jurisdiction .'-'. $session->name;
571 }
572 }
573 }
574 $form = array();
575
576 $form['legislature_sessions'] = array(
577 '#title' => t('Enabled sessions'),
578 '#type' => 'checkboxes',
579 '#options' => $session_options,
580 '#default_value' => $session_value,
581 );
582
583 $form['legislature_ca_data_directory'] = array(
584 '#type' => 'textfield',
585 '#title' => t('California Legislature data directory'),
586 '#default_value' => variable_get('legislature_ca_data_directory', ''),
587 );
588 $form['legislature_ca_antiword'] = array(
589 '#type' => 'textfield',
590 '#title' => t('Location of Antiword application (converts Word documents to plain text)'),
591 '#default_value' => variable_get('legislature_ca_antiword', ''),
592 );
593
594 $form['legislature_sunlight_api_key'] = array(
595 '#type' => 'textfield',
596 '#title' => t('Sunlight API key'),
597 '#default_value' => variable_get('legislature_sunlight_api_key', ''),
598 '#description' => l(t('Register for a key'), 'http://services.sunlightlabs.com/api/register/'),
599 );
600
601 $form['submit'] = array(
602 '#type' => 'submit',
603 '#default_value' => t('Save'),
604 );
605
606 return $form;
607 }
608
609 function legislature_configure_form_submit($form, &$form_state) {
610 foreach ($form_state['values']['legislature_sessions'] as $key => $value) {
611 list($jurisdiction, $name) = explode('-', $key);
612 $session = legislature_get_session($jurisdiction, $name);
613 if (is_null($session)) {
614 db_query("INSERT INTO {legislature_session} (session_jurisdiction, session_name, enabled) VALUES ('%s', '%s', %d)", $jurisdiction, $name, ($key === $value));
615
616 // If this is the first of a jurisdiction, we need to add offices.
617 if (db_result(db_query("SELECT count(*) FROM {legislature_office} WHERE jurisdiction = '%s'", drupal_strtoupper($jurisdiction))) == 0) {
618 $offices = legislature_offices();
619 foreach ($offices[$jurisdiction] as $title => $office) {
620 db_query("INSERT INTO {legislature_office} (jurisdiction, title, length, count_limit) VALUES ('%s', '%s', %d, %d)", drupal_strtoupper($jurisdiction), $title, $office['length'], $office['count_limit']);
621 }
622 }
623 }
624 else {
625 db_query('UPDATE {legislature_session} SET enabled = %d WHERE session_id = %d', ($key === $value), $session->session_id);
626 }
627 }
628
629 variable_set('legislature_ca_data_directory', $form_state['values']['legislature_ca_data_directory']);
630 variable_set('legislature_ca_antiword', $form_state['values']['legislature_ca_antiword']);
631
632 variable_set('legislature_sunlight_api_key', $form_state['values']['legislature_sunlight_api_key']);
633
634 drupal_set_message(t('Saved configuration.'));
635 }
636
637 function legislature_ids_csv() {
638 $output = "politician_id,govtrack_id,crp_id,legis_id,fec_id,name\n";
639 $result = db_query('SELECT politician_id, govtrack_id, crp_id, legis_id, fec_id, full_name FROM {legislature_politician}');
640 while ($row = db_fetch_array($result)) {
641 $row['name'] = '"'. $row['name'] .'"';
642 $output .= implode(',', $row) ."\n";
643 }
644 drupal_set_header('Content-type: text/csv');
645 print $output;
646 }
647
648 /**
649 * Get the session object for a given jurisdiction and name.
650 *
651 * @ingroup info
652 *
653 * @param $jurisdiction
654 * A jurisdiction name
655 * @param $name
656 * A session name
657 * @return
658 * A session object. If the session does not exist, NULL.
659 */
660 function legislature_get_session($jurisdiction, $name) {
661 static $sessions;
662
663 if (is_null($sessions)) {
664 $sessions = array();
665 $result = db_query('SELECT session_id, session_jurisdiction, session_name, enabled FROM {legislature_session}');
666 while ($session = db_fetch_object($result)) {
667 $sessions[$session->session_jurisdiction][$session->session_name] = (object) array(
668 'session_id' => $session->session_id,
669 'jurisdiction' => $session->session_jurisdiction,
670 'name' => $session->session_name,
671 'enabled' => $session->enabled,
672 );
673 }
674 }
675
676 if (isset($sessions[$jurisdiction][$name])) {
677 return $sessions[$jurisdiction][$name];
678 }
679 return NULL;
680 }
681
682 /**
683 * Find enabled sessions within a jurisdiction.
684 *
685 * @ingroup info
686 *
687 * @param $jurisdiction
688 * A jurisdiction string.
689 * @return
690 * An array of enabled session name strings.
691 */
692 function legislature_get_enabled_sessions($jurisdiction) {
693 $enabled_sessions = array();
694
695 $sessions = legislature_sessions();
696 foreach (array_keys($sessions[$jurisdiction]) as $name) {
697 $session = legislature_get_session($jurisdiction, $name);
698 if (!is_null($session) && $session->enabled) {
699 $enabled_sessions[] = $name;
700 }
701 }
702
703 return $enabled_sessions;
704 }
705
706 function legislature_html_parser_installed() {
707 return is_readable(drupal_get_path('module', 'legislature') .'/htmlparser.inc');
708 }
709
710 /**
711 * Import a politician.
712 *
713 * @ingroup import
714 *
715 * @param $politician
716 * An associative array of the politician's data. A key matching
717 * $id_key .'_id' must exist. Not all possible keys need to exist.
718 * @param $id_key
719 * The key to de-dupe on. Do not include '_id' suffix.
720 *
721 * @return
722 * The $politician array with the 'politician_id' filled in.
723 */
724 function legislature_politician($politician, $id_key = 'politician') {
725 if ($id_key != 'politician') {
726 $politician['politician_id'] = legislature_get_politician_id($id_key, $politician[$id_key .'_id']);
727 }
728
729 if ($politician['politician_id'] == 0) {
730 drupal_write_record('legislature_politician', $politician);
731 $politician['politician_id'] = db_last_insert_id('legislature_politician', 'politician_id');
732 }
733 else {
734 drupal_write_record('legislature_politician', $politician, 'politician_id');
735 }
736
737 return $politician['politician_id'];
738 }
739
740 /**
741 * Import a district and retrieve the district_id.
742 *
743 * @ingroup import
744 *
745 * @param $jurisdiction
746 * The jusrisdiction string, such as 'ca' or 'us'.
747 * @param $office
748 * The office string, such as 'Senate' or 'House'.
749 * @param $state
750 * Two-letter state abbreviation, such as 'CA' or 'IA'.
751 * @param $number
752 * District number, or 0 if the district is not numbered.
753 * @param $name_display
754 * The human-readable name for the district.
755 *
756 * @return
757 * The district_id for the district.
758 */
759 function legislature_district($jurisdiction, $office, $state, $number, $name_display) {
760 $name = drupal_strtoupper($jurisdiction) .'-'. $office .'-'. $state .'-'. $number;
761 $return = db_result(db_query("SELECT district_id FROM {legislature_district} WHERE name = '%s'", $name));
762 if ($return !== FALSE) {
763 return $return;
764 }
765 else {
766 db_query("INSERT INTO {legislature_district} (jurisdiction, name, office, number, city, state, name_display) VALUES ('%s', '%s', '%s', %d, '', '%s', '%s')", $jurisdiction, $name, $office, $number, $state, $name_display);
767 return db_last_insert_id('legislature_district', 'district_id');;
768 }
769 }
770
771 /**
772 * Get a district's district_id.
773 *
774 * @ingroup info
775 *
776 * @param $jurisdiction
777 * The jusrisdiction string, such as 'ca' or 'us'.
778 * @param $office
779 * The office string, such as 'Senate' or 'House'.
780 * @param $state
781 * Two-letter state abbreviation, such as 'CA' or 'IA'.
782 * @param $number
783 * District number, or 0 if the district is not numbered.
784 *
785 * @return
786 * The district_id for the district.
787 */
788 function legislature_get_district_id($jurisdiction, $state, $office, $number) {
789 static $districts = array();
790
791 $name = drupal_strtoupper($jurisdiction) .'-'. $office .'-'. drupal_strtoupper($state) .'-'. $number;
792
793 if (!isset($districts[$name])) {
794 $districts[$name] = db_result(db_query("SELECT district_id FROM {legislature_district} WHERE name = '%s'", $name));
795 }
796
797 return $districts[$name];
798 }
799
800 /**
801 * Returns all districts in a jurisdiction.
802 */
803 function legislature_get_districts($jurisdiction) {
804 $districts = array();
805 $result = db_query("SELECT district_id, name, office, number, city, state, name_display FROM {legislature_district} WHERE jurisdiction = '%s'", $jurisdiction);
806 while ($district = db_fetch_object($result)) {
807 $districts[$district->district_id] = $district;
808 }
809 return $districts;
810 }
811
812 /**
813 * Get an office's office_id.
814 *
815 * @ingroup info
816 *
817 * @param $jurisdiction
818 * The jusrisdiction string, such as 'ca' or 'us'.
819 * @param $title
820 * The office's title.
821 * @return
822 * The office_id for the office.
823 */
824 function legislature_get_office_id($jurisdiction, $title) {
825 return db_result(db_query("SELECT office_id FROM {legislature_office} WHERE jurisdiction = '%s' AND title = '%s'", $jurisdiction, $title));
826 }
827
828 /**
829 * Save elected jobs for a politician. All the elected jobs for a politician
830 * need to be saved in one call to this function. Include elected_jobs from all
831 * sessions.
832 *
833 * Keys in a elected_job associative array are:
834 * - office_id
835 * - district_id
836 * - start, a UNIX timestamp for the start of the politician's term.
837 * - end, a UNIX timestamp for the end of the politician's term.
838 * - is_candidate, TRUE if the job is a candidacy.
839 *
840 * @ingroup import
841 *
842 * @param $jurisdiction
843 * The jusrisdiction string, such as 'ca' or 'us'.
844 * @param $politician_id
845 * The politician_id for the elected job.
846 * @param $elected_jobs
847 * An array of elected_job associative arrays.
848 */
849 function legislature_save_elected_jobs($jurisdiction, $politician_id, $elected_jobs, $session = NULL) {
850 //todo this thrashes the elected job id, but we don't really use that anywhere.
851 // Clear old jobs
852 if (isset($session)) {
853 $result = db_query("SELECT ej.elected_job_id FROM {legislature_elected_job} ej INNER JOIN {legislature_elected_job_session} ejs ON ejs.elected_job_id = ej.elected_job_id INNER JOIN {legislature_session} s ON s.session_id = ejs.session_id WHERE ej.politician_id = %d AND s.session_name = '%s' AND s.session_jurisdiction = '%s'", $politician_id, $session, $jurisdiction);
854 }
855 else {
856 $result = db_query('SELECT elected_job_id FROM {legislature_elected_job} WHERE politician_id = %d', $politician_id);
857 }
858 $old_elected_job_ids = array();
859 while ($row = db_fetch_object($result)) {
860 $old_elected_job_ids[] = $row->elected_job_id;
861 }
862 db_query("DELETE FROM {legislature_elected_job_session} WHERE elected_job_id IN (" . db_placeholders($old_elected_job_ids) . ")", $old_elected_job_ids);
863 db_query("DELETE FROM {legislature_elected_job} WHERE elected_job_id IN (" . db_placeholders($old_elected_job_ids) . ")", $old_elected_job_ids);
864
865 // Save the jobs
866 foreach ($elected_jobs as $job) {
867 db_query('INSERT INTO {legislature_elected_job} (politician_id, office_id, district_id, start, end, is_candidate) VALUES (%d, %d, %d, %d, %d, %d)', $politician_id, $job['office_id'], $job['district_id'], $job['start'], $job['end'], $job['is_candidate']);
868 $job['elected_job_id'] = db_last_insert_id('legislature_elected_job', 'elected_job_id');
869 foreach (legislature_get_sessions_by_date($jurisdiction, $job['start'], $job['end']) as $session) {
870 db_query('INSERT INTO {legislature_elected_job_session} (elected_job_id, session_id) VALUES (%d, %d)', $job['elected_job_id'], $session->session_id);
871 }
872 }
873
874 // Mark is_most_recent for all elected job rows as 0
875 db_query("UPDATE {legislature_elected_job} SET is_most_recent = 0 WHERE politician_id = %d", $politician_id);
876 // Mark record with most recent start value as 1
877 $max_start = db_result(db_query("SELECT MAX(start) FROM {legislature_elected_job} WHERE politician_id = %d", $politician_id));
878 db_query("UPDATE {legislature_elected_job} SET is_most_recent = 1 WHERE politician_id = %d AND start = %d", $politician_id, $max_start);
879 }
880
881 /**
882 * Get an array of sessions intersecting with a time period.
883 *
884 * @ingroup info
885 *
886 * @param $jurisdiction
887 * The jusrisdiction string, such as 'ca' or 'us'.
888 * @param $start
889 * A UNIX timestamp for the time period start.
890 * @param $end
891 * A UNIX timestamp for the time period end.
892 *
893 * @return
894 * An array of session objects.
895 */
896 function legislature_get_sessions_by_date($jurisdiction, $start, $end) {
897 $session_years = legislature_session_years();
898 $years = range(gmdate('Y', $start), gmdate('Y', $end));
899
900 $sessions = array();
901 foreach (legislature_get_enabled_sessions($jurisdiction) as $session) {
902 if (array_intersect(range($session_years[$jurisdiction][$session]['start'], $session_years[$jurisdiction][$session]['end']), $years)) {
903 $sessions[] = legislature_get_session($jurisdiction, $session);
904 }
905 }
906 return $sessions;
907 }
908
909 /**
910 * Import a bill's metadata.
911 *
912 * Keys for bill data include:
913 * - session_id
914 * - measure, {machine-readable prefix} .' '. {bill number}
915 * - last_version, any string which changes when a full bill update is needed.
916 * - status, a short machine-readble string describing the bill's status.
917 * - topic, the bill's main title.
918 * - description, a string descriibng the bill.
919 * all are required.
920 *
921 * @ingroup import
922 *
923 * @param $data
924 * An object of bill data.
925 *
926 * @return
927 * TRUE if a full bill update needs to be run, otherwise FALSE. The bill_id
928 * is filled into $data, which is passed by-reference.
929 */
930 function legislature_save_bill(&$data) {
931 $update = FALSE;
932
933 $bill = db_fetch_object(db_query("SELECT bill_id, last_version FROM {legislature_bill} WHERE session_id = %d AND measure = '%s'", $data['session_id'], $data['measure']));
934 if ($bill !== FALSE) {
935 $data['bill_id'] = $bill->bill_id;
936 if ($data['last_version'] != $bill->last_version) {
937 $update = TRUE;
938 $data['queued'] = TRUE;
939 }
940 db_query("UPDATE {legislature_bill} SET status = '%s', last_version = '%s', topic = '%s', description = '%s' WHERE bill_id = %d", $data['status'], $data['last_version'], $data['topic'], $data['description'], $data['bill_id']);
941 module_invoke_all('legislature', 'bill', 'update', $data);
942 }
943 else {
944 db_query("INSERT INTO {legislature_bill} (measure, prefix, session_id, topic, description, status, last_version) VALUES ('%s', '%s', %d, '%s', '%s', '%s', '%s')", $data['measure'], $data['prefix'], $data['session_id'], $data['topic'], $data['description'], $data['status'], $data['last_version']);
945 $data['bill_id'] = db_last_insert_id('legislature_bill', 'bill_id');
946 $update = TRUE;
947 module_invoke_all('legislature', 'bill', 'insert', $data);
948 }
949
950 return $update;
951 }
952
953 /**
954 * Get the standardized politician ID using a 3rd party ID.
955 *
956 * @ingroup info
957 *
958 * @param $type
959 * Either 'govtrack', 'crp', or 'legis' for US, CA, and IA.
960 * @param $id
961 * An ID number of $type.
962 *
963 * @return
964 * The corresponding standardized politician ID.
965 */
966 function legislature_get_politician_id($type, $id) {
967 static $politician_ids = array(
968 'govtrack' => array(),
969 'crp' => array(),
970 'legis' => array(),
971 'fec' => array(),
972 );
973
974 if (!in_array($type, array_keys($politician_ids))) {
975 return NULL; // We don't support this $type.
976 }
977
978 if (!isset($politician_ids[$type][$id])) {
979 $politician_ids[$type][$id] = db_result(db_query('SELECT politician_id FROM {legislature_politician} WHERE '. $type ."_id = '%s'", $id));
980 }
981
982 return $politician_ids[$type][$id];
983 }
984
985 /**
986 *
987 */
988 function legislature_search_politician_id($conditions = array()) {
989 $where = array();
990 $args = array();
991
992 foreach ($conditions as $key => $value) {
993 switch ($key) {
994 case 'session id':
995 $where[] = 'ejs.session_id = %d';
996 $args[] = $value;
997 break;
998
999 case 'office id':
1000 $where[] = 'ej.office_id = %d';
1001 $args[] = $value;
1002 break;
1003
1004 case 'district id':
1005 $where[] = 'ej.district_id = %d';
1006 $args[] = $value;
1007 break;
1008
1009 case 'name':
1010 $politician_ids = legislature_normalize_politician_name($value);
1011 if (count($politician_ids) == 0) {
1012 return NULL;
1013 }
1014 $where[] = 'p.politician_id IN ('. implode(',', array_fill(0, count($politician_ids), '%d')) .') ';
1015 $args = array_merge($args, $politician_ids);
1016 break;
1017
1018 case 'first last name':
1019 $politician_ids = legislature_normalize_politician_name($value, LEGISLATURE_FIRST_LAST);
1020 if (count($politician_ids) == 0) {
1021 return NULL;
1022 }
1023 $where[] = 'p.politician_id IN ('. implode(',', array_fill(0, count($politician_ids), '%d')) .') ';
1024 $args = array_merge($args, $politician_ids);
1025 break;
1026 }
1027 }
1028
1029 $return = db_result(db_query_range('SELECT DISTINCT p.politician_id FROM {legislature_politician} p INNER JOIN {legislature_elected_job} ej ON ej.politician_id = p.politician_id INNER JOIN {legislature_elected_job_session} ejs ON ejs.elected_job_id = ej.elected_job_id WHERE '. implode(' AND ', $where), $args, 0, 1));
1030 if ($return === FALSE) {
1031 return NULL;
1032 }
1033 else {
1034 return $return;
1035 }
1036 }
1037
1038 /**
1039 * Normalize a poltician's name and look for matches.
1040 *
1041 * Based on work by GovTrack.us. http://www.govtrack.us/source.xpd
1042 *
1043 * @param $name
1044 * The politician's name. Either as 'Last, First' or 'First Last'.
1045 * @param $format
1046 * Either LEGISLATURE_LAST_FIRST or LEGISLATURE_FIRST_LAST.
1047 * @return
1048 * An array of possible politician_ids.
1049 */
1050 function legislature_normalize_politician_name($name, $format = LEGISLATURE_LAST_FIRST) {
1051 // Split last name, first name
1052 $name = str_replace(array('``', "''"), array('"', '"'), $name); // ``Hank''
1053 $name = str_replace(array('é', 'ú'), array('e', 'u'), $name); // MySQL has little respect for proper accents
1054 $name = str_replace(',"', '",', $name); // Henry C. "Hank," Jr.
1055 $name = preg_replace('/(\.)(\S)/', '$1 $2', $name); // 'C.A.' => 'C. A.'
1056
1057 if ($format == LEGISLATURE_FIRST_LAST) {
1058 list($name, $namemod) = legislature_parse_suffix($name, $namemod);
1059
1060 $fnames = preg_split('/\s+/', $name);
1061 $lastname = array_pop($fnames);
1062 if ($namemod != '') {
1063 array_push($fnames, $namemod);
1064 }
1065
1066 if (preg_match('/^(du|de|La|Van)$/i', $fnames[count($fnames) - 1])) {
1067 $lastname = array_pop($fnames) .' '. $lastname;
1068 }
1069 }
1070 else { // Either just last name, or 'last, first' format.
1071 $lastname = $name;
1072 $fnames = array();
1073 if (preg_match('/^([^,]+),\s+([\w\W]+)/', $name, $matches)) {
1074 $lastname = $matches[1];
1075 $fnames = preg_split('/[\s,]+/', $matches[2]);
1076
1077 // Test $namemod
1078 $m = $fnames[count($fnames) - 1];
1079 if (preg_match('/^(Jr\.?|Sr\.?|I|II|III|IV)$/', $m)) {
1080 $namemod = $m;
1081 array_pop($fnames);
1082 }
1083 }
1084
1085 if ($lastname == drupal_strtoupper($lastname)) {
1086 $lastname = drupal_strtolower($lastname);
1087 $lastname = drupal_ucfirst($lastname);
1088 }
1089
1090 list($lastname, $namemod) = legislature_parse_suffix($lastname, $namemod);
1091 if ($namemod != '') {
1092 array_push($fnames, $namemod);
1093 }
1094 }
1095
1096 // Single letter first name entries need a period.
1097 foreach ($fnames as $key => $f) {
1098 if (strlen($f) == 1) {
1099 $fnames[$key] .= '.';
1100 }
1101 }
1102
1103 // Get list of matching records by last name.
1104
1105 $lastsplit = explode(' ', $lastname);
1106
1107 // For women we better search the middle name field for maiden names, but
1108 // then if it matches the last name we have to undef the middle name field
1109 // because then it's not to be understood as a middle name.
1110 $lastname = strtr($lastname, ' ', '-');
1111 $lastname2 = strtr($lastname, '-', ' ');
1112 $where = array();
1113 $args = array();
1114 $where[] = "last_name = '%s'";
1115 $args[] = $lastname;
1116 $where[] = "last_name = '%s'";
1117 $args[] = $lastname2;
1118 $where[] = "middle_name = '%s'";
1119 $args[] = $lastname2;
1120 if (count($lastsplit) == 2) {
1121 $where[] = "(middle_name = '%s' AND last_name = '%s')";
1122 $args[] = $lastsplit[0];
1123 $args[] = $lastsplit[1];
1124 }
1125 $where[] = "last_name LIKE '%s'";
1126 $args[] = $lastname .'-';
1127 $where[] = "last_name LIKE '%s'";
1128 $args[] = $lastname .' ';
1129 if (count($fnames) >= 2) {
1130 $where[] = "last_name = '%s'";
1131 $args[] = $fnames[count($fnames) - 1] .' '. $lastname;
1132 $where[] = "last_name = '%s'";
1133 $args[] = $fnames[count($fnames) - 1] .'-'. $lastname;
1134 }
1135 $count = db_result(db_query('SELECT count(*) FROM {legislature_politician} WHERE ('. implode(' OR ', $where) .') AND govtrack_id <> 0', $args));
1136 if ($count == 1) {
1137 return array(db_result(db_query('SELECT politician_id FROM {legislature_politician} WHERE ('. implode(' OR ', $where) .') AND govtrack_id <> 0', $args)));
1138 }
1139 $result = db_query('SELECT politician_id, middle_name, nick_name, last_name, name_modifier, first_name FROM {legislature_politician} WHERE ('. implode(' OR ', $where) .') AND govtrack_id <> 0', $args);
1140 while ($match = db_fetch_object($result)) {
1141 if ($lastname != $match->lastname && $lastname == $match->middle_name) {
1142 $match->middle_name = '';
1143 }
1144
1145 foreach (array('first_name', 'nick_name', 'name_modifier', 'middle_name') as $n) {
1146 $match->$n = str_replace(array('é', 'ú'), array('e', 'u'), $match->$n); // MySQL has little respect for proper accents
1147 }
1148
1149 // Make list of possible first name strings
1150 $fntests = array();
1151 $fntests[] = array($match->first_name, $match->nick_name, $match->name_modifier);
1152 if ($match->nick_name != '') {
1153 $fntests[] = array($match->first_name, $match->middle_name, $match->name_modifier);
1154 $fntests[] = array($match->first_name, '('. $match->nick_name .')', $match->name_modifier);
1155 $fntests[] = array($match->first_name, '"'. $match->nick_name .'"', $match->name_modifier);
1156 $fntests[] = array($match->first_name, $match->middle_name, '('. $match->nick_name .')', $match->name_modifier);
1157 $fntests[] =