cronでgunzipを実行する

crontabでgunzipと書くだけです。

20 * * * * root cd /home/vagrant/cookie/cron; gunzip GeoLite2-Country.mmdb.gz

ほお~

sudo cat /var/log/cron
Apr 26 22:20:02 localhost CROND[17108]: (root) CMD (cd /home/vagrant/cookie/cron; gunzip GeoLite2-Country.mmdb.gz)

無償版は毎月3日に更新されるそう(真偽は不明です)なので、日本との時差を考慮して、毎月5日の1時15分にwgetして、1時20分にgunzipするよう設定します。

15 1 5 * * root cd /home/vagrant/cookie/cron; /usr/bin/wget "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz"
15 1 5 * * root cd /home/vagrant/cookie/cron; /usr/bin/wget "http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz"
20 1 5 * * root cd /home/vagrant/cookie/cron; gunzip GeoLite2-Country.mmdb.gz
20 1 5 * * root cd /home/vagrant/cookie/cron; gunzip GeoLite2-City.mmdb.gz

geolite側はこれでOK
次はmongodbに入れる前に、phpでipから、continent, country, cityを取得して配列にpushするところをやりたいと思います。

cronでwgetを実行する

wget:ノンインタラクティブなダウンローダー

wgetがどこにあるか確認します。

/usr/bin/wget

bin:Binary codeのこと、実行可能プログラム置き場
/bin:FHSによって定められたシングルユーザを含めたどのユーザでも使えるコマンド
/usr/bin:ユーザーが一般的に使用するコマンド

続いて、crontabを編集します。

sudo vi /etc/crontab

任意のディレクトリ(ここでは/home/vagrant/cookie/cron)に、wgetでdownloadしたいとすると、まず、cdで移動する必要があります。そのため、以下のように書きます。
root cd で移動して、/usr/bin/wgetで、”http://hogehoge”をダウンロード
※ここでは、テスト用に毎時55分にwgetする

55 * * * * root cd /home/vagrant/cookie/cron; /usr/bin/wget "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz"
/etc/rc.d/init.d/crond start

編集後、crond start
/etc/rc.d/init.d/crond start

結果
->取れてる!!!

sudo cat /var/log/cron でログも確認

Apr 26 21:55:01 localhost CROND[17001]: (root) CMD (cd /home/vagrant/cookie/cron; /usr/bin/wget "http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz")

OK牧場!

ipから、continent, country, cityを取得

encodeしてdecodeします。

$reader = new Reader('GeoLite2-City.mmdb');
$ip_addr = '182.22.59.229';
$record = json_decode(json_encode($reader->city($ip_addr)));



echo $record->continent->names->en ."<br>";
echo $record->country->names->en ."<br>";
echo $record->city->names->en ."<br>";

首相官邸
https://www.kantei.go.jp/
202.214.216.10
Asia
Japan
Komagatani
大阪府羽曳野市駒ケ谷?

white house
https://www.whitehouse.gov/
North America
United States
Cambridge

おもろい!続けて..

ペニンシュラ
peninsula.com
North America
United States
cityがnullの場合もあるようです。
ってか、アメリカかい!

GeoIP2でipアドレスの住所判定

GeoLite2 CountryがIPアドレスから国を取得するデータベース、GeoLite2 CityがIPアドレスから地域を取得するデータベース

# wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-Country.mmdb.gz
# gunzip GeoLite2-Country.mmdb.gz
# wget http://geolite.maxmind.com/download/geoip/database/GeoLite2-City.mmdb.gz
# gunzip GeoLite2-City.mmdb.gz

mmdbはインメモリデータベース
gunzip:圧縮された圧縮ファイルを解凍する

composerを入れてGeoIP2 PHP APIをインストール

$ curl -sS https://getcomposer.org/installer | php
composer require geoip2/geoip2:~2.0

<?php
require_once 'vendor/autoload.php';
use GeoIp2\Database\Reader;

$reader = new Reader('GeoLite2-City.mmdb');
$ip_addr = '182.22.59.229';
$record = $reader->city($ip_addr);

print_r("<pre>");
var_dump($record);
print_r("</pre>");
?>

