Google chartlive
Материал из Dom.
[править] Проект Google chartlive - некрасивые графики посещаемости вашего сайта малой кровью
Насмотревшись на "невероятную" посещаемость моего сайта в google analytics (черт, как мне нравится эта штука!) я решил создать сервис-надстройку над google analytics. К сожалению google не представляет никакого api для доступа к своим отчетам. Не понятно будет ли когда либо предприняты шаги в этом направлении. По крайней мере в интернете я нашел пару решений, которые имитировали действия пользователя на сайте и "как-будто-бы-это-сам-пользователь" заходит в свой рабочий кабинет и жмет кнопку "Экспортировать даннные". Я попробовал одно из таких творений (greqo-analytics-alpha, от Tom-а Klenwell-а). Увы и ах, но время работы этого скрипта было порядка 40 с гаком секунд, несерьезно. У меня на этом бесплатном хостинге лимит в сего 4 секунды, можно было бы разместить скрипт где-нибудь из множества дружественных мне сайтов (задание для cron, которое бы выкачивало бы аналитику один раз в день), но потом я решил что это решение не для настоящего кулхацкера и сейчас я думаю над решением этой проблемы. Как только я найду приемлимое решение, то обязательно сюда сообщу. А пока решение только частичное.
В любом случае я написал и предлагаю вам попользоваться следующим скриптом:
В шаблон mediawiki я добавил подключение следующих файлов:
<link rel="stylesheet" type="text/css" href="/mediawiki/extensions/chartvisits/st.css" /> <script src="/mediawiki/extensions/chartvisits/charts_splash.js" > </script>
Затем где то там в теле страницы я разместил маленький баннер-плейсхолдер:
<div id="p-search" class="portlet"> <h5 id="heading_line_visitors">Visitors Actiity</h5> <div class="pBody"> <a href="javascript:make_toggle_big_chart()"> <img border="0" id="chart_visitors_activity" src="/mediawiki/extensions/chartvisits/google_pars.php" alt="" /> </a> </div> </div>
Ссылка на файл /mediawiki/extensions/chartvisits/google_pars.php - генерирует график - это поведение по-умолчанию, однако все можно изменить. Так указав параметр:
kind = image или xml - мы выбираем формат отчета sparkline = VisitsSparkline или PageviewsSparkline или AvgPageSparkline или TimeOnSiteSparkline или BounceRateSparkline или NewVisitsSparkline или AvgPageviewsSparkline - я указываю тип графика который нужен w - задает ширину графика, но в отрезке от 40 - 800 пиксклей h - задает высоту графика, но в отрезке от 40 - 640 пикселей
Отчеты в формате xml от google analytics я складываю в папку:
/mediawiki/extensions/chartvisits/hist
Всегда при построении графика ищется отчет за последнюю дату
Вот исходный код файла php, который все это строит:
<?php header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past $result_output_type = 'image'; if (isset($_REQUEST['kind'])){ $result_output_type = $_REQUEST['kind']; } if ($result_output_type != 'xml' && $result_output_type != 'image') $result_output_type = 'image'; // ****************************************************** $good_sparklines = array ( 'VisitsSparkline', 'PageviewsSparkline', 'AvgPageSparkline', 'TimeOnSiteSparkline', 'BounceRateSparkline', 'NewVisitsSparkline', 'AvgPageviewsSparkline', ); $used_sparkline = 'VisitsSparkline'; if (isset($_REQUEST['sparkline'])){ $used_sparkline = $_REQUEST['sparkline']; } if (! in_array($used_sparkline, $good_sparklines)) $used_sparkline = 'VisitsSparkline'; // **************************************************************** $PIC_WIDTH = 140; $PIC_HEIGHT = 60; if (isset($_REQUEST['w']) && is_numeric($_REQUEST['w'])){ $PIC_WIDTH = max(40, min(intval($_REQUEST['w']), 800)); } if (isset($_REQUEST['h']) && is_numeric($_REQUEST['h'])){ $PIC_HEIGHT =max(40, min(intval($_REQUEST['h']), 640)); } function dexport ($x){die ('<pre>'.var_export($x, true). '</pre>');} function makeSimpleArrayPlot ($plot_data){ return join('.', $plot_data); } function makeFatality ($kind, $error_type = false, $error_msg = false){ if ($kind == 'image'){ header ('Content-Type: image/png'); $img = imagecreatefrompng(dirname(__FILE__) . '/pics_no_files.png'); imagepng($img); imagedestroy($img); } else{ header('Content-Type: text/xml'); print ('<?xml version="1.0" encoding="windows-1251" ?> <result status="error" error_type="'.$error_type.'"> <error-message><![CDATA['.$error_msg.']]></error-message> </result> '); } die (); } include_once('xml_pars_support.php'); $fname = null; $dir = dirname(__FILE__) . '/hist/'; $dh = opendir($dir); $arr_names = array (); while ( ($file = readdir($dh))) { $fullname = $dir . $file; if ($file == '..' || $file == '.') continue; if (is_dir($full_msg)) continue; $arr_got = array (); if (! preg_match('/Analytics_.+_(\d+)-(\d+)_/i', $file , $arr_got)){ continue; } $arr_names [] = array ('fullname' => $fullname, 'start_range' => $arr_got[1], 'end_range' => $arr_got[2]); } function get_flat_date ($a){ $arr_got = array (); if (preg_match('/^(\d{4})(\d{2})(\d{2})/i', $a, $arr_got)) return mktime(0,0,0, $arr_got[2], $arr_got[3], $arr_got[1]); else return mktime(0,0,0,0,0,0); } function cmp_by_ranges_3454_dsf_234 ($a , $b){ $a = $a ['end_range']; $b = $b ['end_range']; $a = get_flat_date ($a); $b = get_flat_date ($b); if ($a < $b) return -1; if ($a > $b) return +1; return 0; } usort($arr_names, 'cmp_by_ranges_3454_dsf_234'); if (count($arr_names) == 0){ makeFatality ($result_output_type , 'no_files'); } $fname = $arr_names [count($arr_names) - 1]['fullname']; $end_date_range = $arr_names [count($arr_names) - 1]['end_range']; $end_date_range = get_flat_date($end_date_range); $xml = join (file ($fname)); $xml = xml2array ($xml); $sparks = xml2array_getxpath_list ($xml, 'AnalyticsReport/Report/Sparkline'); if (count($sparks) == 0){ makeFatality ($result_output_type, 'bad_file'); } $plot_data = array (); for ($i = 0; $i < count($sparks); $i++){ $spark = $sparks [$i]; if (isset($spark['attributes']) && isset($spark['attributes']['id']) && $spark['attributes']['id'] == $used_sparkline ){ $plot_data_tags = xml2array_getxpath_list ($spark, 'Sparkline/PrimaryValue'); //dexport($plot_data_tags); for ($j = 0; $j < count($plot_data_tags); $j++){ //$plot_data [] = preg_replace('|\..*$|', '', get_elt_array($plot_data_tags[$j], 'text')); $plot_data [] = floatval(get_elt_array($plot_data_tags[$j], 'text')); }// --- for --- break; }// --- if --- }// -- for -- $COUNT_OF_POINTS = 10; $pos_from = 0; if (count($plot_data) > $COUNT_OF_POINTS) $plot_data = array_slice($plot_data, count ($plot_data) - $COUNT_OF_POINTS); $max_plot = max ($plot_data); $min_plot = min ($plot_data); $end_date_range = strtotime('-'.count($plot_data).' day', $end_date_range); if ($result_output_type == 'image'){ $koeff_x = ($PIC_WIDTH-20) / count($plot_data); $koeff_y = ($PIC_HEIGHT-20) / ($max_plot - $min_plot + 2); $img = imagecreatetruecolor($PIC_WIDTH, $PIC_HEIGHT); $background_color = imagecolorallocate($img, 155, 255, 68); $line_color = imagecolorallocate($img, 0, 0, 0); $grid_color = imagecolorallocate($img, 65, 146, 252); imagefilledrectangle($img, 0,0, $PIC_WIDTH, $PIC_HEIGHT, $background_color); $box_outer_x_start = 0; $box_outer_x_end = 0; for ($i = 0; $i < count($plot_data) - 1; $i++){ $x_start = round(15 + $i*$koeff_x); $y_start = $PIC_HEIGHT - round(10 + ($plot_data[$i] - $min_plot)*$koeff_y); $x_end = round(15 + ($i+1)*$koeff_x); $y_end = $PIC_HEIGHT - round(10 + ($plot_data[$i+1]- $min_plot)*$koeff_y); //die('x = ' . $x_start . ' y = ' . $y_start . ' x2 = '. $x_end . ' y2 = ' . $y_end); imagesetthickness($img , 1); imageline($img , $x_start, 0, $x_start, $PIC_HEIGHT, $grid_color); imagesetthickness($img , 2); imageline($img , $x_start, $y_start, $x_end, $y_end, $line_color); imagefilledellipse($img, $x_start, $y_start ,5, 5, $line_color); if ($i == 0){ imagestring($img , 1, $x_start - 10, $y_start - 5 , $plot_data[$i], $line_color); $box_outer_x_start = $x_start; } if ($i == (count($plot_data) - 2)){ imagesetthickness($img , 1); imageline($img , $x_end, 0, $x_end, $PIC_HEIGHT, $grid_color); imagefilledellipse($img, $x_end, $y_end,5, 5, $line_color); imagestring($img , 1, $x_end + 5, $y_end - 5 , $plot_data[$i+1], $line_color); $box_outer_x_end = $x_end; } } $y_start = $PIC_HEIGHT - round(10 + ($min_plot - $min_plot)*$koeff_y); $y_end = $PIC_HEIGHT - round(10 + ($max_plot - $min_plot)*$koeff_y); imagesetthickness($img , 1); imageline($img , $box_outer_x_start, $y_start, $box_outer_x_end, $y_start, $grid_color); imageline($img , $box_outer_x_start, $y_end, $box_outer_x_end, $y_end, $grid_color); setcookie('plot_x', makeSimpleArrayPlot ($plot_data), time() + 3600); header ('Content-Type: image/png'); imagepng($img); imagedestroy($img); } elseif ($result_output_type == 'xml'){ header('Content-Type: text/xml'); print '<?xml version="1.0" encoding="windows-1251" ?>'. "\n"; print '<plot>' . "\n"; for ($i = 0; $i < count($plot_data) ; $i++){ $date_i = date ('Y-m-d', strtotime('+'.($i+1).' day', $end_date_range)); print '<point x="'.$date_i.'" y="'.$plot_data[$i].'" />' . "\n"; } print '</plot>'. "\n"; } ?>
Для работы данному файлу необходима маленькая библиотечка для работы с xml|xpath, т.к. при словосочетании "встроенная поддержка xml в php" меня начинает тошнить, то пришлось взять и сделать небольшую собственную библиотечку выполняющую разбор xml с помощью regexp-ов. В основе своей код был взят на php.net (автора я боюсь сейчас не найду), затем переработана и дополнена.
<?php function get_elt_array ($arr , $name){ return $arr[$name]; } function xml2array ($xml){ $xmlary = array (); $ReElements = '/<(\w+)\s*([^\/>]*)\s*(?:\/>|>(.*?)<(\/\s*\1\s*)>)/s'; $ReAttributes = '/(\w+)=(?:"|\')([^"\']*)(:?"|\')/'; preg_match_all ($ReElements, $xml, $elements); foreach ($elements[1] as $ie => $xx) { $xmlary[$ie]["name"] = $elements[1][$ie]; if ( $attributes = trim($elements[2][$ie])) { preg_match_all ($ReAttributes, $attributes, $att); foreach ($att[1] as $ia => $xx) // all the attributes for current element are added here $xmlary[$ie]["attributes"][$att[1][$ia]] = $att[2][$ia]; } // if $attributes // get text if it's combined with sub elements $cdend = strpos($elements[3][$ie],"<"); if ($cdend > 0) { $xmlary[$ie]["text"] = substr($elements[3][$ie],0,$cdend -1); } // if cdend if (preg_match ($ReElements, $elements[3][$ie])){ $xmlary[$ie]["elements"] = xml2array ($elements[3][$ie]); } else if (isset($elements[3][$ie])){ $xmlary[$ie]["text"] = $elements[3][$ie]; } $xmlary[$ie]["closetag"] = $elements[4][$ie]; } //foreach ? return $xmlary; } function xml2array_getxpath ($xml_array , $xpath_expr){ $elts = explode('/', $xpath_expr); $elts2 = array (); for ($i = 0; $i < count($elts); $i++){ $tmp = trim($elts [$i]); if ($tmp == '') continue; $elts2 [] = $tmp; } if (count($xml_array) != 0 && isset($xml_array[0])) return rec_xml2array_getxpath ($xml_array[0] , $elts2, false); else return rec_xml2array_getxpath ($xml_array , $elts2, false); } function xml2array_getxpath_list ($xml_array , $xpath_expr){ $GLOBALS['tmp_xpath_cached_23432'] = array (); $elts = explode('/', $xpath_expr); $elts2 = array (); for ($i = 0; $i < count($elts); $i++){ $tmp = trim($elts [$i]); if ($tmp == '') continue; $elts2 [] = $tmp; } if (count($xml_array) != 0 && isset($xml_array[0])) rec_xml2array_getxpath ($xml_array[0] , $elts2, 'tmp_xpath_cached_23432'); else rec_xml2array_getxpath ($xml_array , $elts2, 'tmp_xpath_cached_23432'); return $GLOBALS['tmp_xpath_cached_23432']; } function rec_xml2array_getxpath ($xml_array , $xpath_expr_arr, $target_name){ if ($xml_array == null) return false; if (count($xpath_expr_arr) != 0 && count($xml_array) == 0 ) return false; if (count($xpath_expr_arr) == 0) { if ($target_name) $GLOBALS[$target_name] [] = $xml_array; return $xml_array; } if (count($xpath_expr_arr) == 1 && isset($xml_array['name']) && $xml_array['name'] == $xpath_expr_arr[0]){ if ($target_name) $GLOBALS[$target_name] [] = $xml_array; return $xml_array; } $first = array_shift($xpath_expr_arr); if (strpos($first, '@') !== false){ $clear_attrname = substr($first , 1); $attrs = $xml_array ['attributes']; foreach ($attrs as $key => $value){ if ($key == $clear_attrname) { if ($target_name) $GLOBALS[$target_name] [] = $value; return $value; } } return false; } if (strpos($first , '[') !== false){ $arr_got = array (); preg_match('/(.*)\[(d+)\]/' , $first , $arr_got); $idx = -1; for ($i = 0; $i < count ($xml_array); $i++) if ($xml_array [$i]['name'] == $first){ $idx ++; if ($idx == $arr_got [2]){ $rezzz = rec_xml2array_getxpath ($xml_array [$i]['elements'] , $xpath_expr_arr, $target_name); if (! $target_name) return $rezzz; } } } else { if (! isset($xml_array ['name'])){ for ($i = 0; $i < count ($xml_array); $i++){ if (isset($xml_array [$i]['name']) && $xml_array [$i]['name'] == $first){ $rezzz = rec_xml2array_getxpath ($xml_array [$i] , $xpath_expr_arr, $target_name); if (! $target_name) return $rezzz; } } } else{ if ($xml_array ['name'] !== $first) return false; if (count ($xpath_expr_arr)){ if (strpos($xpath_expr_arr[0] , '@') === false){ if (! isset($xml_array ['elements'])) return false; for ($i = 0; $i < count($xml_array ['elements']); $i++){ $rezzz = rec_xml2array_getxpath ($xml_array ['elements'][$i] , $xpath_expr_arr, $target_name); if ($rezzz) { if (! $target_name) return $rezzz; } } } else return rec_xml2array_getxpath ($xml_array , $xpath_expr_arr, $target_name); } else{ if ($target_name) $GLOBALS[$target_name] [] = $xml_array; return $xml_array; } } } return false; } ?>
И теперь пример исходника для javascript-файла который реализует userside (как всегда я использовал http://jquery.com/ ):
var outer_livechart_box = null; $(document).ready(function() { outer_livechart_box = document.createElement('div'); outer_livechart_box.id = 'outer_livechart_box'; outer_livechart_box.className = 'outer_livechart_box'; document.getElementsByTagName ('body')[0].appendChild (outer_livechart_box); outer_livechart_box.innerHTML = '<table border="0" class="tab_livechart_top_row">'+ '<tr><td align="left" valign="top">'+ '<div class="know_more_about_chartlive" ><a href="/mediawiki/index.php/google_chartlive">Узнай больше о проекте ChartLive</a></div>'+ '</td><td align="right" valign="top">'+ '<div id="clickable_chartlive_close_btn" class="clickable_chartlive_close_btn" onclick="toggle_chartlive()">Закрыть/Спрятать</div>'+ '</td></tr></table>'+ '<div id="inner_livechart_box">'+ '<div>Выберите предпочтимый тип графика в списке: '+ '<select name="sparkline" onchange="makeChartsLiveChange(this)">'+ '<option value="VisitsSparkline">VisitsSparkline</option>'+ '<option value="PageviewsSparkline">PageviewsSparkline</option>'+ '<option value="AvgPageSparkline">AvgPageSparkline</option>'+ '<option value="TimeOnSiteSparkline">TimeOnSiteSparkline</option>'+ '<option value="BounceRateSparkline">BounceRateSparkline</option>'+ '<option value="NewVisitsSparkline">NewVisitsSparkline</option>'+ '<option value="AvgPageviewsSparkline">AvgPageviewsSparkline</option>'+ '</select>'+ '</div>'+ '<img id="img_chart_zone" src="/mediawiki/extensions/chartvisits/google_pars.php?kind=image&sparkline=TimeOnSiteSparkline&w=500&h=200" />'+ '</div>'; }); function toggle_chartlive(){ $('#outer_livechart_box').toggle(); } function makeChartsLiveChange(sel){ $('#img_chart_zone').attr ({src: '/mediawiki/extensions/chartvisits/google_pars.php?kind=image&sparkline='+sel.value+'&w=500&h=200&random=' + Math.random() }); } function make_toggle_big_chart (){ $('#outer_livechart_box').toggle(); $('#outer_livechart_box').css ( {left: findPosX(document.getElementById('heading_line_visitors')) + 200, top: findPosY(document.getElementById('heading_line_visitors'))+20} ); } function findPosX(obj){ var curleft = 0; if (obj.offsetParent) { while (obj.offsetParent) { curleft += obj.offsetLeft; obj = obj.offsetParent; } } else if (obj.x) curleft += obj.x; return curleft;} function findPosY(obj){ var curtop = 0; if (obj.offsetParent) { while (obj.offsetParent) { curtop += obj.offsetTop; obj = obj.offsetParent; } } else if (obj.y) curtop += obj.y; return curtop;} function GetY(el){ var log = ''; var res = el.offsetTop; log += res + ', '; while(el = el.parentNode) {if (! isNaN (el.offsetTop)) res += el.offsetTop; log += res + ', '; }; return res; } function GetX(el){ var res = el.offsetLeft; while(el = el.parentNode) { if (! isNaN (el.offsetLeft)) res += el.offsetLeft;} return res; }
И наконец, пример стилей css:
.outer_livechart_box { z-index: 1002; background-color: #cacaca; font-size: 12px; padding: 3px; margin: 3px; border: 2px; position: absolute; left: 100px; top: 500px; display: none; width: 530px; } .clickable_chartlive_close_btn { cursor: pointer; margin: 5px; padding-right: 25px; background:url(img/close_btn.png) no-repeat right top #cacaca; } #img_chart_zone{ margin: 5px; } .inner_livechart_box{ } .know_more_about_chartlive{ } .tab_livechart_top_row{ background-color: #cacaca; width: 100%; }
|
|
Subscribe Now! |
|

