Automatically exported from code.google.com/p/planningalerts
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

164 lines
4.7 KiB

  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. use CGI qw(:cgi);
  5. use DateTime;
  6. #use DateTime::Format::DateParse;
  7. use HTML::TreeBuilder;
  8. use LWP::UserAgent;
  9. use XML::Writer;
  10. # The master URL for the Broxbourne planning search
  11. our $SearchURL = "http://www2.broxbourne.gov.uk/planningsearch/webform1.aspx";
  12. # We're a CGI script...
  13. my $query = CGI->new();
  14. # Get the date as an offset from 2000-01-01
  15. my $epoch = DateTime->new(year => 2000, month => 1, day => 1);
  16. my $querydate = DateTime->new(year => $query->param("year"),
  17. month => $query->param("month"),
  18. day => $query->param("day"));
  19. $querydate = $querydate->delta_days($epoch)->delta_days;
  20. # Construct an LWP user agent
  21. our $UA = LWP::UserAgent->new(env_proxy => 1);
  22. # Post the URL to get an initial blank form
  23. my $state = get_state(do_post());
  24. # Post each date in turn to build up the state - you can thank
  25. # Microsoft and ASP.NET for the horrible way we have to do this
  26. # by posting each argument in turn to build up the state
  27. $state = get_state(do_post_back($state, 'DateSelector1$Calendar1', $querydate));
  28. $state = get_state(do_post_back($state, 'DateSelector2$Calendar1', $querydate));
  29. # Output an HTTP response header
  30. print $query->header(-type => "text/xml");
  31. # Create an XML output stream
  32. my $Writer = XML::Writer->new(DATA_MODE => 1);
  33. # Output the XML header data
  34. $Writer->xmlDecl("UTF-8");
  35. $Writer->startTag("planning");
  36. $Writer->dataElement("authority_name", "Borough of Broxbourne");
  37. $Writer->dataElement("authority_short_name", "Broxbourne");
  38. $Writer->startTag("applications");
  39. # Get the arguments for the search...
  40. my $args = {
  41. "Srch" => "rb1",
  42. "__VIEWSTATE" => $state,
  43. "btnSearch" => "Search",
  44. "tbReference" => "",
  45. "tbRef2" => ""
  46. };
  47. # ...and then (at last) we can do the search!
  48. my $page = do_post($args);
  49. # Loop processing pages of results
  50. while ($page)
  51. {
  52. my $table = $page->look_down("_tag" => "table", "id" => "DataGrid1");
  53. # Remember the state
  54. $state = get_state($page);
  55. # Clear the page for now - this will be reinitialised if we
  56. # find another page of results to make us go round the loop
  57. # all over again
  58. undef $page;
  59. # Check that we found a table - searches that find no results
  60. # produce a page with no table in it
  61. if ($table)
  62. {
  63. # Process each row of the results
  64. foreach my $row ($table->look_down("_tag" => "tr"))
  65. {
  66. my @cells = $row->look_down("_tag" => "td");
  67. if ($cells[0]->look_down("_tag" => "input"))
  68. {
  69. my $reference = $cells[1]->as_trimmed_text;
  70. my $date = $cells[2]->as_trimmed_text;
  71. my $address = $cells[3]->as_trimmed_text;
  72. my $description = $cells[4]->as_trimmed_text;
  73. my $postcode;
  74. if ($address =~ /\s+([A-Z]+\d+\s+\d+[A-Z]+)$/)
  75. {
  76. $postcode = $1;
  77. }
  78. $Writer->startTag("application");
  79. $Writer->dataElement("council_reference", $reference);
  80. $Writer->dataElement("address", $address);
  81. $Writer->dataElement("postcode", $postcode);
  82. $Writer->dataElement("description", $description);
  83. $Writer->dataElement("date_received", $date);
  84. $Writer->endTag("application");
  85. }
  86. elsif ($cells[0]->attr("colspan") && $cells[0]->attr("colspan") eq "5")
  87. {
  88. foreach my $link ($cells[0]->look_down("_tag" => "a"))
  89. {
  90. if ($link->as_trimmed_text eq ">" &&
  91. $link->attr("href") =~ /^javascript:__doPostBack\('([^\']*)','([^\']*)'\)$/)
  92. {
  93. $page = do_post_back($state, $1, $2);
  94. }
  95. }
  96. }
  97. }
  98. }
  99. }
  100. # Finish off XML output
  101. $Writer->endTag("applications");
  102. $Writer->endTag("planning");
  103. $Writer->end();
  104. exit 0;
  105. # Extract the state from a page so we can repost it
  106. sub get_state
  107. {
  108. my $page = shift;
  109. my $viewstate = $page->look_down("_tag" => "input", "name" => "__VIEWSTATE");
  110. return $viewstate->attr("value");
  111. }
  112. # Fake up what the doPostBack javascript function in the page does...
  113. sub do_post_back
  114. {
  115. my $state = shift;
  116. my $target = shift;
  117. my $argument = shift;
  118. $target =~ s/\$/:/g;
  119. my $args = {
  120. "__EVENTTARGET" => $target,
  121. "__EVENTARGUMENT" => $argument,
  122. "__VIEWSTATE" => $state
  123. };
  124. return do_post($args);
  125. }
  126. # Post to the planning search page
  127. sub do_post
  128. {
  129. my $response = $UA->post($SearchURL, @_);
  130. die $response->status_line unless $response->is_success;
  131. return HTML::TreeBuilder->new_from_content($response->content);
  132. }