ドイツ語、英語、スペイン語、フランス語、日本語、ポルトガル語、ロシア語、中国語で出力されます。
[“record”:”GeoIp2\Record\AbstractRecord”:private]=>
array(2) {
[“geoname_id”]=>
int(1850147)
[“names”]=>
array(8) {
[“de”]=>
string(5) “Tokio”
[“en”]=>
string(5) “Tokyo”
[“es”]=>
string(5) “Tokio”
[“fr”]=>
string(5) “Tokyo”
[“ja”]=>
string(6) “東京”
[“pt-BR”]=>
string(7) “Tóquio”
[“ru”]=>
string(10) “Токио”
[“zh-CN”]=>
string(6) “东京”
}
}

1.エリア、国、都市を取得
2.wget・gunzipをcronで実行できるか確認が必要
3.クライアントリクエストからの負担を軽くするため、都市の抽出はmongoDBに入れる前にphp側で実行したい

anlyticsを設計していく2

必要項目: language, country, city, browser, operating system, service provider, operating system, service provider, screen resolution

例えば、countryをクリックすると、GetパラメーターにselectedDimensionが追加される。
&overview-dimensionSummary.selectedDimension=analytics.country/

Language – Country – City はIP情報か?
IP情報は、$_SERVER[‘REMOTE_ADDR’];で取得できるとして、それをどう紐づけるかが問題。。。

>IPアドレスの割り振りはIANAにが一元管理しており、世界レベルで階層構造(組織)があり、階層ごとにそれぞれ管理が行われています。(IPv4,IPv6)
なんだと???????

IANA(ICANN):全IPアドレスを地域レジストリや一部企業に割り当てしている
地域レジストリ:全世界を5ブロックに分け、地域レジストリを配置している。地域レジストリは、IANAより割り当てされたIPアドレスを国別レジストリに再割り当てしている。また、WHOIS情報の管理も行っている。
国別レジストリ:国や地域でレジストリが配置されている。(一部は企業もあり)地域レジストリより割り当てられたIPアドレスをプロバイダや企業に再割り当てしている。またWHOIS情報の管理も行っている。

IPv4 Address Space Registry
http://www.iana.org/assignments/ipv4-address-space/ipv4-address-space.xhtml
Internet Protocol Version 6 Address Space
https://www.iana.org/assignments/ipv6-address-space/ipv6-address-space.xhtml

うお、吐き気がしてきた。。
000 IANA – Reserved(予約済み) whois.arin.net
001 APNIC アジア/太平洋圏 whois.apnic.net
002 RIPE NCC ヨーロッパ、中東、中央アジア whois.ripe.net
003 General Electric Company ゼネラル・エレトリック社(アメリカ) whois.arin.net
004 Level3 Communications Inc. レベル3コミュニケーションズ社(アメリカ) whois.arin.net
005 RIPE NCC ヨーロッパ、中東、中央アジア whois.ripe.net
006 Army Information Systems Center 軍情報システムセンター whois.arin.net
007 ARIN 北アメリカおよびサブサハラ等 whois.arin.net
008 Level3 Communications Inc. レベル3コミュニケーションズ社(アメリカ) whois.arin.net
009 IBM アイ・ビー・エム社(IBM社) whois.arin.net
010 IANA – Private Use See [RFC1918] ローカルIPアドレス whois.arin.net ….

現在のIPアドレス:106.***.***.***
->106 APNIC アジア/太平洋圏 whois.apnic.net
そうですか。。。

というか、USのdefence forceばっかりだ。。やばいな、これ。
Army Information Systems Center、DoD Intel Information Systems、DDN-RVN

あれ、そもそも、apnicって何?
https://www.apnic.net/

ip情報は$_SERVER[‘REMOTE_ADDR’]で渡しているので、上3桁を切り抜きたいが。。

dateのfrom – toをgetパラメーターで送る

パラメーターのnameはGA同様、_u.date00、_u.date01にします。

<?php
$date1 = date("Y-m-d", strtotime("-50 days"));
$date2 = date("Y-m-d");

?>
<style>
input{
	width:90px;
}
</style>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/i18n/jquery.ui.datepicker-ja.min.js"></script>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/redmond/jquery-ui.css" >

