statistics.cgi 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775
  1. #!/usr/bin/perl -w
  2. ################################################################
  3. # $Id: statistics.cgi 8278 2015-03-23 17:55:33Z betateilchen $
  4. #
  5. # (c) 2012 Copyright: Martin Fischer (m_fischer at gmx dot de)
  6. # All rights reserved
  7. #
  8. # Rework started: 2015 by betateilchen
  9. #
  10. # This script free software; you can redistribute it and/or modify
  11. # it under the terms of the GNU General Public License as published by
  12. # the Free Software Foundation; either version 2 of the License, or
  13. # any later version.
  14. #
  15. # The GNU General Public License can be found at
  16. # http://www.gnu.org/copyleft/gpl.html.
  17. # A copy is found in the textfile GPL.txt and important notices to the license
  18. # from the author is found in LICENSE.txt distributed with these scripts.
  19. #
  20. # This script is distributed in the hope that it will be useful,
  21. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. # GNU General Public License for more details.
  24. #
  25. ################################################################
  26. use Time::HiRes qw(gettimeofday);
  27. sub
  28. Log($)
  29. {
  30. my $text = shift;
  31. my @t = localtime;
  32. my $tim = sprintf("%04d.%02d.%02d %02d:%02d:%02d",
  33. $t[5]+1900,$t[4]+1,$t[3], $t[2],$t[1],$t[0]);
  34. my ($seconds, $microseconds) = gettimeofday();
  35. $tim .= sprintf(".%03d", $microseconds/1000);
  36. print LOG "$tim $text\n";
  37. }
  38. open(LOG, ">>log.out");
  39. LOG->autoflush(1);
  40. Log("Starting");
  41. use CGI qw(:standard Vars);
  42. use CGI::Carp qw(warningsToBrowser fatalsToBrowser);
  43. use DBI; #requires libdbd-sqlite3-perl
  44. use lib "./lib";
  45. use Geo::IP;
  46. use strict;
  47. use warnings;
  48. no warnings 'uninitialized';
  49. sub createDB();
  50. sub insertDB();
  51. sub checkColumn($$);
  52. sub getLocation($);
  53. sub googleVisualizationLib($);
  54. sub drawMarkersMap(@);
  55. sub drawPieChart(@);
  56. sub drawRegionsMap(@);
  57. sub drawTable(@);
  58. sub drawColumnChartTop10Modules(@);
  59. sub drawColumnChartTop10ModDef(@);
  60. sub drawBarChartModules(@);
  61. sub viewStatistics();
  62. my %tblColName;
  63. my %tblSum;
  64. my %tblCnt;
  65. # directory cointains databases
  66. my $datadir = "./data";
  67. # cascading style sheet
  68. my $css = "http://fhem.de/../css/style.css";
  69. # geo ip database file from http://www.maxmind.com/download/geoip/database/
  70. # should be updated once per month
  71. my $geoIPDat = "$datadir/GeoLiteCity.dat";
  72. # exclude modules from top 10
  73. my $excludeModules = "at autocreate dummy eventTypes holiday notify telnet weblink FileLog FHEMWEB Global SUNRISE_EL";
  74. # set limit for statistics
  75. my $limit = "datetime('now', '-12 months')";
  76. # database
  77. my $dbf = "$datadir/fhem_statistics_db.sqlite";
  78. my $dsn = "dbi:SQLite:dbname=$dbf";
  79. my $sth;
  80. # fhem node
  81. my $ua = $ENV{HTTP_USER_AGENT};
  82. my $ip = $ENV{REMOTE_ADDR};
  83. my %data = Vars();
  84. # create database if not exists
  85. createDB() if (! -e $dbf);
  86. my $dbh = DBI->connect($dsn,"","", { RaiseError => 1, ShowErrorStatement => 1 }) ||
  87. die "Cannot connect: $DBI::errstr";
  88. if(index($ua,"Fhem") > -1) {
  89. print header("application/x-www-form-urlencoded");
  90. insertDB();
  91. print "==> ok";
  92. Log("Upload-End");
  93. } else {
  94. viewStatistics();
  95. }
  96. sub viewStatistics() {
  97. my $visLib = googleVisualizationLib("'corechart','geochart','table'");
  98. my $cOS = drawPieChart("nodes","os","Operating System",390,300,"chart_os");
  99. my $cArch = drawPieChart("nodes","arch","Architecture",390,300,"chart_arch");
  100. my $cRelease = drawPieChart("nodes","release","FHEM Release",390,300,"chart_release");
  101. my $cPerl = drawPieChart("nodes","perl","Perl Version",390,300,"chart_perl");
  102. my $cModulesTop10 = drawColumnChartTop10Modules("modules","modulestop10",,"Used",800,300,"chart_modulestop10");
  103. my $cModDefTop10;# = drawColumnChartTop10ModDef("modules","definitions","Definitions",800,300,"chart_moddeftop10");
  104. #my $cModules = drawBarChartModules("modules","modules","Used","Definitions",800,600,"chart_modules");
  105. my $mWorld = drawRegionsMap("locations","countryname","world","map_world");
  106. my $mEU = drawRegionsMap("locations","countryname","150","map_europe");
  107. # my $mWesternEU = drawMarkersMap("locations","city","155","map_germany");
  108. my $mWesternEU = drawMarkersMap("locations","regionname","155","map_germany");
  109. my $tModules = drawTable3cols("modules","total_modules","string","Module","number","Used","number","Definitions","table_modules");
  110. #my $tModDef = drawTable("modules","total_moddef","string","Module","number","Definitions","table_moddef");
  111. my $tModels;# = drawTable2cols("models","total_models","string","Model","boolean","defined","table_models");
  112. my @res = $dbh->selectrow_array("SELECT created FROM db");
  113. my $since = "@res";
  114. # print header;
  115. print start_html(
  116. -title => 'fhem.de - Statistics',
  117. # -author => 'm_fischer@gmx.de',
  118. -style => {-src => $css},
  119. -meta => {'keywords' => 'fhem homeautomation statistics'},
  120. -script => [
  121. { -type => 'text/javascript',
  122. -src => 'https://www.google.com/jsapi',
  123. },
  124. $visLib,
  125. $cOS, $cArch, $cRelease, $cPerl,
  126. $cModulesTop10, $cModDefTop10,
  127. #$cModules,
  128. $mWorld, $mEU, $mWesternEU,
  129. $tModules, $tModels,
  130. ],
  131. );
  132. my ($nodes) = $dbh->selectrow_array("SELECT COUNT(uniqueID) FROM nodes");
  133. my ($nodes12) = $dbh->selectrow_array("SELECT COUNT(uniqueID) FROM nodes WHERE lastseen > $limit");
  134. print <<END;
  135. <div id="menuScrollArea">
  136. <div id="logo"></div>
  137. <div id="menu">
  138. <table><tr><td>
  139. <table class="room">
  140. <tr><td></td></tr>
  141. <tr><td><b>back to</b></td></tr>
  142. <tr><td></td></tr>
  143. <tr><td><a href="http://fhem.de">Homepage</a></td></tr>
  144. <tr><td></td></tr>
  145. </table>
  146. </td></tr></table>
  147. </div>
  148. </div>
  149. <div id="right">
  150. <noscript>
  151. <div style="text-align:center; border: 2px solid red; background: #D7FFFF;">
  152. <div style="text-align:center; background: #D7FFFF; color: red;">
  153. <h4>Please enable Javascript on your Browser!</h4>
  154. </div>
  155. </div>
  156. </noscript>
  157. <h3>Fhem Statistics ($nodes submissions since $since)</h3>
  158. <h3>Statistics use $nodes12 submissions (last 12 months)</h3>
  159. <h4>Installed on...</h4>
  160. <div id="chart_os" style="float:left; border: 1px solid black; margin-right:18px;"></div>
  161. <div id="chart_arch" style="float:left; border: 1px solid black;"></div>
  162. <div style="clear:both;"></div>
  163. <h4>Versions...</h4>
  164. <div id="chart_release" style="float:left; border: 1px solid black; margin-right:18px;"></div>
  165. <div id="chart_perl" style="float:left; border: 1px solid black;"></div>
  166. <div style="clear:both;"></div>
  167. <h4>Top 10 of most commonly used modules<small><sup>1</sup></small>...</h4>
  168. <div id="chart_modulestop10" style="width: 800px; height: 300px; border: 1px solid black;"></div>
  169. <small><sup>1</sup> excluded from graph: $excludeModules</small>
  170. <!--
  171. <h4>Top 10 of total definitions by module<small><sup>1</sup></small>...</h4>
  172. <div id="chart_moddeftop10" style="width: 800px; height: 300px; border: 1px solid black;"></div>
  173. <small><sup>1</sup> excluded from graph: $excludeModules</small>
  174. -->
  175. <!--
  176. // <h4>Top 20 of most commonly used modules (with total definitions by module)...</h4>
  177. // <div id="chart_modules" style="width: 825px; height: 600px; border: 1px solid black;"></div>
  178. //-->
  179. <h4>Locations worldwide...</h4>
  180. <div id="map_world" style="width: 800px; height: 500px; border: 1px solid black;"></div>
  181. <h4>Locations in Europe...</h4>
  182. <div id="map_europe" style="width: 800px; height: 500px; border: 1px solid black;"></div>
  183. <h4>Locations in Western Europe...</h4>
  184. <div id="map_germany" style="width: 800px; height: 500px; border: 1px solid black;"></div>
  185. <div style="float:left; width: 390px; margin-right:20px;">
  186. <h4>List of total used modules (with definitions)...</h4>
  187. <div id="table_modules" style="width: 390px; border: 1px solid black;"></div>
  188. <small><strong>Note:</strong> Click on a column header for sorting</small>
  189. </div>
  190. <div style="float:left; width: 390px;">
  191. <!--
  192. <h4>List of defined models...</h4>
  193. <div id="table_models" style="width: 390px; border: 1px solid black;"></div>
  194. <small><strong>Note:</strong> Click on a column header for sorting</small>
  195. -->
  196. </div>
  197. <div style="clear:both;"></div>
  198. <div id="footer" style="position:relative; left: -100px; text-align: center;">
  199. <p><small>Layout by M. Fischer<br/>
  200. Rework 2015 started by betateilchen</small></p>
  201. </div>
  202. </div>
  203. END
  204. print end_html;
  205. Log("Stat-End");
  206. }
  207. sub googleVisualizationLib($) {
  208. my $packages = shift;
  209. my $code =<<END;
  210. // Load the Visualization API library
  211. google.load('visualization', '1.0', {'packages':[$packages]});
  212. END
  213. return $code;
  214. }
  215. sub drawPieChart(@) {
  216. my ($table,$column,$title,$width,$height,$divID) = @_;
  217. # my $res = $dbh->selectall_arrayref("SELECT DISTINCT $column FROM $table ORDER BY $column ASC");
  218. my $res = $dbh->selectall_arrayref("SELECT DISTINCT $column FROM $table WHERE lastseen > $limit ORDER BY $column ASC");
  219. my %hash = ();
  220. foreach my $row (@$res) {
  221. my ($value) = @$row;
  222. my ($count) = $dbh->selectrow_array("SELECT COUNT(*) FROM $table WHERE $column = '$value' AND lastseen > $limit");
  223. $hash{$value} = $count;
  224. }
  225. my $addRows;
  226. foreach my $value (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
  227. $addRows .= "\t['$value',$hash{$value}],\n";
  228. }
  229. chop($addRows);
  230. my $code =<<END;
  231. google.setOnLoadCallback(drawChart_$column);
  232. function drawChart_$column() {
  233. var data = new google.visualization.DataTable();
  234. data.addColumn('string', 'Topping');
  235. data.addColumn('number', 'Slices');
  236. data.addRows([
  237. $addRows
  238. ]);
  239. var options = {
  240. title : '$title',
  241. width : $width,
  242. height : $height,
  243. is3D : true,
  244. tooltip : { showColorCode: true, },
  245. chartArea : { height:'80%',width:'95%', },
  246. };
  247. var chart = new google.visualization.PieChart(document.getElementById('$divID'));
  248. chart.draw(data, options);
  249. };
  250. END
  251. return $code;
  252. }
  253. sub drawColumnChartTop10Modules(@) {
  254. my ($table,$postfix,$rowtitle,$width,$height,$divID) = @_;
  255. $sth = $dbh->prepare("SELECT * FROM $table LIMIT 0");
  256. $sth->execute();
  257. my $res = $sth->{NAME};
  258. $sth->finish;
  259. my %hash = ();
  260. foreach my $column (@$res) {
  261. next unless $column;
  262. next if($column eq "uniqueID");
  263. #my ($sum) = $dbh->selectrow_array("SELECT sum($column) FROM $table");
  264. my ($sum) = $dbh->selectrow_array("SELECT count($column) FROM $table as t join nodes as n on t.uniqueID=n.uniqueID WHERE t.$column != 0 AND n.lastseen > $limit");
  265. $hash{$column} = $sum;
  266. }
  267. my $data;
  268. my $i=0;
  269. foreach my $column (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
  270. next if($column eq "uniqueID");
  271. next if($excludeModules =~ /$column/);
  272. $data .= "\t['$column',$hash{$column}],\n";
  273. $i++;
  274. last if($i == 10);
  275. }
  276. chop($data);
  277. my $code =<<END;
  278. google.setOnLoadCallback(drawChart_$postfix);
  279. function drawChart_$postfix() {
  280. var data = google.visualization.arrayToDataTable([
  281. ['Module','$rowtitle'],
  282. $data
  283. ]);
  284. var options = {
  285. // title : 'title',
  286. legend : { position:'none' },
  287. chartArea : { width:"90%" },
  288. fontSize : 12,
  289. vAxis : { minValue:0, },
  290. };
  291. var chart = new google.visualization.ColumnChart(document.getElementById('$divID'));
  292. chart.draw(data, options);
  293. };
  294. END
  295. return $code;
  296. }
  297. sub drawColumnChartTop10ModDef(@) {
  298. my ($table,$postfix,$rowtitle,$width,$height,$divID) = @_;
  299. $sth = $dbh->prepare("SELECT * FROM $table LIMIT 0");
  300. $sth->execute();
  301. my @res = @{$sth->{NAME}};
  302. $tblColName{$table} = \@res;
  303. $sth->finish;
  304. my @cols = map { "sum($_),count($_)" }
  305. grep { $_ ne "uniqueID" && $_ } @res;
  306. $sth = $dbh->prepare("SELECT ".join(",",@cols)." FROM $table as t join nodes as n on t.uniqueID=n.uniqueID WHERE n.lastseen > $limit");
  307. $sth->execute();
  308. my @row = $sth->fetchrow_array;
  309. my %sum = ();
  310. my %cnt = ();
  311. for(my $idx = 0; $idx < @res; $idx++) {
  312. $sum{$res[$idx]} = $row[2*$idx];
  313. $cnt{$res[$idx]} = $row[2*$idx+1];
  314. }
  315. $sth->finish;
  316. $tblSum{$table} = \%sum;
  317. $tblCnt{$table} = \%cnt;
  318. my $data;
  319. my $i=0;
  320. foreach my $column (sort {$sum{$b} <=> $sum{$a}} keys %sum) {
  321. next if($excludeModules =~ /$column/);
  322. $data .= "\t['$column',$sum{$column}],\n";
  323. $i++;
  324. last if($i == 10);
  325. }
  326. chop($data);
  327. my $code =<<END;
  328. google.setOnLoadCallback(drawChart_$postfix);
  329. function drawChart_$postfix() {
  330. var data = google.visualization.arrayToDataTable([
  331. ['Module','$rowtitle'],
  332. $data
  333. ]);
  334. var options = {
  335. // title : 'title',
  336. legend : { position:'none' },
  337. chartArea : { width:"90%" },
  338. fontSize : 12,
  339. vAxis : { minValue:0, },
  340. };
  341. var chart = new google.visualization.ColumnChart(document.getElementById('$divID'));
  342. chart.draw(data, options);
  343. };
  344. END
  345. return $code;
  346. }
  347. sub drawBarChartModules(@) {
  348. my ($table,$postfix,$row1title,$row2title,$width,$height,$divID) = @_;
  349. $sth = $dbh->prepare("SELECT * FROM $table where 1=0");
  350. $sth->execute();
  351. my $res = $sth->{NAME};
  352. $sth->finish;
  353. my %hash = ();
  354. foreach my $column (@$res) {
  355. next if($column eq "uniqueID");
  356. my ($count) = $dbh->selectrow_array("SELECT count($column) FROM $table WHERE $column != 0");
  357. my ($sum) = $dbh->selectrow_array("SELECT sum($column) FROM $table");
  358. $hash{$column}{count} = $count;
  359. $hash{$column}{sum} = $sum;
  360. }
  361. my $data;
  362. my $i=0;
  363. foreach my $column (sort {$hash{$b}{count} <=> $hash{$a}{count}} keys %hash) {
  364. $data .= "\t['$column',$hash{$column}{count},$hash{$column}{sum}],\n";
  365. $i++;
  366. last if($i == 20);
  367. }
  368. chop($data);
  369. my $code =<<END;
  370. google.setOnLoadCallback(drawChart_$postfix);
  371. function drawChart_$postfix() {
  372. var data = google.visualization.arrayToDataTable([
  373. ['Module','$row1title','$row2title'],
  374. $data
  375. ]);
  376. var options = {
  377. height : $height,
  378. width : $width,
  379. chartArea : {left:150,top:20,width:"65%",height:"90%"},
  380. // title : 'title',
  381. };
  382. var chart = new google.visualization.BarChart(document.getElementById('$divID'));
  383. chart.draw(data, options);
  384. };
  385. END
  386. return $code;
  387. }
  388. sub drawMarkersMap(@) {
  389. my ($table,$column,$region,$divID) = @_;
  390. my $res = $dbh->selectall_arrayref("SELECT DISTINCT $column FROM $table as t join nodes as n on t.uniqueID=n.uniqueID WHERE n.lastseen > $limit ORDER BY $column ASC");
  391. my %hash = ();
  392. foreach my $row (@$res) {
  393. my ($value) = @$row;
  394. $value =~ s/\'/_/g;
  395. my ($count) = $dbh->selectrow_array("SELECT COUNT(*) FROM $table as t join nodes as n on t.uniqueID=n.uniqueID WHERE $column = '$value' AND n.lastseen > $limit");
  396. #$value = "Germany" if($value eq "");
  397. next if($value eq "");
  398. $hash{$value} = $count;
  399. }
  400. my $addRows;
  401. foreach my $value (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
  402. $addRows .= "\t['$value',$hash{$value}],\n";
  403. }
  404. chop($addRows);
  405. my $code=<<END;
  406. google.setOnLoadCallback(drawMarkersMap_$region);
  407. function drawMarkersMap_$region() {
  408. var data = google.visualization.arrayToDataTable([
  409. ['City','Installations'],
  410. $addRows
  411. ]);
  412. var options = {
  413. region: '$region',
  414. displayMode: 'markers',
  415. colorAxis: {colors: ['gold', 'darkgreen']},
  416. backgroundColor : 'lightblue',
  417. };
  418. var chart = new google.visualization.GeoChart(document.getElementById('$divID'));
  419. chart.draw(data, options);
  420. };
  421. END
  422. return $code;
  423. }
  424. sub drawRegionsMap(@) {
  425. my ($table,$column,$region,$divID) = @_;
  426. my $res = $dbh->selectall_arrayref("SELECT DISTINCT $column FROM $table as t join nodes as n on t.uniqueID=n.uniqueID WHERE n.lastseen > $limit ORDER BY $column ASC");
  427. my %hash = ();
  428. foreach my $row (@$res) {
  429. my ($value) = @$row;
  430. my ($count) = $dbh->selectrow_array("SELECT COUNT(*) FROM $table as t join nodes as n on t.uniqueID=n.uniqueID WHERE $column = '$value' AND n.lastseen > $limit");
  431. $hash{$value} = $count;
  432. }
  433. my $addRows;
  434. foreach my $value (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
  435. $addRows .= "\t['$value',$hash{$value}],\n";
  436. }
  437. chop($addRows);
  438. my $code=<<END;
  439. google.setOnLoadCallback(drawRegionsMap_$region);
  440. function drawRegionsMap_$region() {
  441. var data = google.visualization.arrayToDataTable([
  442. ['Country','Installations'],
  443. $addRows
  444. ]);
  445. var options = {
  446. region: '$region',
  447. // colorAxis: {colors: ['#FFFF80', 'darkgreen']},
  448. backgroundColor : 'lightblue',
  449. };
  450. var chart = new google.visualization.GeoChart(document.getElementById('$divID'));
  451. chart.draw(data, options);
  452. };
  453. END
  454. return $code;
  455. }
  456. sub drawTable2cols(@) {
  457. my ($table,$postfix,$type1,$title1,$type2,$title2,$divID) = @_;
  458. $sth = $dbh->prepare("SELECT * FROM $table where 1=0");
  459. $sth->execute();
  460. my $res = $sth->{NAME};
  461. $sth->finish;
  462. my %hash = ();
  463. foreach my $column (@$res) {
  464. next if($column =~ m/["']/);
  465. my ($sum) = $dbh->selectrow_array("SELECT sum(\"$column\") FROM $table");
  466. $hash{$column} = $sum;
  467. }
  468. my $data;
  469. if($type2 eq "boolean") {
  470. foreach my $column (sort keys %hash) {
  471. next if($column eq "uniqueID");
  472. $data .= "\t['$column',true],\n";
  473. }
  474. } else {
  475. foreach my $column (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
  476. next if($column eq "uniqueID");
  477. $data .= "\t['$column',$hash{$column}],\n";
  478. }
  479. }
  480. chop($data);
  481. my $code=<<END;
  482. google.setOnLoadCallback(drawTable_$postfix);
  483. function drawTable_$postfix() {
  484. var data = new google.visualization.DataTable();
  485. data.addColumn('$type1', '$title1');
  486. data.addColumn('$type2', '$title2');
  487. data.addRows([
  488. $data
  489. ]);
  490. var options = {
  491. showRowNumber : false,
  492. sortAscending : true,
  493. sortColumn : 0,
  494. height : 400,
  495. };
  496. var table = new google.visualization.Table(document.getElementById('$divID'));
  497. table.draw(data,options);
  498. };
  499. END
  500. return $code;
  501. }
  502. sub drawTable3cols(@) {
  503. my ($table,$postfix,$type1,$title1,$type2,$title2,$type3,$title3,$divID) = @_;
  504. ###################
  505. # Results in "Internal Error 500", and 1und1 will not give me an error-log
  506. # extract.
  507. ###################
  508. # $sth = $dbh->prepare("SELECT * FROM $table where 1=0");
  509. # $sth->execute();
  510. # my $res = $sth->{NAME};
  511. # $sth->finish;
  512. #
  513. # my %hash = ();
  514. #
  515. # foreach my $column (@$res) {
  516. # my ($count) = $dbh->selectrow_array("SELECT count(\"$column\") FROM $table WHERE \"$column\" != 0");
  517. # my ($sum) = $dbh->selectrow_array("SELECT sum(\"$column\") FROM $table");
  518. # $hash{$column}{count} = $count;
  519. # $hash{$column}{sum} = $sum;
  520. # }
  521. $sth = $dbh->prepare("SELECT * FROM $table");
  522. $sth->execute();
  523. my %hash;
  524. while(my $h = $sth->fetchrow_hashref) {
  525. foreach my $k (keys %{$h}) {
  526. next if($k eq "uniqueID");
  527. $hash{$k}{count}++ if($h->{$k});
  528. $hash{$k}{sum} += $h->{$k} if($h->{$k});
  529. }
  530. }
  531. $sth->finish;
  532. my $data;
  533. foreach my $column (sort {$hash{$b} <=> $hash{$a}} keys %hash) {
  534. next if($column eq "uniqueID");
  535. $data .= "\t['$column',$hash{$column}{count},$hash{$column}{sum}],\n";
  536. }
  537. chop($data);
  538. my $code=<<END;
  539. google.setOnLoadCallback(drawTable_$postfix);
  540. function drawTable_$postfix() {
  541. var data = new google.visualization.DataTable();
  542. data.addColumn('$type1', '$title1');
  543. data.addColumn('$type2', '$title2');
  544. data.addColumn('$type3', '$title3');
  545. data.addRows([
  546. $data
  547. ]);
  548. var options = {
  549. showRowNumber : false,
  550. sortAscending : false,
  551. sortColumn : 1,
  552. height : 400,
  553. };
  554. var table = new google.visualization.Table(document.getElementById('$divID'));
  555. table.draw(data,options);
  556. };
  557. END
  558. return $code;
  559. }
  560. sub createDB() {
  561. my $dbh = DBI->connect($dsn,"","", { RaiseError => 1, ShowErrorStatement => 1 }) ||
  562. die "Cannot connect: $DBI::errstr";
  563. $dbh->do("CREATE TABLE db (created TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
  564. $dbh->do("CREATE TABLE nodes (uniqueID VARCHAR(32) PRIMARY KEY UNIQUE, release VARCHAR(16), branch VARCHAR(32), os VARCHAR(32), arch VARCHAR(64), perl VARCHAR(16), lastSeen TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
  565. $dbh->do("CREATE TABLE locations (uniqueID VARCHAR(32) PRIMARY KEY UNIQUE, countrycode VARCHAR(2), countrycode3 VARCHAR(3), countryname VARCHAR(64), region CHAR(2) ,regionname VARCHAR(64), city VARCHAR(255), latitude FLOAT(8,6), longitude FLOAT(8,6), timezone VARCHAR(64), continentcode CHAR(2))");
  566. $dbh->do("CREATE TABLE modules (uniqueID VARCHAR(32) PRIMARY KEY UNIQUE)");
  567. $dbh->do("CREATE TABLE models (uniqueID VARCHAR(32) PRIMARY KEY UNIQUE)");
  568. $dbh->do("INSERT INTO db (created) VALUES (CURRENT_TIMESTAMP)");
  569. $dbh->disconnect();
  570. return;
  571. }
  572. sub insertDB() {
  573. my $uniqueID = $data{uniqueID};
  574. my $system = $data{system};
  575. my $modules = $data{modules};
  576. my $models = $data{models};
  577. my $sth;
  578. # fill second database table
  579. $dbh->do("CREATE TABLE IF NOT EXISTS nodes2 (uniqueID VARCHAR(32) PRIMARY KEY UNIQUE, ip VARCHAR(64), system BLOB, modules BLOB, models BLOB, lastSeen TIMESTAMP DEFAULT CURRENT_TIMESTAMP)");
  580. $sth = $dbh->prepare(q{REPLACE INTO nodes2 (uniqueID,ip,system,modules,models,lastSeen) VALUES(?,?,?,?,?,CURRENT_TIMESTAMP)});
  581. $sth->execute($uniqueID,$ip,$system,$modules,$models);
  582. # insert or update fhem node
  583. my ($release,$branch,$os,$arch,$perl);
  584. foreach (split(/\|/,$data{system})) {
  585. my ($k,$v) = split /:/;
  586. $release = $v if($k eq "Release");
  587. $branch = $v if($k eq "Branch");
  588. $os = $v if($k eq "OS");
  589. $arch = $v if($k eq "Arch");
  590. $perl = $v if($k eq "Perl");
  591. }
  592. $sth = $dbh->prepare(q{REPLACE INTO nodes (uniqueID,release,branch,os,arch,perl,lastSeen) VALUES(?,?,?,?,?,?,CURRENT_TIMESTAMP)});
  593. $sth->execute($uniqueID,$release,$branch,$os,$arch,$perl);
  594. # insert or update geo location of fhem node
  595. #### TODO: sprachcode 84.191.75.195
  596. my @geo = getLocation($ip);
  597. if(@geo) {
  598. $sth = $dbh->prepare(q{REPLACE INTO locations (uniqueID,countrycode,countrycode3,countryname,region,regionname,city,latitude,longitude,timezone,continentcode) VALUES(?,?,?,?,?,?,?,?,?,?,?)});
  599. $sth->execute($uniqueID,$geo[0],$geo[1],$geo[2],$geo[3],$geo[4],$geo[5],$geo[6],$geo[7],$geo[8],$geo[9]);
  600. }
  601. # delete old modules of fhem node
  602. $sth = $dbh->prepare(q{DELETE FROM modules WHERE uniqueID=?});
  603. $sth->execute($uniqueID);
  604. # insert new modules of fhem node
  605. $sth = $dbh->prepare("INSERT INTO modules (uniqueID) VALUES (?)");
  606. $sth->execute($uniqueID);
  607. foreach (split(/\|/,$data{modules})) {
  608. my ($k,$v) = split /:/;
  609. checkColumn("modules",$k);
  610. $sth = $dbh->prepare("UPDATE modules SET '$k'='$v' WHERE uniqueID='$uniqueID'");
  611. $sth->execute();
  612. }
  613. if($data{models}) {
  614. # delete old models of fhem node
  615. $sth = $dbh->prepare(q{DELETE FROM models WHERE uniqueID=?});
  616. $sth->execute($uniqueID);
  617. # insert new models of fhem node
  618. $sth = $dbh->prepare("INSERT INTO models (uniqueID) VALUES (?)");
  619. $sth->execute($uniqueID);
  620. foreach (split(/\|/,$data{models})) {
  621. my @models = split /,/;
  622. foreach my $m (@models) {
  623. checkColumn("models",$m);
  624. $sth = $dbh->prepare("UPDATE models SET '$m'='1' WHERE uniqueID='$uniqueID'");
  625. $sth->execute();
  626. }
  627. }
  628. }
  629. $sth->finish();
  630. $dbh->disconnect();
  631. return;
  632. }
  633. sub checkColumn($$) {
  634. my ($t,$k) = @_;
  635. # get table info
  636. my %column = %{ $dbh->column_info(undef, undef,$t, undef)->fetchall_hashref('COLUMN_NAME') };
  637. # check if column exists
  638. my $found;
  639. foreach my $col (keys %column) {
  640. if(lc($col) eq lc($k)) {
  641. $found = 1;
  642. last;
  643. }
  644. }
  645. if(!$found) {
  646. Log "Adding column >$k<";
  647. $sth = $dbh->prepare("ALTER TABLE $t ADD COLUMN '$k' INTEGER DEFAULT 0");
  648. $sth->execute();
  649. $sth->finish;
  650. Log "..Add ok";
  651. }
  652. return;
  653. }
  654. sub getLocation($) {
  655. my ($ip) = shift;
  656. my $gi = Geo::IP->open($geoIPDat, GEOIP_STANDARD);
  657. my $rec = $gi->record_by_addr($ip);
  658. if(!$rec) {
  659. return;
  660. } else {
  661. return (
  662. $rec->country_code,$rec->country_code3,$rec->country_name,$rec->region,$rec->region_name,$rec->city,
  663. $rec->latitude,$rec->longitude,$rec->time_zone,$rec->continent_code
  664. );
  665. }
  666. }
  667. 1;