As of 2016-02-26, there will be no more posts for this blog. s/blog/pba/
Showing posts with label event. Show all posts

I had this idea in the end of September 2010 when I was playing with Google Analytics' tracking code. I wrote some code for rating blog posts using the option value, the code did stay on my blog for a day or two before I took it down, it wasn't too useful for me. But a function allows visitor to report page issue could be very helpful if someone is willing to click on some buttons.

I have finished a simple code and it's at bottom of this blog:


Well, it doesn't look pretty. Here is the code in that HTML/JavaScript gadget:

<script src="https://gist.github.com/raw/1713067/ga-wr.js"></script>
<script>
function init_page() {
  var gawr_options = {
    target: 'ga-wr',
    UA: 'UA-XXXXXXXX-X',
    report_options: [
      {
        title: 'Image is not loaded'
      },
      {
        title: 'Link is broken'
      },
      {
        title: 'Other'
      }
      ]
    };
  new GAWR(gawr_options);
}
$(init_page);
</script>
<div id="ga-wr"></div>

For report of issues report, I can write my own program to get daily report using my current daily report as base. But I don't think I will trouble myself, not yet anyway. Right now, I can see the report with custom report in Google Analytics:


It works great for me for now. Note that, you need to use Alert/Total Events instead of Pageviews. It's event not page. The report does get updated very quick, probably a few minutes after reported. I will say that's instant almost.

Now a little technical background of this script. Basically, you should use different profile. It will track page when a report is being submit and the report is recorded as Event. Event action is the issue name and option label is the additional information as you seen in the image above.

Option value can only accept integer, custom value probably can do the trick, but I put the data in option label. There is another way to record is to rewrite the page URL when tracking the page, but I don't like that. But this could be a benefit, rewriting url to be /original-page-url/issue and still send the event. This way, if you watch Real-time tab, you can see there is a report just comes in if you don't use separate profile.

And remember when visitor reports, page URL is recorded by page tracking, also user's browser and system and everything Google Analytics collects by default is already in the data. Isn't this awesome and brilliant? I don't even need to code for collecting such data if I need to check visitor's browser, they are just there for me to read.

Google Analytics API can do more than just website access statistics, you can set up a poll or some thing more. Imagine you let people to vote and you use visitors metric or something to prevent some degree of voting spam.

Only the data isn't public without coding and they require process.

I spent few hours trying to find the solution for them, I did find one, but it's not portable and not good for me.


The final code is

#!/usr/bin/env python


import os
import select
import signal
import sys
import termios
import time
import tty


def ttywidth():

#f = os.popen('stty size', 'r')
# Should return 'HHH WWW'
#width = int(f.read().split(' ')[1])
f = os.popen('tput cols', 'r')
width = int(f.read())
f.close()
return width


def getch():

return sys.stdin.read(1)


def update_width(signum, frame):

global width

width = ttywidth()
sys.stdout.write(str(width) + '\r\n')

width = ttywidth()

# Use signal to be ackknowledged of window change event
signal.signal(signal.SIGWINCH, update_width)

# Get stdin file descriptor
fd = sys.stdin.fileno()
# Backup, important!
old_settings = termios.tcgetattr(fd)
tty.setraw(sys.stdin.fileno())

p = select.poll()
# Register for data-in
p.register(sys.stdin, select.POLLIN)

while True:
# If do not need the width of terminal, then this catch might not be
# necessary.
try:
# Wait for 1ms, if still not char in, then return.
if p.poll(1):
ch = getch()
if ch == "\x03":
# Ctrl+C
break
if ch == "\x0d":
# Entry key
sys.stdout.write("\033[97;101m" + " " * width + "\r\n\033[39;49m\r\n")
break
except select.error:
# Conflict with signal
# select.error: (4, 'Interrupted system call') on p.poll(1)
pass
# Must restore the setting, or stdin still not echo after exit this program.
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)

For first part of problem, terminal's width (columns), it calls external program, which is what I don't like. There also is a way to use environment variable COLUMNS, but it's not a good way because most of people would not export COLUMNS.

The second part of problem, non-blocking stdin without echoing, it uses select.poll to do non-blocking and tty to do no echoing. In addition, it uses termios to backup and to restore terminal attributes, which is very important. If it doesn't do, then the stdin of shell will be still not echoing after program exits.

Unless you are in console, or you would change terminal's geometry sometimes, therefore signal comes to catch the event SIGWINCH. That will slightly affect select.poll, but should be no harm, you can see the try except for that.

One more thing to note, after setraw(), it needs to use "\r\n"("\x0d\x0a") not just "\n" for a newline (this I don't know the reason).

All I want from the code is when user hits enter, then it prints out a horizontal bar with same width of terminal as seperator.

This code is put in Public Domain. If you know of a portable code, and it does rely on additional library, please share with me. Please also comment on the code, explain some things that I didn't mention above, those must be the things that I didn't know. It should be able to merge the code inside main loop into getch(), make it return None when no data available, I leave you that to finish.

Updated on 5/26: If you don't want to show cursor, you can do:
# Hide cursor
sys.stdout.write('\033[?25l')
# Show cursor
sys.stdout.write('\033[?25h')
If you don't like change original code for "\r\n", you can do
class STDOUT_R:

@staticmethod
def write(s):

s = s.replace('\n', '\r\n')
sys.__stdout__.write(s.encode('utf-8'))

@staticmethod
def flush():

return sys.__stdout__.flush()


class STDERR_R:

@staticmethod
def write(s):

s = s.replace('\n', '\r\n')
sys.__stderr__.write(s.encode('utf-8'))

@staticmethod
def flush():

return sys.__stderr__.flush()