<form method="GET" name="form" action="">
<input name="_u.date00" type="text" id="datepicker" value="<?php echo $date1; ?>"> ~ <input name="_u.date01" type="text" id="datepicker2" value="<?php echo $date2; ?>">
<input type="submit" class="submit">
</form>
<script>
  var dateFormat = 'yy-mm-dd';
  $(function() {
    $("#datepicker").datepicker({
    	dateFormat: dateFormat,
    	maxDate:0,
    	onClose: function(dateText, inst){
    		$('#datepicker2').datepicker('option', 'minDate', dateText);
    		$('#datepicker').val(dateText);
    	}
    });
    $("#datepicker2").datepicker({
    	dateFormat: dateFormat,
    	maxDate:0,
    	onClose: function(dateText, inst){
    		$('#datepicker').datepicker('option', 'maxDate', dateText);
    		$('#datepicker2').val(dateText);
    	}
    });
  });
</script>

送れています。

つなげると、、、

mongodbのfilterで’date’ => [ ‘$gte’ => $date1, ‘$lte’ => $date2]とすると、
date2が00:00:00で算出してしまうので、以下のように、Getパラメーター取得時は、その日の23:59:59にします。

if(isset($_GET['_udate00'])&&isset($_GET['_udate01'])) {
  $date1 = $_GET['_udate00'];
  $date2 = $_GET['_udate01'] . " 23:59:59";
} else {
  $date1 = date("Y-m-d", strtotime("-10 day"));
  $date2 = date("Y-m-d H:i:s");
}

次はarray_count_valuesでアクセスされたページのテーブル表示です。

あれ、google見ると、ページごとにPageviews、Unique Pageviews、Avg. Time on Page 、Entrances、Bounce Rateがある。。。まじかよ。。。

datepicker

  var dateFormat = 'yy-mm-dd';
  var form = document.getElementById('datepicker').value;
  $(function() {
    $("#datepicker").datepicker({
    	onClose: function(dateText, inst){
    		$('#datepicker2').datepicker('option', 'minDate', dateText);
    		$('#datepicker').val(dateText);
    	}
    });
    $("#datepicker2").datepicker({
    	onClose: function(dateText, inst){
    		$('#datepicker').datepicker('option', 'maxDate', dateText);
    		$('#datepicker2').val(dateText);
    	}
    });
  });

onClose
->DatePickerが閉じた時に実行
:function( String dateText, Object inst )

1.datepicker1が閉じるときに、datepicker2のminDateをdateTextにして、datapickerのvalをdateTextにする
2.datepicker2が閉じるときに、datepicker2のminDateをdateTextにして、datapickerのvalをdateTextにする

今日以前の日付にする

  var dateFormat = 'yy-mm-dd';
  var form = document.getElementById('datepicker').value;
  $(function() {
    $("#datepicker").datepicker({
    	dateFormat: dateFormat,
    	maxDate:0,
    	onClose: function(dateText, inst){
    		$('#datepicker2').datepicker('option', 'minDate', dateText);
    		$('#datepicker').val(dateText);
    	}
    });
    $("#datepicker2").datepicker({
    	dateFormat: dateFormat,
    	maxDate:0,
    	onClose: function(dateText, inst){
    		$('#datepicker').datepicker('option', 'maxDate', dateText);
    		$('#datepicker2').val(dateText);
    	}
    });
  });

来た!
今日以前の日付で、datapicker1のdateTextがdatapicker2のminDate, datapicker2のdateTextがdatapicker2のmaxDateになる。

datepickerを使う

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/i18n/jquery.ui.datepicker-ja.min.js"></script>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/redmond/jquery-ui.css" >
<input type="text" id="datepicker">
<script>
  $(function() {
    $("#datepicker").datepicker();
  });
</script>

このように書くと、下のフォームは動きません。

<input type="text" id="datepicker"><br>
<input type="text" id="datepicker">
<script>
  $(function() {
    $("#datepicker").datepicker();
  });
</script>

idを変えると両方動きます。

<input type="text" id="datepicker"> ~ <input type="text" id="datepicker2">
<script>
  $(function() {
    $("#datepicker").datepicker();
  });
  $(function() {
    $("#datepicker2").datepicker();
  });
</script>

今日と10日前の初期値を入れます。

<?php
$date1 = date("Y/m/d", strtotime("-10 days"));
$date2 = date("Y/m/d");

?>
<style>
input{
	width:90px;
}
</style>

<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script>
<script src="http://ajax.googleapis.com/ajax/libs/jqueryui/1/i18n/jquery.ui.datepicker-ja.min.js"></script>
<link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/redmond/jquery-ui.css" >
<input type="text" id="datepicker" value="<?php echo $date1; ?>"> ~ <input type="text" id="datepicker2" value="<?php echo $date2; ?>">
<script>
  $(function() {
    $("#datepicker").datepicker();
  });
  $(function() {
    $("#datepicker2").datepicker();
  });
