diff --git a/bin/create-schema-classes.pl b/bin/create-schema-classes.pl
index 1ee24ba..ccfd694 100755
--- a/bin/create-schema-classes.pl
+++ b/bin/create-schema-classes.pl
@@ -30,7 +30,7 @@ sub column_info {
$info->{accessor} = $ref->{columns}->{uc($column_name)};
}
if($column_info->{data_type}=~m,date,i) {
- $info->{'locale'} = 'es_MX';
+ $info->{'locale'} = 'en_US';
$info->{datetime_undef_if_invalid} = 1;
}
if(($column_name eq 'id') || ($info->{accessor} eq 'id')) {
diff --git a/bin/import-gtd.pl b/bin/import-gtd.pl
new file mode 100755
index 0000000..0b42ed0
--- /dev/null
+++ b/bin/import-gtd.pl
@@ -0,0 +1,126 @@
+#!/usr/bin/env perl
+# Import Google Transparency Data
+use strict;
+use encoding 'utf8';
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use warnings;
+use Carp;
+use Data::Dumper;
+use Config::Any;
+use XML::Simple;
+use CPP::Object;
+use WWW::Mechanize;
+use JSON::XS;
+use DateTime::Format::Strptime;
+
+my($db,$seen,$URL);
+my $config_dir = $FindBin::Bin.'/..';
+my @config = glob('*.conf');
+my $cfg = Config::Any->load_files({files => \@config, use_ext=>1});
+for(@{$cfg}) {
+ my($filename,$config)=%$_;
+ if(my $section = $config->{'Model::DB'}) {
+ $db = CPP::Object->connect($section->{connect_info});
+ }
+ if(my $url = $config->{DataSource}->{MLabs}) {
+ $URL = $url;
+ }
+}
+die("Can't connect to database") unless $db;
+
+my $xs = XML::Simple->new();
+my $json = JSON::XS->new();
+my $strp = new DateTime::Format::Strptime(
+ pattern => '%Y-%m-%d',
+ locale => 'en_US',
+ time_zone => 'GMT',
+ );
+my $mech = WWW::Mechanize->new();
+
+$mech->get($URL.'/countries.json');
+my $js = $mech->content();
+my $list = $json->decode($js);
+foreach my $country_rec (@{$list}) {
+ my $id = $country_rec->{code};
+ next unless $id;
+ print "Doing country=$id\n";
+ my $country = $db->resultset('Country')->find($id);
+ eval {
+ $mech->get($URL.'content_removal_requests.json?country_code='.$id);
+ };
+ if($@) {
+ warn($@);
+ } else {
+ my $data = $json->decode($mech->content());
+ #print Dumper($data);
+ foreach my $rec (@{$data}) {
+ my $ts = $strp->parse_datetime($rec->{content_removal_request_period}->{period_start});
+ my $ts_end = $strp->parse_datetime($rec->{content_removal_request_period}->{period_end});
+ my @src;
+ if($rec->{court_orders}) {
+ push @src, $rec->{court_orders}." court order".($rec->{court_orders}==1?'':'s');
+ }
+ if($rec->{executive}) {
+ push @src, $rec->{executive}." request".($rec->{executive}==1?'':'s')." from executive organs (Police, etc.)";
+ }
+ my $reason =$rec->{reason}->{name} eq 'Undefined' ? 'for unspecified reasons' : 'citing "'.$rec->{reason}->{name}.'" as the main reason';
+ my $comp = $rec->{content_removal_request_period}->{percentage_complied} == 100 ? 'fully' : sprintf("by censoring %d%% of the requested items",$rec->{content_removal_request_period}->{percentage_complied});
+ my $descr = sprintf("Between %s and %s, Google received %s to take down %s from %s %s. Google complied %s.",
+ $ts->strftime("%Y-%m-%d"),
+ $ts_end->strftime("%Y-%m-%d"),
+ join(" and ",@src),
+ $rec->{items}?$rec->{items}.' item'.($rec->{items}==1?'':'s').' ':' unspecified content ',
+ $rec->{product}->{name},
+ $reason,
+ $comp,
+ );
+ my $name = sprintf("%s takedown request by government body",$rec->{product}->{name});
+ my $incident = $db->resultset('Incident')->create({
+ 'type' => '17',
+ 'ts' => $ts,
+ 'ts_end' => $ts_end,
+ 'name' => $name,
+ 'descr' => $descr,
+ 'lat' => $country_rec->{latitude},
+ 'lon' => $country_rec->{longitude},
+ });
+ $country->add_to_country_incidents({
+ incident => $incident,
+ });
+ }
+ }
+ eval {
+ $mech->get($URL.'user_data_requests.json?country_code='.$id);
+ };
+ if($@) {
+ warn($@);
+ } else {
+ my $data = $json->decode($mech->content());
+ foreach my $rec (@{$data}) {
+ my $ts = $strp->parse_datetime($rec->{period_start});
+ my $ts_end = $strp->parse_datetime($rec->{period_end});
+ my $comp = $rec->{percentage_complied} == 100 ? 'fully' : sprintf("with %d%% of the requests",$rec->{percentage_complied});
+ my $descr = sprintf("Between %s and %s, Google received %s%s. Google complied %s.",
+ $ts->strftime("%Y-%m-%d"),
+ $ts_end->strftime("%Y-%m-%d"),
+ $rec->{requests}." user data request".($rec->{requests}==1?'':'s'),
+ $rec->{accounts}?" for ".$rec->{accounts}." account".($rec->{accounts}==1?'':'s'):'',
+ $comp,
+ );
+ my $name = $rec->{requests}." user data request".($rec->{requests}==1?'':'s');
+ my $incident = $db->resultset('Incident')->create({
+ 'type' => '18',
+ 'ts' => $ts,
+ 'ts_end' => $ts_end,
+ 'name' => $name,
+ 'descr' => $descr,
+ 'lat' => $country_rec->{latitude},
+ 'lon' => $country_rec->{longitude},
+ });
+ $country->add_to_country_incidents({
+ incident => $incident,
+ });
+ }
+ }
+}
diff --git a/bin/import-mlabs b/bin/import-mlabs
new file mode 100755
index 0000000..6659311
--- /dev/null
+++ b/bin/import-mlabs
@@ -0,0 +1,150 @@
+#!/usr/bin/env perl
+use strict;
+use encoding 'utf8';
+use FindBin;
+use lib "$FindBin::Bin/../lib";
+use warnings;
+use Carp;
+use Data::Dumper;
+use Config::Any;
+use XML::Simple;
+use CPP::Object;
+use WWW::Mechanize;
+use JSON::XS;
+use DateTime::Format::Strptime;
+
+my($db,$seen,$URL);
+my $config_dir = $FindBin::Bin.'/..';
+my @config = glob('*.conf');
+my $cfg = Config::Any->load_files({files => \@config, use_ext=>1});
+for(@{$cfg}) {
+ my($filename,$config)=%$_;
+ if(my $section = $config->{'Model::DB'}) {
+ $db = CPP::Object->connect($section->{connect_info});
+ }
+ if(my $url = $config->{DataSource}->{MLabs}) {
+ $URL = $url;
+ }
+}
+die("Can't connect to database") unless $db;
+
+my $xs = XML::Simple->new();
+my $json = JSON::XS->new();
+my $strp = new DateTime::Format::Strptime(
+ pattern => '%Y-%m-%d',
+ locale => 'en_US',
+ time_zone => 'GMT',
+ );
+my(%suspicious);
+my $mech = WWW::Mechanize->new();
+$mech->get($URL.'/country_maps.json');
+my $js = $mech->content();
+my $list = $json->decode($js);
+foreach my $country_rec (@{$list}) {
+ my $id = $country_rec->{country_code};
+ next unless $id;
+ print "Doing country=$id\n";
+ my $country = $db->resultset('Country')->find($id);
+ $mech->get($URL.'country_measurements.json?country_code='.$id);
+ my $data = $json->decode($mech->content());
+ #print Dumper($data);
+ foreach my $rec (@{$data}) {
+ if($rec->{suspicious}) {
+ ++$suspicious{$id};
+ if(0) {
+ my $ts = $strp->parse_datetime($rec->{month});
+ my $incident = $db->resultset('Incident')->create({
+ 'type' => '15',
+ 'ts' => $ts,
+ 'name' => 'Suspicious connectivity drop',
+ 'descr' => 'Suspicious connectivity drop',
+ 'lat' => $country_rec->{latitude},
+ 'lon' => $country_rec->{longitude},
+ });
+ $country->add_to_country_incidents({
+ incident => $incident,
+ });
+ }
+ }
+ }
+}
+
+
+$mech->get($URL.'/city_maps.json');
+$js = $mech->content();
+$list = $json->decode($js);
+foreach my $city_rec (@{$list}) {
+ my $id = $city_rec->{country_code} || next;
+ next unless $suspicious{$id};
+ my $country = $db->resultset('Country')->find($id);
+ if(!$country) {
+ warn "No country for $id\n";
+ next;
+ }
+ print "Doing country=$id city=".$city_rec->{city}."\n";
+ eval {
+ $mech->get($URL.'city_measurements.json?city='.$city_rec->{city});
+ };
+ if($@) {
+ warn($@);
+ next;
+ }
+ my $data = $json->decode($mech->content());
+ foreach my $rec (@{$data}) {
+ if($rec->{suspicious}) {
+ my $ts = $strp->parse_datetime($rec->{month});
+ my $incident = $db->resultset('Incident')->create({
+ 'type' => '16',
+ 'ts' => $ts,
+ 'name' => 'Suspicious connectivity drop in '.$city_rec->{name},
+ 'descr' => 'Suspicious connectivity drop in '.$city_rec->{name},
+ 'lat' => $city_rec->{latitude},
+ 'lon' => $city_rec->{longitude},
+ });
+ $country->add_to_country_incidents({
+ incident => $incident,
+ });
+ }
+ }
+}
+exit(0);
+
+my $xml = $xs->XMLin("$FindBin::Bin/../tmp/countryInfo.xml");
+#print Dumper($xml);
+foreach my $country (@{$xml->{country}}) {
+ print join(":",$country->{countryCode},$country->{countryName})."\n";
+ my $id = $country->{countryCode};
+ my $found = $db->resultset('Country')->find($id);
+ my $obj = $found ? $found : $db->resultset('Country')->create({
+ id => $id,
+ });
+ $obj->name($country->{countryName});
+ $obj->bb_nw_lat($country->{bBoxNorth});
+ $obj->bb_se_lat($country->{bBoxSouth});
+ $obj->bb_nw_lon($country->{bBoxWest});
+ $obj->bb_se_lon($country->{bBoxEast});
+ $obj->update();
+ check_region($country);
+ if(!$found) {
+ $obj->add_to_region_countries({
+ region => $country->{continent},
+ });
+ }
+}
+
+sub check_region {
+ my $country = shift;
+ my $id = $country->{continent};
+ next if $seen->{continent}->{$id};
+ my $obj = $db->resultset('Region')->find($id);
+ if(!$obj) {
+ $obj = $db->resultset('Region')->create({
+ id => $id,
+ type => 'continent',
+ name => $country->{continentName},
+ });
+ $obj->update();
+ ++$seen->{continent}->{$id};
+ }
+
+}
diff --git a/lib/CPP/Controller/Root.pm b/lib/CPP/Controller/Root.pm
index 1107e35..32e07b3 100644
--- a/lib/CPP/Controller/Root.pm
+++ b/lib/CPP/Controller/Root.pm
@@ -1,6 +1,7 @@
package CPP::Controller::Root;
use Moose;
use namespace::autoclean;
+use JSON;
BEGIN { extends 'Catalyst::Controller' }
@@ -61,6 +62,55 @@ it under the same terms as Perl itself.
=cut
+our $json = JSON->new();
+
+sub incidents :Local {
+ my($self,$c) = @_;
+ my($ret);
+ foreach my $rec ($c->model('DB::Incident')->search({type=>15},{order_by=>'ts desc',rows=>50})->all) {
+ my($data);
+ map {$data->{$_} = $rec->$_} qw(lat lon);
+ $data->{ts} = $rec->ts ? $rec->ts->strftime('%Y-%m-%d') : '';
+ $data->{type} = $rec->type->id;
+ $data->{name} = $rec->type->name;
+ if(my @countries = $rec->countries()) {
+ $data->{country} = $countries[0]->id;
+ }
+
+ push @{$ret}, $data;
+ }
+ $c->response->body($json->encode($ret));
+}
+
+
+sub incident_timeline :Local {
+ my($self,$c) = @_;
+ my($ret);
+ foreach my $rec ($c->model('DB::Incident')->search({type=>15},{order_by=>'ts'})->all) {
+ my($data);
+ map {$data->{$_} = $rec->$_} qw(lat lon);
+ my $ts = $rec->ts ? $rec->ts->strftime('%Y-%m-%d') : '';
+ $data->{ts} = $ts;
+ $data->{type} = $rec->type->id;
+ $data->{name} = $rec->type->name;
+ if(my @countries = $rec->countries()) {
+ $data->{country} = $countries[0]->id;
+ }
+ if($ts) {
+ push @{$ret->{$ts}}, $data;
+ }
+ }
+ my $data;
+ foreach my $ts (sort keys %{$ret}) {
+ push @{$data}, {ts=>$ts,data=>$ret->{$ts}};
+ }
+ $c->response->body($json->encode($data));
+}
+
+sub about :Local {
+
+}
+
__PACKAGE__->meta->make_immutable;
1;
diff --git a/lib/CPP/Object/Result/CountryContent.pm b/lib/CPP/Object/Result/CountryContent.pm
index a3dadb0..d1b89ce 100644
--- a/lib/CPP/Object/Result/CountryContent.pm
+++ b/lib/CPP/Object/Result/CountryContent.pm
@@ -38,7 +38,7 @@ __PACKAGE__->table("country_content");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
=head2 last_updated
@@ -46,7 +46,7 @@ __PACKAGE__->table("country_content");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
set_on_update: 1
@@ -74,7 +74,7 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
},
"last_updated",
@@ -82,7 +82,7 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
set_on_update => 1,
},
@@ -126,8 +126,8 @@ __PACKAGE__->belongs_to(
);
-# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-10-15 22:58:22
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:A1eH5awOPjJ3V/09gRY5MQ
+# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-11-21 21:20:26
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:85N1c8jqbFr1nVBW/4NO1w
# You can replace this text with custom content, and it will be preserved on regeneration
diff --git a/lib/CPP/Object/Result/Incident.pm b/lib/CPP/Object/Result/Incident.pm
index c9a4bf8..3fe8acc 100644
--- a/lib/CPP/Object/Result/Incident.pm
+++ b/lib/CPP/Object/Result/Incident.pm
@@ -31,7 +31,7 @@ __PACKAGE__->table("incident");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
=head2 last_updated
@@ -39,10 +39,24 @@ __PACKAGE__->table("incident");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
set_on_update: 1
+=head2 ts
+
+ data_type: 'datetime'
+ datetime_undef_if_invalid: 1
+ is_nullable: 1
+ locale: 'en_US'
+
+=head2 ts_end
+
+ data_type: 'datetime'
+ datetime_undef_if_invalid: 1
+ is_nullable: 1
+ locale: 'en_US'
+
=head2 type
data_type: 'varchar'
@@ -68,6 +82,12 @@ __PACKAGE__->table("incident");
is_nullable: 1
size: 64
+=head2 name
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
=head2 descr
data_type: 'text'
@@ -93,7 +113,7 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
},
"last_updated",
@@ -101,10 +121,24 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
set_on_update => 1,
},
+ "ts",
+ {
+ data_type => "datetime",
+ datetime_undef_if_invalid => 1,
+ is_nullable => 1,
+ locale => "en_US",
+ },
+ "ts_end",
+ {
+ data_type => "datetime",
+ datetime_undef_if_invalid => 1,
+ is_nullable => 1,
+ locale => "en_US",
+ },
"type",
{ data_type => "varchar", is_foreign_key => 1, is_nullable => 1, size => 16 },
"source",
@@ -113,6 +147,8 @@ __PACKAGE__->add_columns(
{ data_type => "varchar", is_nullable => 1, size => 64 },
"usr",
{ data_type => "varchar", is_nullable => 1, size => 64 },
+ "name",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
"descr",
{ data_type => "text", is_nullable => 1 },
"lat",
@@ -175,8 +211,8 @@ __PACKAGE__->has_many(
);
-# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-10-15 22:58:22
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:gp7YcORO8Lb63HKfbC/BsA
+# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-11-21 22:37:18
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:EXiC3KmqeeEwgG3GYbNxVw
__PACKAGE__->many_to_many('countries' => 'country_incidents', 'country');
__PACKAGE__->many_to_many('regions' => 'region_incidents', 'region');
diff --git a/lib/CPP/Object/Result/IncidentType.pm b/lib/CPP/Object/Result/IncidentType.pm
index 1b62da5..771bb3a 100644
--- a/lib/CPP/Object/Result/IncidentType.pm
+++ b/lib/CPP/Object/Result/IncidentType.pm
@@ -32,6 +32,12 @@ __PACKAGE__->table("incident_type");
is_nullable: 0
size: 255
+=head2 class
+
+ data_type: 'varchar'
+ is_nullable: 1
+ size: 255
+
=cut
__PACKAGE__->add_columns(
@@ -39,6 +45,8 @@ __PACKAGE__->add_columns(
{ data_type => "varchar", is_nullable => 0, size => 16 },
"name",
{ data_type => "varchar", is_nullable => 0, size => 255 },
+ "class",
+ { data_type => "varchar", is_nullable => 1, size => 255 },
);
__PACKAGE__->set_primary_key("id");
@@ -60,8 +68,8 @@ __PACKAGE__->has_many(
);
-# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-10-15 22:58:22
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:2i02wTMw6LpXi5KGW8HR2w
+# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-11-09 12:17:11
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:CGeGC0qZG4MvxapYSeVG/g
# You can replace this text with custom content, and it will be preserved on regeneration
diff --git a/lib/CPP/Object/Result/Page.pm b/lib/CPP/Object/Result/Page.pm
index 4bf8f52..1f60261 100644
--- a/lib/CPP/Object/Result/Page.pm
+++ b/lib/CPP/Object/Result/Page.pm
@@ -31,7 +31,7 @@ __PACKAGE__->table("page");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
=head2 last_updated
@@ -39,7 +39,7 @@ __PACKAGE__->table("page");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
set_on_update: 1
@@ -58,7 +58,7 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
},
"last_updated",
@@ -66,7 +66,7 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
set_on_update => 1,
},
@@ -76,8 +76,8 @@ __PACKAGE__->add_columns(
__PACKAGE__->set_primary_key("id");
-# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-10-12 11:33:00
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:hrvDprhUMMYuG3paLlDtEQ
+# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-11-21 21:20:26
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:JYAL1YodBGOBl6EBFu+7Mg
# You can replace this text with custom content, and it will be preserved on regeneration
diff --git a/lib/CPP/Object/Result/Region.pm b/lib/CPP/Object/Result/Region.pm
index 22f07de..8a459f5 100644
--- a/lib/CPP/Object/Result/Region.pm
+++ b/lib/CPP/Object/Result/Region.pm
@@ -117,5 +117,6 @@ __PACKAGE__->has_many(
# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:wozagY2AxIulZR8kA9iY5w
__PACKAGE__->many_to_many('incidents' => 'region_incidents', 'incident');
+ __PACKAGE__->many_to_many('countries' => 'region_countries', 'country');
1;
diff --git a/lib/CPP/Object/Result/RegionContent.pm b/lib/CPP/Object/Result/RegionContent.pm
index 684482b..1eb801c 100644
--- a/lib/CPP/Object/Result/RegionContent.pm
+++ b/lib/CPP/Object/Result/RegionContent.pm
@@ -38,7 +38,7 @@ __PACKAGE__->table("region_content");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
=head2 last_updated
@@ -46,7 +46,7 @@ __PACKAGE__->table("region_content");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
set_on_update: 1
@@ -74,7 +74,7 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
},
"last_updated",
@@ -82,7 +82,7 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
set_on_update => 1,
},
@@ -126,8 +126,8 @@ __PACKAGE__->belongs_to(
);
-# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-10-15 22:58:22
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:7zDv7VVSarp9z3CLm14ncA
+# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-11-21 21:20:27
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:OjkfLjSsbhbP8RRfELduKg
# You can replace this text with custom content, and it will be preserved on regeneration
diff --git a/lib/CPP/Object/Result/Usr.pm b/lib/CPP/Object/Result/Usr.pm
index 8bf00db..cdb5205 100644
--- a/lib/CPP/Object/Result/Usr.pm
+++ b/lib/CPP/Object/Result/Usr.pm
@@ -31,7 +31,7 @@ __PACKAGE__->table("usr");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
=head2 last_updated
@@ -39,7 +39,7 @@ __PACKAGE__->table("usr");
data_type: 'datetime'
datetime_undef_if_invalid: 1
is_nullable: 1
- locale: 'es_MX'
+ locale: 'en_US'
set_on_create: 1
set_on_update: 1
@@ -71,7 +71,7 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
},
"last_updated",
@@ -79,7 +79,7 @@ __PACKAGE__->add_columns(
data_type => "datetime",
datetime_undef_if_invalid => 1,
is_nullable => 1,
- locale => "es_MX",
+ locale => "en_US",
set_on_create => 1,
set_on_update => 1,
},
@@ -125,8 +125,8 @@ __PACKAGE__->has_many(
);
-# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-10-15 22:58:22
-# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:YvVZQUV5nX0oV5zt+9u90A
+# Created by DBIx::Class::Schema::Loader v0.07002 @ 2011-11-21 21:20:27
+# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:1PMch2npYmPjd2fSY9klsw
# You can replace this text with custom content, and it will be preserved on regeneration
diff --git a/root/about.html b/root/about.html
new file mode 100644
index 0000000..6daadae
--- /dev/null
+++ b/root/about.html
@@ -0,0 +1,39 @@
+<&| /comp/page.html, title=>'About', body=>'home' &>
+
+
+
+
+
About
+
+
+
+What we did:
+
+
+We built this test version of a planned larger platform using broadband statistics provided by Measurement Lab (M-Lab)and the Transparency Reports published by Google. The former contains continuous information about the Internet speed at various locations in the world. By analyzing this data, we can estimate the Internet connectivity status for the countries and cities covered by the data.
+
+
+Our main aim has been working to build a platform to give people a tool to know what's happening in their country now and over the historical period, augmented with information on law and lobbying activities. We created a branch of our application for the hackathon and this is the result of 1,5 days work.
+
+
+Some use of dummy data:
+
+
+The landing page of this example app is displaying dummy data for the current incidents listed under the map. It's there to give you an idea of what this application could become. Obviously getting radar jamming data is completely different from monitoring internet traffic.
+
+
+We need more data:
+
+
+If you know of relevant data sets either historical or dynamic that you think would benefit this project, please let us know.
+
+
+Team for the hackday: Chris Pinchen, Ruben Bloemgarten, James Burke, Simon Funke, Florian Rathgeber, Javier Arturo Rodríguez.
+
+
+
+
+
+
+
+&>
diff --git a/root/comp/page.html b/root/comp/page.html
index c2c65f4..fe1d8db 100644
--- a/root/comp/page.html
+++ b/root/comp/page.html
@@ -1,7 +1,8 @@
<%args>
$title => ''
- $nav_active => '/',
+ $nav_active => '/'
$map => 1
+ $body => ''
%args>
<%perl>
my $static = $c->uri_for('/static');
@@ -49,17 +50,18 @@
+
% if($map) {
% }
-
+>
&>
diff --git a/root/index.html b/root/index.html
index 08ecd5f..ea02793 100644
--- a/root/index.html
+++ b/root/index.html
@@ -1,26 +1,28 @@
-<&| /comp/page.html, title=>'Home' &>
+<&| /comp/page.html, title=>'Home', body=>'home' &>
-
+
+What's the state of the Internet in
?
-
-Current Incidents
+Current Incidents
+
@@ -32,31 +34,31 @@ Current Incidents
Blackout
- Cell towers blackout in Germany >>
+ Cell towers blackout in Germany >>
Restriction
- Bandwidth throttling in Belgium >>
+ Bandwidth throttling in Belgium >>
Attack
- Server Seizure in Florida >>
+ Server Seizure in Florida >>
Attack
- Denial of Service attack in Azerbaijan >>
+ Denial of Service attack in Azerbaijan >>
Blackout
- Radio Frequency Jamming in Azerbaijan >>
+ Radio Frequency Jamming in Azerbaijan >>
@@ -68,31 +70,31 @@ Current Incidents
Blackout
- Internet blackout in Azerbaijan >>
+ Internet blackout in Azerbaijan >>
Restriction
- Destination IP blocking in Syria >>
+ Destination IP blocking in Syria >>
Attack
- Website Defacement in Paris >>
+ Website Defacement in Paris >>
Attack
- Denial of Service attack in Azabaijan >>
+ Denial of Service attack in Azabaijan >>
Blackout
- Cell Towers blackout in Mongolia >>
+ Cell Towers blackout in Mongolia >>
@@ -102,51 +104,109 @@ Current Incidents
diff --git a/root/region/comp/page.html b/root/region/comp/page.html
index a745e44..3edd4a5 100644
--- a/root/region/comp/page.html
+++ b/root/region/comp/page.html
@@ -1,3 +1,3 @@
-<&| /comp/page.html, nav_active=>'/country/' &>
+<&| /comp/page.html, nav_active=>'/region/' &>
<% $m->content() %>
&>
\ No newline at end of file
diff --git a/root/region/index.html b/root/region/index.html
index d208dd7..46a109d 100644
--- a/root/region/index.html
+++ b/root/region/index.html
@@ -1,9 +1,10 @@
<&| comp/page.html, title=>'Country list' &>
<%perl>
-my($COUNTRY,$LETTER);
+my($COUNTRY,$LETTER,$HASDATA);
foreach my $country ($c->model('DB::Country')->all) {
my($id,$name)=($country->id,$country->name);
$COUNTRY->{$id} = $name;
+ $HASDATA->{$id} = $country->incidents->count > 0;
push @{$LETTER->{lc(substr($name,0,1))}}, $id;
}
%perl>
@@ -15,8 +16,8 @@ Continent list
Content:
@@ -27,13 +28,13 @@ Continent list
<% $region->name %>
-% foreach my $rc ($region->region_countries) {
-% my $country = $rc->country;
-
<% $country->name %>
+% foreach my $country ($region->countries->search({},{order_by=>'name'})) {
+%# my $country = $rc->country;
+
<% $country->name %>
% }
% }
-&>
\ No newline at end of file
+&>
diff --git a/root/static/css/cpp.css b/root/static/css/cpp.css
index a875bf7..30b6b20 100644
--- a/root/static/css/cpp.css
+++ b/root/static/css/cpp.css
@@ -3,9 +3,12 @@
}
body {
- background-color: #B1CED6;
- background: #B1CED6 url("../img/bg.gif") top left repeat-x;
-
+ background-color: #FFFFFF;
+}
+
+body.home {
+ background: #B1CED6 url("../img/bg.gif") top left repeat-x;
+
}
@@ -33,7 +36,7 @@ header {
}
#subtitle {
-font-size:1.2em;
+font-size:1.2em;
background:#fff;
float: left;
//margin-top:10px;
@@ -51,11 +54,11 @@ position: absolute;
top: 450px;
width: 400px;
margin-left: 1px;
-z-index: 9000;
+z-index: 9000;
}
#playbutt img {
-margin-bottom:4px;
+margin-bottom:4px;
}
.playcontroltext {
@@ -69,23 +72,23 @@ position: absolute;
top: 450px;
width: 400px;
margin-left: 780px;
-z-index: 9000;
+z-index: 9000;
+
-
}
-a#playbutton:hover {
+a#playbutton:hover {
background: url("../img/playButton.png")no-repeat 0 0;
}
a#playbutton:active {
background: url("../img/pauseButton.gif")no-repeat 0 0;
-
-
+
+
}
.incidents{
@@ -93,7 +96,7 @@ background: url("../img/pauseButton.gif")no-repeat 0 0;
margin-left: 0;
padding-left: 1em;
text-indent: -1em;
-
+
}
hr {
@@ -101,13 +104,14 @@ hr {
}
div.incidents ul li {
-
-
+
+
}
.line {
-border-top: #000 solid 2px;
+border-top: #000 solid 2px;
width:900px;
+clear:both;
}
@@ -125,7 +129,7 @@ footer {
#bottomnav {
font-weight: normal;
float: right;
-
+
margin-right: 60px;
margin-bottom: 40px;
}
@@ -270,7 +274,7 @@ footer {
}
#content {
- margin-top: 190px;
+ margin-top: 40px;
}
h2.initial {
@@ -292,8 +296,23 @@ div.country-list {
column-gap: 10px;
}
+div.country-list a.country {
+ padding-left: 10px;
+}
+div.country-list a.hasdata:link {
+ background: transparent url('../img/country_not_clicked.png') center left no-repeat;
+}
+
+div.country-list a.hasdata:visited {
+ /*
+ * Please note that background-image *will* be ignored: http://hacks.mozilla.org/2010/03/privacy-related-changes-coming-to-css-vistited/
+ */
+ background: transparent url('../img/country_clicked.png') center left no-repeat;
+ color: #666;
+}
+
#map {
- background-color: #444;
+ background-color: #B0CDD5;
}
h1 {
@@ -303,6 +322,10 @@ h1 {
text-transform: uppercase;
}
+h1.float {
+ float:left;
+}
+
h1 span.country {
font-family: "Helvetica Neue", "Helvetica", "Arial", sans-serif;
font-size: 28px;
@@ -316,6 +339,24 @@ h1 span.country {
top: 180px !important;
}
+.olControlZoomPanel div {
+ background-image: url('../img/zoom-new.png') !important;
+}
+
+div.olControlZoomPanel {
+ top: 160px !important;
+}
+
+div.olControlZoomToMaxExtentItemInactive {
+ display: none;
+}
+
+div.olControlZoomOutItemInactive {
+ top: 18px !important;
+}
+
+
+
ul.linkbar {
font-family: "Helvetica Neue","Helvetica","Arial",sans-serif;
font-size: 12px;
@@ -400,3 +441,87 @@ h2:hover ul.inline {
*/
+a.panel {
+ padding-left: 8px;
+}
+a.open {
+ background: transparent url("../img/panel_open.png") center left no-repeat;
+}
+a.closed {
+ background: transparent url("../img/panel_closed.png") center left no-repeat;
+}
+
+div.panel {
+ border-top: 1px solid #f8f8f8;
+ margin:4px 4px 4px 0px;
+ padding:8px 8px 8px 16px;
+ background:#fff;
+}
+
+.phidden {
+ display:none;
+}
+
+ul.incident {
+ margin:0;
+ padding:0;
+}
+
+ul.incident li {
+ list-style: none;
+ padding:8px;
+}
+
+
+ul.incident li:nth-child(odd) {
+ background:#f8f8f8;
+}
+
+#about {
+ margin: 80px 0px;
+ background: #fff;
+ padding: 32px;
+ -moz-border-radius: 16px;
+ -webkit-border-radius: 16px;
+ border-radius: 16px;
+}
+
+
+.incident .ts {
+ float:right;
+ color: #888;
+ text-transform: uppercase;
+ font-size: 0.8em;
+}
+
+.incident .type {
+ font-weight: bold;
+}
+
+.attack .type {
+ color: #f00;
+}
+
+.restriction .type {
+ color: #ffb500;
+}
+
+.blackout .type {
+ color: #000;
+}
+
+a.data-source {
+ text-decoration: underline;
+}
+
+#homecountry {
+ margin-top: 5px;
+}
+
+#homecountry a {
+ text-decoration: underline;
+}
+
+.source-note {
+ margin: 15px 0 0 0;
+}
diff --git a/root/static/img/blackout.gif b/root/static/img/blackout.gif
new file mode 100644
index 0000000..3b10aa2
Binary files /dev/null and b/root/static/img/blackout.gif differ
diff --git a/root/static/img/country_clicked.png b/root/static/img/country_clicked.png
new file mode 100644
index 0000000..516dca0
Binary files /dev/null and b/root/static/img/country_clicked.png differ
diff --git a/root/static/img/country_not_clicked.png b/root/static/img/country_not_clicked.png
new file mode 100644
index 0000000..5341d54
Binary files /dev/null and b/root/static/img/country_not_clicked.png differ
diff --git a/root/static/img/mockdata.png b/root/static/img/mockdata.png
new file mode 100644
index 0000000..f382cf5
Binary files /dev/null and b/root/static/img/mockdata.png differ
diff --git a/root/static/img/panel_closed.png b/root/static/img/panel_closed.png
new file mode 100644
index 0000000..f61f78c
Binary files /dev/null and b/root/static/img/panel_closed.png differ
diff --git a/root/static/img/panel_open.png b/root/static/img/panel_open.png
new file mode 100644
index 0000000..fd8aa55
Binary files /dev/null and b/root/static/img/panel_open.png differ
diff --git a/root/static/img/zoom-new.png b/root/static/img/zoom-new.png
new file mode 100644
index 0000000..1336d41
Binary files /dev/null and b/root/static/img/zoom-new.png differ
diff --git a/root/static/js/jquery.sparkline.min.js b/root/static/js/jquery.sparkline.min.js
new file mode 100644
index 0000000..fe745b4
--- /dev/null
+++ b/root/static/js/jquery.sparkline.min.js
@@ -0,0 +1,94 @@
+/* jquery.sparkline 1.6 - http://omnipotent.net/jquery.sparkline/
+** Licensed under the New BSD License - see above site for details */
+
+(function($){var defaults={common:{type:'line',lineColor:'#00f',fillColor:'#cdf',defaultPixelsPerValue:3,width:'auto',height:'auto',composite:false,tagValuesAttribute:'values',tagOptionsPrefix:'spark',enableTagOptions:false},line:{spotColor:'#f80',spotRadius:1.5,minSpotColor:'#f80',maxSpotColor:'#f80',lineWidth:1,normalRangeMin:undefined,normalRangeMax:undefined,normalRangeColor:'#ccc',drawNormalOnTop:false,chartRangeMin:undefined,chartRangeMax:undefined,chartRangeMinX:undefined,chartRangeMaxX:undefined},bar:{barColor:'#00f',negBarColor:'#f44',zeroColor:undefined,nullColor:undefined,zeroAxis:undefined,barWidth:4,barSpacing:1,chartRangeMax:undefined,chartRangeMin:undefined,chartRangeClip:false,colorMap:undefined},tristate:{barWidth:4,barSpacing:1,posBarColor:'#6f6',negBarColor:'#f44',zeroBarColor:'#999',colorMap:{}},discrete:{lineHeight:'auto',thresholdColor:undefined,thresholdValue:0,chartRangeMax:undefined,chartRangeMin:undefined,chartRangeClip:false},bullet:{targetColor:'red',targetWidth:3,performanceColor:'blue',rangeColors:['#D3DAFE','#A8B6FF','#7F94FF'],base:undefined},pie:{sliceColors:['#f00','#0f0','#00f']},box:{raw:false,boxLineColor:'black',boxFillColor:'#cdf',whiskerColor:'black',outlierLineColor:'#333',outlierFillColor:'white',medianColor:'red',showOutliers:true,outlierIQR:1.5,spotRadius:1.5,target:undefined,targetColor:'#4a2',chartRangeMax:undefined,chartRangeMin:undefined}};var VCanvas_base,VCanvas_canvas,VCanvas_vml;$.fn.simpledraw=function(width,height,use_existing){if(use_existing&&this[0].VCanvas){return this[0].VCanvas;}
+if(width===undefined){width=$(this).innerWidth();}
+if(height===undefined){height=$(this).innerHeight();}
+if($.browser.hasCanvas){return new VCanvas_canvas(width,height,this);}else if($.browser.msie){return new VCanvas_vml(width,height,this);}else{return false;}};var pending=[];$.fn.sparkline=function(uservalues,userOptions){return this.each(function(){var options=new $.fn.sparkline.options(this,userOptions);var render=function(){var values,width,height;if(uservalues==='html'||uservalues===undefined){var vals=this.getAttribute(options.get('tagValuesAttribute'));if(vals===undefined||vals===null){vals=$(this).html();}
+values=vals.replace(/(^\s*\s*$)|\s+/g,'').split(',');}else{values=uservalues;}
+width=options.get('width')=='auto'?values.length*options.get('defaultPixelsPerValue'):options.get('width');if(options.get('height')=='auto'){if(!options.get('composite')||!this.VCanvas){var tmp=document.createElement('span');tmp.innerHTML='a';$(this).html(tmp);height=$(tmp).innerHeight();$(tmp).remove();}}else{height=options.get('height');}
+$.fn.sparkline[options.get('type')].call(this,values,options,width,height);};if(($(this).html()&&$(this).is(':hidden'))||($.fn.jquery<"1.3.0"&&$(this).parents().is(':hidden'))||!$(this).parents('body').length){pending.push([this,render]);}else{render.call(this);}});};$.fn.sparkline.defaults=defaults;$.sparkline_display_visible=function(){for(var i=pending.length-1;i>=0;i--){var el=pending[i][0];if($(el).is(':visible')&&!$(el).parents().is(':hidden')){pending[i][1].call(el);pending.splice(i,1);}}};var UNSET_OPTION={};var normalizeValue=function(val){switch(val){case'undefined':val=undefined;break;case'null':val=null;break;case'true':val=true;break;case'false':val=false;break;default:var nf=parseFloat(val);if(val==nf){val=nf;}}
+return val;};$.fn.sparkline.options=function(tag,userOptions){var extendedOptions;this.userOptions=userOptions=userOptions||{};this.tag=tag;this.tagValCache={};var defaults=$.fn.sparkline.defaults;var base=defaults.common;this.tagOptionsPrefix=userOptions.enableTagOptions&&(userOptions.tagOptionsPrefix||base.tagOptionsPrefix);var tagOptionType=this.getTagSetting('type');if(tagOptionType===UNSET_OPTION){extendedOptions=defaults[userOptions.type||base.type];}else{extendedOptions=defaults[tagOptionType];}
+this.mergedOptions=$.extend({},base,extendedOptions,userOptions);};$.fn.sparkline.options.prototype.getTagSetting=function(key){var val,i,prefix=this.tagOptionsPrefix;if(prefix===false||prefix===undefined){return UNSET_OPTION;}
+if(this.tagValCache.hasOwnProperty(key)){val=this.tagValCache.key;}else{val=this.tag.getAttribute(prefix+key);if(val===undefined||val===null){val=UNSET_OPTION;}else if(val.substr(0,1)=='['){val=val.substr(1,val.length-2).split(',');for(i=val.length;i--;){val[i]=normalizeValue(val[i].replace(/(^\s*)|(\s*$)/g,''));}}else if(val.substr(0,1)=='{'){var pairs=val.substr(1,val.length-2).split(',');val={};for(i=pairs.length;i--;){var keyval=pairs[i].split(':',2);val[keyval[0].replace(/(^\s*)|(\s*$)/g,'')]=normalizeValue(keyval[1].replace(/(^\s*)|(\s*$)/g,''));}}else{val=normalizeValue(val);}
+this.tagValCache.key=val;}
+return val;};$.fn.sparkline.options.prototype.get=function(key){var tagOption=this.getTagSetting(key);if(tagOption!==UNSET_OPTION){return tagOption;}
+return this.mergedOptions[key];};$.fn.sparkline.line=function(values,options,width,height){var xvalues=[],yvalues=[],yminmax=[];for(var i=0;imaxy){maxy=normalRangeMax;}}
+if(options.get('chartRangeMin')!==undefined&&(options.get('chartRangeClip')||options.get('chartRangeMin')maxy)){maxy=options.get('chartRangeMax');}
+if(options.get('chartRangeMinX')!==undefined&&(options.get('chartRangeClipX')||options.get('chartRangeMinX')maxx)){maxx=options.get('chartRangeMaxX');}
+var rangex=maxx-minx===0?1:maxx-minx;var rangey=maxy-miny===0?1:maxy-miny;var vl=yvalues.length-1;if(vl<1){this.innerHTML='';return;}
+var target=$(this).simpledraw(width,height,options.get('composite'));if(target){var canvas_width=target.pixel_width;var canvas_height=target.pixel_height;var canvas_top=0;var canvas_left=0;var spotRadius=options.get('spotRadius');if(spotRadius&&(canvas_width<(spotRadius*4)||canvas_height<(spotRadius*4))){spotRadius=0;}
+if(spotRadius){if(options.get('minSpotColor')||(options.get('spotColor')&&yvalues[vl]==miny)){canvas_height-=Math.ceil(spotRadius);}
+if(options.get('maxSpotColor')||(options.get('spotColor')&&yvalues[vl]==maxy)){canvas_height-=Math.ceil(spotRadius);canvas_top+=Math.ceil(spotRadius);}
+if(options.get('minSpotColor')||options.get('maxSpotColor')&&(yvalues[0]==miny||yvalues[0]==maxy)){canvas_left+=Math.ceil(spotRadius);canvas_width-=Math.ceil(spotRadius);}
+if(options.get('spotColor')||(options.get('minSpotColor')||options.get('maxSpotColor')&&(yvalues[vl]==miny||yvalues[vl]==maxy))){canvas_width-=Math.ceil(spotRadius);}}
+canvas_height--;var drawNormalRange=function(){if(normalRangeMin!==undefined){var ytop=canvas_top+Math.round(canvas_height-(canvas_height*((normalRangeMax-miny)/rangey)));var height=Math.round((canvas_height*(normalRangeMax-normalRangeMin))/rangey);target.drawRect(canvas_left,ytop,canvas_width,height,undefined,options.get('normalRangeColor'));}};if(!options.get('drawNormalOnTop')){drawNormalRange();}
+var path=[];var paths=[path];var x,y,vlen=yvalues.length;for(i=0;imaxy){y=maxy;}
+if(!path.length){path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)),canvas_top+canvas_height]);}
+path.push([canvas_left+Math.round((x-minx)*(canvas_width/rangex)),canvas_top+Math.round(canvas_height-(canvas_height*((y-miny)/rangey)))]);}}
+var lineshapes=[];var fillshapes=[];var plen=paths.length;for(i=0;i2){path[0]=[path[0][0],path[1][1]];}
+lineshapes.push(path);}
+plen=fillshapes.length;for(i=0;imax)){max=options.get('chartRangeMax');}
+var zeroAxis=options.get('zeroAxis');if(zeroAxis===undefined){zeroAxis=min<0;}
+var range=max-min===0?1:max-min;var colorMapByIndex,colorMapByValue;if($.isArray(options.get('colorMap'))){colorMapByIndex=options.get('colorMap');colorMapByValue=null;}else{colorMapByIndex=null;colorMapByValue=options.get('colorMap');}
+var target=$(this).simpledraw(width,height,options.get('composite'));if(target){var color,canvas_height=target.pixel_height,yzero=min<0&&zeroAxis?canvas_height-Math.round(canvas_height*(Math.abs(min)/range))-1:canvas_height-1;for(i=values.length;i--;){var x=i*(options.get('barWidth')+options.get('barSpacing')),y,val=values[i];if(val===null){if(options.get('nullColor')){color=options.get('nullColor');val=(zeroAxis&&min<0)?0:min;height=1;y=(zeroAxis&&min<0)?yzero:canvas_height-height;}else{continue;}}else{if(valmax){val=max;}
+color=(val<0)?options.get('negBarColor'):options.get('barColor');if(zeroAxis&&min<0){height=Math.round(canvas_height*((Math.abs(val)/range)))+1;y=(val<0)?yzero:yzero-height;}else{height=Math.round(canvas_height*((val-min)/range))+1;y=canvas_height-height;}
+if(val===0&&options.get('zeroColor')!==undefined){color=options.get('zeroColor');}
+if(colorMapByValue&&colorMapByValue[val]){color=colorMapByValue[val];}else if(colorMapByIndex&&colorMapByIndex.length>i){color=colorMapByIndex[i];}
+if(color===null){continue;}}
+target.drawRect(x,y,options.get('barWidth')-1,height-1,color,color);}}else{this.innerHTML='';}};$.fn.sparkline.tristate=function(values,options,width,height){values=$.map(values,Number);width=(values.length*options.get('barWidth'))+((values.length-1)*options.get('barSpacing'));var colorMapByIndex,colorMapByValue;if($.isArray(options.get('colorMap'))){colorMapByIndex=options.get('colorMap');colorMapByValue=null;}else{colorMapByIndex=null;colorMapByValue=options.get('colorMap');}
+var target=$(this).simpledraw(width,height,options.get('composite'));if(target){var canvas_height=target.pixel_height,half_height=Math.round(canvas_height/2);for(var i=values.length;i--;){var x=i*(options.get('barWidth')+options.get('barSpacing')),y,color;if(values[i]<0){y=half_height;height=half_height-1;color=options.get('negBarColor');}else if(values[i]>0){y=0;height=half_height-1;color=options.get('posBarColor');}else{y=half_height-1;height=2;color=options.get('zeroBarColor');}
+if(colorMapByValue&&colorMapByValue[values[i]]){color=colorMapByValue[values[i]];}else if(colorMapByIndex&&colorMapByIndex.length>i){color=colorMapByIndex[i];}
+if(color===null){continue;}
+target.drawRect(x,y,options.get('barWidth')-1,height-1,color,color);}}else{this.innerHTML='';}};$.fn.sparkline.discrete=function(values,options,width,height){values=$.map(values,Number);width=options.get('width')=='auto'?values.length*2:width;var interval=Math.floor(width/values.length);var target=$(this).simpledraw(width,height,options.get('composite'));if(target){var canvas_height=target.pixel_height,line_height=options.get('lineHeight')=='auto'?Math.round(canvas_height*0.3):options.get('lineHeight'),pheight=canvas_height-line_height,min=Math.min.apply(Math,values),max=Math.max.apply(Math,values);if(options.get('chartRangeMin')!==undefined&&(options.get('chartRangeClip')||options.get('chartRangeMin')max)){max=options.get('chartRangeMax');}
+var range=max-min;for(var i=values.length;i--;){var val=values[i];if(valmax){val=max;}
+var x=(i*interval),ytop=Math.round(pheight-pheight*((val-min)/range));target.drawLine(x,ytop,x,ytop+line_height,(options.get('thresholdColor')&&val1){var canvas_width=target.pixel_width-Math.ceil(options.get('targetWidth')/2),canvas_height=target.pixel_height,min=Math.min.apply(Math,values),max=Math.max.apply(Math,values);if(options.get('base')===undefined){min=min<0?min:0;}else{min=options.get('base');}
+var range=max-min;for(var i=2,vlen=values.length;i1){var canvas_width=target.pixel_width,canvas_height=target.pixel_height,radius=Math.floor(Math.min(canvas_width,canvas_height)/2),total=0,next=0,circle=2*Math.PI;for(var i=values.length;i--;){total+=values[i];}
+if(options.get('offset')){next+=(2*Math.PI)*(options.get('offset')/360);}
+var vlen=values.length;for(i=0;i0){end=next+(circle*(values[i]/total));}
+target.drawPieSlice(radius,radius,radius,start,end,undefined,options.get('sliceColors')[i%options.get('sliceColors').length]);next=end;}}};var quartile=function(values,q){if(q==2){var vl2=Math.floor(values.length/2);return values.length%2?values[vl2]:(values[vl2]+values[vl2+1])/2;}else{var vl4=Math.floor(values.length/4);return values.length%2?(values[vl4*q]+values[vl4*q+1])/2:values[vl4*q];}};$.fn.sparkline.box=function(values,options,width,height){values=$.map(values,Number);width=options.get('width')=='auto'?'4.0em':width;var minvalue=options.get('chartRangeMin')===undefined?Math.min.apply(Math,values):options.get('chartRangeMin'),maxvalue=options.get('chartRangeMax')===undefined?Math.max.apply(Math,values):options.get('chartRangeMax'),target=$(this).simpledraw(width,height,options.get('composite')),vlen=values.length,lwhisker,loutlier,q1,q2,q3,rwhisker,routlier;if(target&&values.length>1){var canvas_width=target.pixel_width,canvas_height=target.pixel_height;if(options.get('raw')){if(options.get('showOutliers')&&values.length>5){loutlier=values[0];lwhisker=values[1];q1=values[2];q2=values[3];q3=values[4];rwhisker=values[5];routlier=values[6];}else{lwhisker=values[0];q1=values[1];q2=values[2];q3=values[3];rwhisker=values[4];}}else{values.sort(function(a,b){return a-b;});q1=quartile(values,1);q2=quartile(values,2);q3=quartile(values,3);var iqr=q3-q1;if(options.get('showOutliers')){lwhisker=undefined;rwhisker=undefined;for(var i=0;iq1-(iqr*options.get('outlierIQR'))){lwhisker=values[i];}
+if(values[i]rwhisker){target.drawCircle((routlier-minvalue)*unitsize+canvas_left,canvas_height/2,options.get('spotRadius'),options.get('outlierLineColor'),options.get('outlierFillColor'));}}
+target.drawRect(Math.round((q1-minvalue)*unitsize+canvas_left),Math.round(canvas_height*0.1),Math.round((q3-q1)*unitsize),Math.round(canvas_height*0.8),options.get('boxLineColor'),options.get('boxFillColor'));target.drawLine(Math.round((lwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height/2),Math.round((q1-minvalue)*unitsize+canvas_left),Math.round(canvas_height/2),options.get('lineColor'));target.drawLine(Math.round((lwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height/4),Math.round((lwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height-canvas_height/4),options.get('whiskerColor'));target.drawLine(Math.round((rwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height/2),Math.round((q3-minvalue)*unitsize+canvas_left),Math.round(canvas_height/2),options.get('lineColor'));target.drawLine(Math.round((rwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height/4),Math.round((rwhisker-minvalue)*unitsize+canvas_left),Math.round(canvas_height-canvas_height/4),options.get('whiskerColor'));target.drawLine(Math.round((q2-minvalue)*unitsize+canvas_left),Math.round(canvas_height*0.1),Math.round((q2-minvalue)*unitsize+canvas_left),Math.round(canvas_height*0.9),options.get('medianColor'));if(options.get('target')){var size=Math.ceil(options.get('spotRadius'));target.drawLine(Math.round((options.get('target')-minvalue)*unitsize+canvas_left),Math.round((canvas_height/2)-size),Math.round((options.get('target')-minvalue)*unitsize+canvas_left),Math.round((canvas_height/2)+size),options.get('targetColor'));target.drawLine(Math.round((options.get('target')-minvalue)*unitsize+canvas_left-size),Math.round(canvas_height/2),Math.round((options.get('target')-minvalue)*unitsize+canvas_left+size),Math.round(canvas_height/2),options.get('targetColor'));}}else{this.innerHTML='';}};if($.browser.msie&&!document.namespaces.v){document.namespaces.add('v','urn:schemas-microsoft-com:vml','#default#VML');}
+if($.browser.hasCanvas===undefined){var t=document.createElement('canvas');$.browser.hasCanvas=t.getContext!==undefined;}
+VCanvas_base=function(width,height,target){};VCanvas_base.prototype={init:function(width,height,target){this.width=width;this.height=height;this.target=target;if(target[0]){target=target[0];}
+target.VCanvas=this;},drawShape:function(path,lineColor,fillColor,lineWidth){alert('drawShape not implemented');},drawLine:function(x1,y1,x2,y2,lineColor,lineWidth){return this.drawShape([[x1,y1],[x2,y2]],lineColor,lineWidth);},drawCircle:function(x,y,radius,lineColor,fillColor){alert('drawCircle not implemented');},drawPieSlice:function(x,y,radius,startAngle,endAngle,lineColor,fillColor){alert('drawPieSlice not implemented');},drawRect:function(x,y,width,height,lineColor,fillColor){alert('drawRect not implemented');},getElement:function(){return this.canvas;},_insert:function(el,target){$(target).html(el);}};VCanvas_canvas=function(width,height,target){return this.init(width,height,target);};VCanvas_canvas.prototype=$.extend(new VCanvas_base(),{_super:VCanvas_base.prototype,init:function(width,height,target){this._super.init(width,height,target);this.canvas=document.createElement('canvas');if(target[0]){target=target[0];}
+target.VCanvas=this;$(this.canvas).css({display:'inline-block',width:width,height:height,verticalAlign:'top'});this._insert(this.canvas,target);this.pixel_height=$(this.canvas).height();this.pixel_width=$(this.canvas).width();this.canvas.width=this.pixel_width;this.canvas.height=this.pixel_height;$(this.canvas).css({width:this.pixel_width,height:this.pixel_height});},_getContext:function(lineColor,fillColor,lineWidth){var context=this.canvas.getContext('2d');if(lineColor!==undefined){context.strokeStyle=lineColor;}
+context.lineWidth=lineWidth===undefined?1:lineWidth;if(fillColor!==undefined){context.fillStyle=fillColor;}
+return context;},drawShape:function(path,lineColor,fillColor,lineWidth){var context=this._getContext(lineColor,fillColor,lineWidth);context.beginPath();context.moveTo(path[0][0]+0.5,path[0][1]+0.5);for(var i=1,plen=path.length;i';this.canvas.insertAdjacentHTML('beforeEnd',groupel);this.group=$(this.canvas).children()[0];},drawShape:function(path,lineColor,fillColor,lineWidth){var vpath=[];for(var i=0,plen=path.length;i'+' ';this.group.insertAdjacentHTML('beforeEnd',vel);},drawCircle:function(x,y,radius,lineColor,fillColor){x-=radius+1;y-=radius+1;var stroke=lineColor===undefined?' stroked="false" ':' strokeWeight="1" strokeColor="'+lineColor+'" ';var fill=fillColor===undefined?' filled="false"':' fillColor="'+fillColor+'" filled="true" ';var vel='';this.group.insertAdjacentHTML('beforeEnd',vel);},drawPieSlice:function(x,y,radius,startAngle,endAngle,lineColor,fillColor){if(startAngle==endAngle){return;}
+if((endAngle-startAngle)==(2*Math.PI)){startAngle=0.0;endAngle=(2*Math.PI);}
+var startx=x+Math.round(Math.cos(startAngle)*radius);var starty=y+Math.round(Math.sin(startAngle)*radius);var endx=x+Math.round(Math.cos(endAngle)*radius);var endy=y+Math.round(Math.sin(endAngle)*radius);if(startx==endx&&starty==endy&&(endAngle-startAngle)'+' ';this.group.insertAdjacentHTML('beforeEnd',vel);},drawRect:function(x,y,width,height,lineColor,fillColor){return this.drawShape([[x,y],[x,y+height],[x+width,y+height],[x+width,y],[x,y]],lineColor,fillColor);}});})(jQuery);
\ No newline at end of file
diff --git a/root/static/js/script.js b/root/static/js/script.js
index 206efd4..305fabd 100644
--- a/root/static/js/script.js
+++ b/root/static/js/script.js
@@ -1,8 +1,18 @@
/* Author:
-*/
-
-
-
-
+ */
+$(document).ready(function() {
+ $('a.panel').click(function() {
+ var panel = $(this);
+ var id = panel.attr('id');
+ if (panel.hasClass('open')) {
+ panel.removeClass('open').addClass('closed');
+ $('#panel_' + id).hide();
+ } else {
+ panel.removeClass('closed').addClass('open');
+ $('#panel_' + id).show();
+ }
+ return false;
+ });
+});
\ No newline at end of file
diff --git a/sql/cpp-data.sql b/sql/cpp-data.sql
index c394228..6ad5f8b 100644
--- a/sql/cpp-data.sql
+++ b/sql/cpp-data.sql
@@ -9,3 +9,8 @@ insert into incident_type values ('10','Bandwidth Throttling','restriction');
insert into incident_type values ('11','Radio Frequency jamming','restriction');
insert into incident_type values ('12','Radio Frequency jamming','restriction');
insert into incident_type values ('13','Radio Frequency jamming','restriction');
+
+insert into incident_type values ('15','Connectivity Issue','blackout');
+insert into incident_type values ('16','Connectivity Issue','blackout');
+insert into incident_type values ('17','Government takedown request','restriction');
+insert into incident_type values ('18','User takedown request','restriction');
diff --git a/sql/cpp.sql b/sql/cpp.sql
index 0809bff..c8ab4d2 100644
--- a/sql/cpp.sql
+++ b/sql/cpp.sql
@@ -76,10 +76,13 @@ create table incident (
id integer auto_increment not null,
date_created datetime,
last_updated datetime,
+ ts datetime,
+ ts_end datetime,
type varchar(16),
source varchar(16),
source_id varchar(64),
usr varchar(64),
+ name varchar(255),
descr text,
lat real,
lon real,
@@ -91,8 +94,8 @@ create table incident (
create table country_incident (
incident integer,
country varchar(16),
- foreign key (incident) references incident(id),
- foreign key (country) references country(id),
+ foreign key (incident) references incident(id) on delete cascade,
+ foreign key (country) references country(id) on delete cascade,
primary key (incident,country)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;