<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Underneath the already known &#187; ruby</title>
	<atom:link href="http://vaskas.ru/tag/ruby/feed/" rel="self" type="application/rss+xml" />
	<link>http://vaskas.ru</link>
	<description>What Vaskas thinks</description>
	<lastBuildDate>Mon, 13 Jul 2009 19:38:23 +0000</lastBuildDate>
	<generator>http://wordpress.org/?v=2.8.1</generator>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
			<item>
		<title>Displaying Google Analytics charts on the website</title>
		<link>http://vaskas.ru/2008/08/08/displaying-google-analytics-charts-on-the-website/</link>
		<comments>http://vaskas.ru/2008/08/08/displaying-google-analytics-charts-on-the-website/#comments</comments>
		<pubDate>Fri, 08 Aug 2008 09:09:15 +0000</pubDate>
		<dc:creator>vaskas</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[charts]]></category>
		<category><![CDATA[curb]]></category>
		<category><![CDATA[google analytics]]></category>
		<category><![CDATA[rexml]]></category>
		<category><![CDATA[ruby]]></category>
		<category><![CDATA[statistics]]></category>

		<guid isPermaLink="false">http://vaskas.ru/?p=39</guid>
		<description><![CDATA[We wanted our Google Analytics charts to be displayed in one of the sections of Inshaker. GA is an excellent service for complex auditorium analysis. Unfortunately, it has no public API (though, I think it will be available in future). Fortunately, it is able to export reports to several formats, including XML.
There are several existing [...]]]></description>
			<content:encoded><![CDATA[<p>We wanted our <a href="http://www.google.com/analytics/">Google Analytics</a> charts to be displayed in one of the sections of <a href="http://www.inshaker.ru/about.html#view-stat">Inshaker.</a> GA is an excellent service for complex auditorium analysis. Unfortunately, it has no public API (though, I think it will be available in future). Fortunately, it is able to export reports to several formats, including XML.<br />
There are several existing tools for drawing charts on-the fly. Among them are the excellent flash-based amCharts and url-based Google Charts. I chose <a href="http://www.amcharts.com">amCharts</a> because they are more nice-looking and interactive. AmCharts rely on their own XML data files, which have rather straightforward formats.<br />
<span id="more-39"></span><br />
<img src="http://vaskas.ru/up/stat.png" alt="Visitors/visits chart" /><br />
So the first problem was to download and convert GA reports into amCharts-compatible formats. The second was to be able to do it automatically, so that our charts always reflect actual statistics.<br />
What I came up to was writing a <a href="http://vaskas.ru/up/analytic.rb">Ruby script</a> to do all the download-and-convert job and launching it with cron each N minutes.</p>
<p>I think the code can speak for itself (since it&#8217;s Ruby). Some notes on it: I used the Curb library to download the reports. It&#8217;s not mature yet to handle cookies, so I had to do cookie handling myself (fetching them from HTTP response and adding them to request). Why care about cookies? In order to authorize in Google Analytics.<br />
The REXML part is rather simple.<br />
Before using the script make sure to create directories specified in Config module (and provide correct login data and site id)<br />
<a href="http://vaskas.ru/up/analytic.rb">Grab the script here</a> or continue reading the code:</p>
<pre>
<code class="ruby">
require 'rubygems'
require 'activesupport'
require 'rexml/document'
require 'unicode'
require 'curb'
require 'cgi'
include REXML
$KCODE = 'u'

# Downloading and conversion of
# Google Analytics reports into amCharts data.xml files

class String
  def merge_i
    self.gsub(" ", "").to_i
  end
end

module Config
  SITE_ID = "9038802" # your site id
  PERIOD = ARGV[0] ? ARGV[0].to_i : 30 # period (days)

  REPORTS_PATH = "reports/" # directory to store downloaded GA reports

  VISITORS_REPORT = "VisitorsOverviewReport"
  CONTENT_REPORT  = "ContentReport"
  GEO_REPORT      = "GeoMapReport"

  OUT_VISITS = "./stat/visitors/data.xml" # amCharts data.xml file (visitors stats)
  OUT_CITIES = "./stat/cities/data.xml" # (cities report xml file)

  EMAIL    = "my_google_analytics@mail.com" # your GA login email
  PASSWORD = "my_password" # your GA password
  PIE_COLORS = ["#a2bcda", "#c1b76f", "#f28358", "#edef00", "#24cbe5", "#64e572"]
end

def process_visits
  path_label = "AnalyticsReport/Report/Graph/Serie/Point/Label"
  path_value = "AnalyticsReport/Report/Graph/Serie/Point/Value"

  vxml = Document.new File.new(Config::REPORTS_PATH + Config::VISITORS_REPORT + ".xml")
  dates    = vxml.elements.to_a(path_label).map {|d| d = d.text}
  visitors = vxml.elements.to_a(path_value).map {|v| v = v.text.merge_i}

  cxml = Document.new File.new(Config::REPORTS_PATH + Config::CONTENT_REPORT + ".xml")
  views = cxml.elements.to_a(path_value).map {|v| v = v.text.merge_i}

  data_file = Document.new

  chart = Element.new("chart")
  data_file.add chart

  # X-Axis - dates
  series = Element.new("series")
  dates.each_with_index { |date, i|
    value = Element.new("value")
    value.attributes["xid"] = i.to_s
    value.text = date
    series.add value
  }
  chart.add_element series

  graphs = Element.new("graphs")
  chart.add_element graphs

  # Y-Axis - numbers
  [views, visitors].each_with_index { |set, i|
    graph = Element.new("graph")
    graph.attributes["gid"] = (i+1).to_s
    set.each_with_index { |num, j|
      value = Element.new("value")
      value.attributes["xid"] = j.to_s
      value.text = num
      graph.add value
    }
    graphs.add graph
  }

  # Output
  File.open(Config::OUT_VISITS, "w+") {|charts| data_file.write(charts) }
  puts "Built visits/visitors chart..."
end

def process_cities
  path_name  = "AnalyticsReport/Report/GeoMap/Region[position()<7]/Name"
  path_value = "AnalyticsReport/Report/GeoMap/Region[position()<7]/Value"

  cxml = Document.new File.new(Config::REPORTS_PATH + Config::GEO_REPORT + ".xml")
  cities = cxml.elements.to_a(path_name).map {|c| c = c.text}
  visitors =  cxml.elements.to_a(path_value).map {|v| v = v.text.merge_i}

  data_file = Document.new
  pie = Element.new("pie")

  data_file.add pie

  cities.each_with_index {|city, i|
    slice = Element.new("slice")
    slice.attributes["title"] = city
    if Config::PIE_COLORS[i] then slice.attributes["color"] = Config::PIE_COLORS[i] end
    slice.text = visitors[i].to_s
    pie.add slice
  }

  # Output
  File.open(Config::OUT_CITIES, "w+") {|pie| data_file.write(pie) }
  puts "Built cities chart..."
end

def get_cookies(header_str)
  cookies = {}
  headers = header_str.split("\n")
  headers.each {|h|
    if h =~ /Set-Cookie: (.+)/
      arr = $1.split("=")
      name = arr[0]
      cookies[name] = arr[1].split(";")[0]
    end
  }
  cstr = ""
  cookies.each {|name, val| cstr += name + "=" + CGI.escape(val) + ";" }
  cstr
end

def download_reports
  statuses = {} # statuses of downloading of reports (true - success, false - failed)

  dates = [Config::PERIOD.days.ago, Time.now].map {|d| d.strftime("%Y%m%d")}

  auth_url   = "https://www.google.com/accounts/ServiceLoginBoxAuth"
  report_pfx = "https://www.google.com/analytics/reporting/export?fmt=1&#038;id=#{Config::SITE_ID}&#038;pdr=#{dates[0]}-#{dates[1]}&#038;segkey=city&#038;cmp=average&#038;&#038;rpt="

  serv  = Curl::PostField.content("service", "analytics")
  hl    = Curl::PostField.content("hl", "ru-RU")
  email = Curl::PostField.content("Email", Config::EMAIL)
  passw = Curl::PostField.content("Passwd", Config::PASSWORD)

  # Authorization in Google Analytics
  c = Curl::Easy.new(auth_url)
  c.http_post(serv, hl, email, passw)
  cstr = get_cookies(c.header_str)

  # Downloading of reports
  [Config::VISITORS_REPORT, Config::CONTENT_REPORT, Config::GEO_REPORT].each { |rpt|
    c = Curl::Easy.new(report_pfx + rpt)
    c.headers["Cookie"] = cstr
    c.http_get
    if c.body_str =~ /<\/AnalyticsReport>/
      puts "Downloaded "+ rpt + "..."
      File.open(Config::REPORTS_PATH + rpt + ".xml", "w+") {|out| out.write(c.body_str) }
      statuses[rpt] = true
    else
      puts "Failed to download " + rpt + "..."
      statuses[rpt] = false
    end
  }
  statuses
end

dates = [Config::PERIOD.days.ago, Time.now].map {|d| d.strftime("%d %B %Y")}
puts "Trying to download reports from #{dates[0]} to #{dates[1]}"
statuses = download_reports
if(statuses[Config::VISITORS_REPORT] &#038;&#038; statuses[Config::CONTENT_REPORT]) then process_visits end
if(statuses[Config::GEO_REPORT]) then process_cities end
</code></pre>
<p>The crontab (crontab -e) is like this</p>
<pre>
# m h  dom mon dow   command
0,30 * * * * /www/mysite/update_stats.sh
</pre>
<p>update_stats.sh executes analytic.rb and does some other work.</p>
]]></content:encoded>
			<wfw:commentRss>http://vaskas.ru/2008/08/08/displaying-google-analytics-charts-on-the-website/feed/</wfw:commentRss>
		<slash:comments>2</slash:comments>
		</item>
		<item>
		<title>Using ERB for massive static pages compilation</title>
		<link>http://vaskas.ru/2008/08/05/using-erb-for-massive-static-pages-compilation/</link>
		<comments>http://vaskas.ru/2008/08/05/using-erb-for-massive-static-pages-compilation/#comments</comments>
		<pubDate>Tue, 05 Aug 2008 10:44:34 +0000</pubDate>
		<dc:creator>vaskas</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[erb]]></category>
		<category><![CDATA[inshaker]]></category>
		<category><![CDATA[ruby]]></category>

		<guid isPermaLink="false">http://vaskas.ru/?p=13</guid>
		<description><![CDATA[For Inshaker, we needed a templating mechanism for pages sharing the same design. We also wanted the pages to be static (for performance reasons). We did not need them to be generated on-the-fly.
ERB was an ideal option for such a purpose. It allows us to generate all (75+) static pages when we need to make [...]]]></description>
			<content:encoded><![CDATA[<p>For <a href="http://www.inshaker.ru">Inshaker</a>, we needed a templating mechanism for pages sharing the same design. We also wanted the pages to be static (for performance reasons). We did not need them to be generated on-the-fly.<br />
ERB was an ideal option for such a purpose. It allows us to generate all (75+) static pages when we need to make an update.<br />
It is also very simple in terms of use:</p>
<p>1. You create a template for your page (template.rhtml)</p>
<pre><code class="html">
&lt;html&gt;
&lt;head&gt;
	&lt;title&gt;&lt;%= @title %&gt;&lt;/title&gt;
&lt;/head&gt;
&lt;body&gt;
	This page was generated by &lt;%= @name %&gt; on &lt;%= Time.now.to_s %&gt;.
	&lt;ul&gt;
	&lt;% @tags.each do |tag| %&gt;
	&lt;li&gt;&lt;%= tag %&gt;&lt;/li&gt;
	&lt;% end %&gt;
	&lt;/ul&gt;
&lt;/body&gt;
&lt;/html&gt;
</code></pre>
<p>2. You create a ruby class which is bound to the template and run erb (erb_test.rb)</p>
<pre><code class="ruby">
require 'rubygems'
require 'erb'

class MyPage
  def initialize(hash)
    @title = hash[:title] + " - that's what I mean"
    @name = hash[:name]
    @tags = hash[:tags]
  end

  def get_binding
    binding
  end
end

# Instantiate ERB with the instance of that class and flush the output
template = File.open("template.rhtml").read
renderer = ERB.new(template)
page     = MyPage.new({:title => "ERB rules",
                       :name => "Ninja",
                       :tags => ["ruby", "erb", "html", "example"]})

File.open("mypage.html", "w+") { |html|
    html.write renderer.result(page.get_binding)
}
</code></pre>
<p>Of course, this is just a basic usage example. ERB shows its power when there are A LOT of pages to be generated, not a single one <img src='http://vaskas.ru/wp-includes/images/smilies/icon_wink.gif' alt=';-)' class='wp-smiley' /><br />
I should have a closer look at SproutCore since they seem to be using the same principle for views compilation.</p>
]]></content:encoded>
			<wfw:commentRss>http://vaskas.ru/2008/08/05/using-erb-for-massive-static-pages-compilation/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>