</script>

あれ、割と簡単に出来た

Y-m-dにして、date1、date2をgetで渡して、mongodbで読み取ればOKだと思います。

時間別のPVをchart.jsで表示する

全て繋げます。

<?php

// $date1 = date("Y-m-d H:i:s", strtotime("- 6 day"));
$date1 = date("Y-m-d", strtotime("-10 day"));
// $date2 = date("Y-m-d H:i:s", strtotime("- 1 day"));
$date2 = date("Y-m-d H:i:s");


$mng = new MongoDB\Driver\Manager("mongodb://localhost:27017");
$filter =  &#91;
	'date' => [ '$gte' => $date1, '$lte' => $date2]
];
$options = [
  'projection' => ['_id' => 0],
  'sort' => ['_id' => -1],
];
$query = new MongoDB\Driver\Query($filter, $options);
$cursor = $mng->executeQuery('app.na007', $query);

foreach ($cursor as $document) {
  // var_dump($document);
  $pv[] = $document->date;
  $cookie[] = $document->cookie;
  $session[] = $document->session;
  if(!is_null($document->referrer)){
  $avgtime[] = $document->avgtime;
  }
  if(!is_null($document->referrer)){
  $referrer[] = $document->referrer;
  }
  $visit[] = $document->visit;
  $access[] = substr($document->date, 11, 2);
}
$visit = json_encode(array_count_values($visit));
$line = array("00"=>0,"01"=>0, "02"=>0,"03"=>0,"04"=>0,"05"=>0,"06"=>0,"07"=>0,"08"=>0,"09"=>0,"10"=>0,"11"=>0,"12"=>0,"13"=>0,"14"=>0,"15"=>0,"16"=>0,"17"=>0,"18"=>0,"19"=>0,"20"=>0,"21"=>0,"22"=>0,"23"=>0);
foreach($access as $value){switch($value){case '00':$line['00'] ++;break;case '01':$line['01'] ++;break;case '02':$line['02'] ++;break;case '03':$line['03'] ++;break;case '04':$line['04'] ++;break;case '05':$line['05'] ++;break;case '06':$line['06'] ++;break;case '07':$line['07'] ++;break;case '08':$line['08'] ++;break;case '08':$line['08'] ++;break;case '10':$line['10'] ++;break;case '11':$line['11'] ++;break;case '12':$line['12'] ++;break;case '13':$line['13'] ++;break;case '14':$line['14'] ++;break;case '15':$line['15'] ++;break;case '16':$line['16'] ++;break;case '17':$line['17'] ++;break;case '18':$line['18'] ++;break;case '19':$line['19'] ++;break;case '20':$line['20'] ++;break;case '21':$line['21'] ++;break;case '22':$line['22'] ++;break;case '23':$line['23'] ++;break;}}

$line_list = json_encode($line);
$uu = array_unique($cookie);
$session = array_unique($session);
$bounce = (count($pv) - count($referrer))/ count($pv) * 100;
$exit = (count($pv) - count($session))/ count($pv) * 100;
$avgtime = array_sum($avgtime) / count($avgtime);
$time = s2h($avgtime);
function s2h($avgtime){
  $hours = floor($avgtime / 3600);
  $minutes = floor(($avgtime / 60)%60);
  $seconds = $avgtime % 60;
  $hms = sprintf("%02d:%02d:%02d", $hours, $minutes, $seconds);
  return $hms; 
}
?>
<style>
.flex-container {
  display: flex;
}
.flex-1{
  flex:1;
}
.flex-2{
  flex:4;
}
#pie {
 height:190;
 width:190;
}
#line {
 height:200;
 width:750;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/2.1.4/Chart.min.js"></script>
<h1>アナリティクス</h1>
<div class="flex-container">
<div class="flex-1">
  <ul>
    <li><a href="">Audience</a></li>
    <li><a href="">Acuisition</a></li>
    <li><a href="">Behavior</a></li>
  </ul>
