Template:NW Scan Status
NecroWatch Scan Status Map
Usage
This template will be used to create a Map with the Scan status of all suburbs and NT Buildings in Malton. Parameters are:
- <Suburb name>=<DangerMapnormal>type - a color for a suburb based on average scan ages in this suburb
- <NT Building name>=<color> - a color for each NT Building based on scan age
- <NT Building name> age=<scan age in days> - the scan age in days for each NT Building for the title-tag
{{NW Scan Status |Dakerstown=DangerMapnormalmoderately dangerous |Jensentown=DangerMapnormalvery dangerous ... |The Serrell Building=#EE0|The Serrell Building age=19 |The Preston Building=Green|The Preston Building age=2 ...
How it works
As the mediawiki offers no functions for calculating and date/time handling, creating an scan age map requires external processing.
Every 3 hours a shell script is run which fetches the NecroWatch pages with the scan images - i.e. NecroWatch/A_to_D up to NecroWatch/S_to_Z. The sourcecode of those pages is concatenated to one big file. After that, a Perl script is executed.
This Perl script will read the file, recognizing suburb headings, building sections and the signature and date of the last update. Based on this data the script calculates average scan age of a whole suburb and determines age levels for all buildings based on last update in days. Using the CMS::MediaWiki Perl module, the script will then connect to the UD wiki and update a template to show the changes.
Requirements
- An external linux/unix-based machine running 24/7
- wget
- Perl
- The CMS::MediaWiki Perl Module
- The Date::Parse Perl Module
Sourcecodes
fetchsubs.sh
Shellscript for fetching scanpages, combining them to one file, then starting the perl script. As all scripts and data files should stay in a single directory, this script should changedir (cd) there first.
#!/bin/sh # Change this to the directory where your files are cd /home/someuser/NecroWatch URL=http://wiki.urbandead.com/index.php/NecroWatch/ if [ -f full_listing ] then cp full_listing full_listing.old rm full_listing fi touch full_listing for PAGE in A_to_D E_to_L M_to_O P_to_R S_to_Z do wget ${URL}${PAGE} >> fetch.log 2>&1 cat ${PAGE} >> full_listing done for PAGE in A_to_D E_to_L M_to_O P_to_R S_to_Z do rm ${PAGE} done ./parseNWindex.pl full_listing >> parse.log 2>&1
parseNWindex.pl
Perl Script for the real work.
#!/usr/bin/perl -w use strict; use POSIX; use Date::Parse; use CMS::MediaWiki; ##################### ### Configuration ### ##################### # UD Wiki Login information: Username/Password my $WikiUser = 'Wikiuser'; my $WikiPass = 'secret password'; # UD Wiki Target Page Information: Page and Section my $WikiPage = 'SomePage/InThe/Wiki'; my $WikiPageSection = 2; # use '' for full page. 2=>edit section 2 # UD Wiki Page/Section prefix: WikiCode before map code my $WikiPagePrefix = '= Test: Scan Age Map update =\n\n'; # UD Wiki Page/Section suffix: WikiCode after map code my $WikiPageSuffix = '\nautomatically updated by --~~~~\n'; # UD Wiki summary: Update comment my $WikiPageSummary = '/* Test: Scan Age Map update */ automatically updated by script'; # Template: Name of the Template my $MapTemplate = 'User:Alka_Selzer/Sandbox1'; # Misc: Debugmessages on (=1) or off (=0) my $Debug = 0; ################################## ### End of Configuration ### ### Don't edit below this line ### ################################## # Disable localization for strftime() setlocale(LC_ALL, "C"); # HTML Indexfile should be first parameter. if (! defined($ARGV[0])) { debug("No filename given, aborting\n"); exit(1); } my $Indexfile = $ARGV[0]; # Check if file exists if (! -f $Indexfile) { debug("$Indexfile: no such file or unreadable\n"); exit(1); } debug("Using $Indexfile\n"); my $_line; my $Now = time(); # Variables for Statekeeping my $Suburb; my $Building; my $Scandate; my $IsSummarized = 0; # Statistics my $BuildingCount = 0; my $SuburbCount = 0; # Suburb Data my %Suburbage; my %Buildingdata; open(INDEX,$Indexfile) or die("cannot open $Indexfile"); while ($_line = <INDEX>) { # regular expression for suburb heading if ($_line =~ /<a name=\"(\w+?)\"><\/a>/) { # in case last building was not summarized, do it now summarize($Suburb,$Building,$Scandate) unless ($IsSummarized); $IsSummarized = 1; $Suburb = $1; $SuburbCount++; if (! defined ($Suburbage{$Suburb})) { # Prepare data structure for this suburb $Suburbage{$Suburb}{'count'}=0; $Suburbage{$Suburb}{'avg'}=0; } } # regular expression for buildings if ($_line =~ /<a href=\"\/index\.php\/User:NTScan\/(.*?)\" title=\".*?\">Update<\/a>/) { # in case last building was not summarized, do it now summarize($Suburb,$Building,$Scandate) unless ($IsSummarized); $IsSummarized = 0; $Scandate = undef; $Building = $1; $BuildingCount++; } # regular expression for user signature and the date (hh:mm, d Month yyyy) if ($_line =~ /title=\".*?\">(.*?) (\d{2}:\d{2}, \d+ \w+ \d{4} \(\w+\))/) { my $_time = $2; # remove ( ) from timezone for str2time() $_time =~ s/[\(\)]//g; $Scandate = str2time($_time); summarize($Suburb,$Building,$Scandate); $IsSummarized = 1; } } close(INDEX); # in case last building did not have a date, summerize it anyway summarize($Suburb,$Building,$Scandate) unless ($IsSummarized); debug("\n$BuildingCount NT-Buildings in $SuburbCount suburbs processed\n"); # Debuging - enable if neccessary #foreach $Suburb (keys %Suburbage) { # my $_average = $Suburbage{$Suburb}{'avg'}; # my $_days = int($_average/86400); # print "$Suburb: Age $_days days\n"; #} # Create the template text my $_template = mktemplate(); # fetch last template and compare - only update if things have changed # last used template is stored in ./template.last my $_doupdate = 0; if ( -f "template.last" ) { open(LASTTEMP, "template.last"); my $_compare=""; my $_line; while ($_line = <LASTTEMP>) { $_compare .= $_line; } close(LASTTEMP); if ($_compare ne $_template) { # update if template has changed $_doupdate = 1; } } else { # update if no template.last present $_doupdate = 1; } # do the update if $_doupdate is set updateWiki($_template,$WikiPage,$WikiPageSection) unless (! $_doupdate); # safe current template for reference open(LASTTEMP, ">template.last"); print LASTTEMP $_template; close(LASTTEMP); # all done exit(0); #################### ### subfunctions ### #################### ### summarize(SUBURB,BUILDING,DATE) ## calculate new suburb average based on date ## if date is not set, this will count as 90 days old sub summarize { my $_sub = shift; my $_bld = shift; my $_sdt = shift; return if (not defined $_bld); debug("$_sub: Got timestamp $_sdt\n") unless (! defined $_sdt); if (! defined $_sdt) { # scandate not set, use 90 days $_sdt = $Now - 10368000; # 90 days = 60*60*24*90 seconds old } elsif ($_sdt < ($Now - 10368000)) { # if scandate is older than 90 days, use 90 days $_sdt = $Now - 10368000; } # Building names may contain %28/%29, HTML code for ( and ). Replace this. $_bld =~ s/%28/(/g; $_bld =~ s/%29/)/g; # Store data $Buildingdata{$_sub}{$_bld} = $_sdt; debug("$_bld in the suburb $_sub : $_sdt\n"); debug("$_sub got time of $_sdt (age: ".($Now-$_sdt)." with Now=$Now)\n"); # calculate new average for suburb my $_count = $Suburbage{$_sub}{'count'}; my $_average = $Suburbage{$_sub}{'avg'}; my $_diff = $Now - $_sdt; my $_avg = $_count * $_average; debug("$_sub : old count $_count, old avg $_avg\n"); $_count++; $_avg += $_diff; $_average = $_avg/$_count; debug("$_sub : new count $_count, new avg $_average\n"); # store new values $Suburbage{$_sub}{'count'} = $_count; $Suburbage{$_sub}{'avg'} = $_average; } ### mktemplate() ## create template text from fetched data sub mktemplate { my $_sub; my $_i = 0; my $_returntemplate = ""; # Template Header # header $_returntemplate .= "{{".$MapTemplate."\n"; # Step one: Suburb data foreach $_sub (keys %Suburbage) { my $_average = $Suburbage{$_sub}{'avg'}; my $_days = int($_average/86400); # a day has 86400 seconds my $_subname = $_sub; # replace _ in suburb names with blanks $_subname =~ s/_/ /g; $_returntemplate .= "|$_subname="; if ($_days <= 7) { # DangerMapnormalsafe: green on the map $_returntemplate .= "DangerMapnormalsafe"; } elsif ($_days <= 21) { # DangerMapnormalmoderately dangerous: yellow on the map $_returntemplate .= "DangerMapnormalmoderately dangerous"; } elsif ($_days <= 45) { # DangerMapnormaldangerous: orange on the map $_returntemplate .= "DangerMapnormaldangerous"; } else { # DangerMapnormalvery dangerous: red on the map $_returntemplate .= "DangerMapnormalvery dangerous"; } $_i++; if ($_i == 2) { $_returntemplate .= "\n"; $_i = 0; } } # Step two: Building data foreach $_sub (keys %Buildingdata) { my $_bld; my $_tmp = $Buildingdata{$_sub}; foreach $_bld (keys %$_tmp) { my $_out = $_bld; # replace _ in building names with blanks $_out =~ s/_/ /g; $_returntemplate .= "|".$_out."="; my $_age = $Now - $Buildingdata{$_sub}{$_bld}; my $_dage = int($_age/86400); # a day has 86400 seconds if ($_dage <= 7) { # Green $_returntemplate .= "Green"; } elsif ($_dage <= 21) { # #EE0: Yellow $_returntemplate .= "#EE0"; } elsif ($_dage <= 45) { # #F80: Orange $_returntemplate .= "#F80"; } else { # Red $_returntemplate .= "Red"; } # in addition to the color, give the age in days for mouseover text $_returntemplate .= "|".$_out." age="; if ($_dage >= 90) { $_returntemplate .= "more than 90"; } else { $_returntemplate .= $_dage; } $_i++; if ($_i == 2) { $_returntemplate .= "\n"; $_i = 0; } } } $_returntemplate .= "}}\n"; return $_returntemplate; } ### debug(TEXT) ## prints TEXT but only if Debug=1 sub debug { my $_text = shift; print STDERR $_text unless (! $Debug); } ### updateWiki(TEMPLATE,PAGE,SECTION) ## updates PAGE/SECTION on the Wiki using Prefix+TEMPLATE+Suffix sub updateWiki { my $_content = shift; my $_page = shift; my $_section = shift; # create a new MediaWiki Object. Using http and wiki.urbandead.com my $_mw = CMS::MediaWiki->new( protocol => 'http', host => 'wiki.urbandead.com', debug => $Debug ); # logon to wiki debug("Login on to wiki...\n"); my $_rv; if ($_rv = $_mw->login(user => $WikiUser, pass => $WikiPass)) { # No success print STDERR "Could not login to UDWiki. Check Name and Password\n"; print "Code: $_rv\n"; exit($_rv); } ### For testing only: fetch old section. enable if needed # my $_lines = $_mw->getPage(title => $_page, section => $_section); #my $_i; #print sprintf('%08d ', ++$_i), " $_\n" foreach @$_lines; # Editing Wiki Page my $_WikiCode = $WikiPagePrefix.$_content.$WikiPageSuffix; $_rv = $_mw->editPage( title => $_page, section => $_section, text => $_WikiCode, summary => $WikiPageSummary ); # that's it. all done. }