</div>
<div class="flex-2">
<?php echo $date1 ." ~ ".$date2 ."<br><br>"; ?>
<div id="line">
<canvas id="myLine" width="750" height="200"></canvas>
</div>
<div id="pie">
<canvas id="myChart" width="200" height="200"></canvas>
</div>
<?php
echo "<br>Users: ".count($uu)."<br>";
echo "Session: ".count($session)."<br>";
echo "Pageviews: ".count($pv)."<br>";
echo "avg time: " .$time."<br>";
echo "Bounce rate :" .sprintf('%.2f',$bounce)."%<br>";
echo "Exit rate:" .sprintf('%.2f',$exit)."%<br>";
?>
</div>
</div>

<script>
  var visit = JSON.parse('<?php echo $visit; ?>');
  console.log(visit);
  var ctx = document.getElementById("myChart").getContext('2d');
  var myChart = new Chart(ctx, {
  type: 'pie',
  data: {
    labels: ["New", "Returned"],
    datasets: [{
      backgroundColor: [
        "#2ecc71",
        "#3498db",
      ],
      data: [visit['New User'],visit['Returned User']]
    }]
  }
});
var line = JSON.parse('<?php echo $line_list; ?>');
console.log(line);
var ctx2 = document.getElementById("myLine").getContext('2d');
var myLine = new Chart(ctx2, {
   type: 'line',
   data: {
      labels: ["00", "01", "02", "03", "04", "05", "06", "07", "08", "09", "10", "11", "12","13","14","15","16","17","18","19","20","21","22","23","24"],
      datasets: [
      {
         label: "時間別Page View",
         borderColor: 'rgb(0, 153, 255)',
         backgroundColor: "#D9E5FF",
         lineTension: 0,
         data: [line['00'], line['01'],line['02'],line['03'],line['04'],line['05'],line['06'],line['06'],line['07'],line['08'],line['09'],line['10'],line['11'],line['12'],line['13'],line['14'],line['15'],line['16'],line['17'],line['18'],line['19'],line['20'],line['21'],line['22'],line['23']],
      },
      ]
   },
   options: {
      responsive: true,
      scales: {
        yAxes: [{
          ticks: {
            // max: Math.max.apply(null, line)*1.2,
          }
        }]
      }
   }
});
</script>

う~ん、なんだろう、この何とも言い難い感じは。。。

1.各指標はボックスレイアウトで表示したい。
2.日付は選択式にしたい
-> gaだと、日付選択は、GETパラメーター使ってますね。
_u.date00=20180420&_u.date01=20180423/

cssをつける

そんなに悪くないような気がしないでもない。

時間ごとの配列をつくる

絶対、もっとうまく書ける筈なんだが。。。

foreach ($cursor as $document) {
  // var_dump($document);
  $pv[] = $document->date;
  $cookie[] = $document->cookie;
  $session[] = $document->session;
  if(!is_null($document->referrer)){
  $avgtime[] = $document->avgtime;
  }
  if(!is_null($document->referrer)){
  $referrer[] = $document->referrer;
  }
  $visit[] = $document->visit;
  $access[] = substr($document->date, 11, 2);
}

$line = array("00"=>0,"01"=>0, "02"=>0,"03"=>0,"04"=>0,"05"=>0,"06"=>0,"07"=>0,"08"=>0,"09"=>0,"10"=>0,"11"=>0,"12"=>0,"13"=>0,"14"=>0,"15"=>0,"16"=>0,"17"=>0,"18"=>0,"19"=>0,"20"=>0,"21"=>0,"22"=>0,"23"=>0);
foreach($access as $value){switch($value){case '00':$line['00'] ++;break;case '01':$line['01'] ++;break;case '02':$line['02'] ++;break;case '03':$line['03'] ++;break;case '04':$line['04'] ++;break;case '05':$line['05'] ++;break;case '06':$line['06'] ++;break;case '07':$line['07'] ++;break;case '08':$line['08'] ++;break;case '08':$line['08'] ++;break;case '10':$line['10'] ++;break;case '11':$line['11'] ++;break;case '12':$line['12'] ++;break;case '13':$line['13'] ++;break;case '14':$line['14'] ++;break;case '15':$line['15'] ++;break;case '16':$line['16'] ++;break;case '17':$line['17'] ++;break;case '18':$line['18'] ++;break;case '19':$line['19'] ++;break;case '20':$line['20'] ++;break;case '21':$line['21'] ++;break;case '22':$line['22'] ++;break;case '23':$line['23'] ++;break;}}
var_dump($line);

悔しいですね。