Find local time at various locations, this is a small mashup between Google map and EarthTools.
If it won't be simple, it simply won't be. [Hire me, source code] by Miki Tebeka, CEO, 353Solutions
Saturday, August 15, 2009
Dynamicall Generating Static Content
Sometimes you want to generate a resource (say a chart) once and then keep it. This way you don't need to keep track of all the objects you've created and can easily clean (since if someone want the resource again it will be generated again).
One way is to do it with lighttpd 404 handlers (view complete code).
One way is to do it with lighttpd 404 handlers (view complete code).
lighttpd.conf
# lighttpd configuration file
server.modules = ( "mod_cgi" )
# This is generated by by web-up
include "lighttpd.inc"
server.document-root = var.root
server.pid-file = var.root + "/lighttpd.pid"
# files to check for if .../ is requested
index-file.names = ( "index.cgi" )
## set the event-handler (read the performance section in the manual)
# server.event-handler = "freebsd-kqueue" # needed on OS X
# mimetype mapping
mimetype.assign = (
".pdf" => "application/pdf",
".sig" => "application/pgp-signature",
".spl" => "application/futuresplash",
".class" => "application/octet-stream",
".ps" => "application/postscript",
".torrent" => "application/x-bittorrent",
".dvi" => "application/x-dvi",
".gz" => "application/x-gzip",
".pac" => "application/x-ns-proxy-autoconfig",
".swf" => "application/x-shockwave-flash",
".tar.gz" => "application/x-tgz",
".tgz" => "application/x-tgz",
".tar" => "application/x-tar",
".zip" => "application/zip",
".mp3" => "audio/mpeg",
".m3u" => "audio/x-mpegurl",
".wma" => "audio/x-ms-wma",
".wax" => "audio/x-ms-wax",
".ogg" => "application/ogg",
".wav" => "audio/x-wav",
".gif" => "image/gif",
".jpg" => "image/jpeg",
".jpeg" => "image/jpeg",
".png" => "image/png",
".xbm" => "image/x-xbitmap",
".xpm" => "image/x-xpixmap",
".xwd" => "image/x-xwindowdump",
".css" => "text/css",
".html" => "text/html",
".htm" => "text/html",
".js" => "text/javascript",
".asc" => "text/plain",
".c" => "text/plain",
".cpp" => "text/plain",
".log" => "text/plain",
".conf" => "text/plain",
".text" => "text/plain",
".txt" => "text/plain",
".dtd" => "text/xml",
".xml" => "text/xml",
".mpeg" => "video/mpeg",
".mpg" => "video/mpeg",
".mov" => "video/quicktime",
".qt" => "video/quicktime",
".avi" => "video/x-msvideo",
".asf" => "video/x-ms-asf",
".asx" => "video/x-ms-asf",
".wmv" => "video/x-ms-wmv",
".bz2" => "application/x-bzip",
".tbz" => "application/x-bzip-compressed-tar",
".tar.bz2" => "application/x-bzip-compressed-tar"
)
$HTTP["url"] =~ "\.pdf$" {
server.range-requests = "disable"
}
# which extensions should not be handle via static-file transfer
static-file.exclude-extensions = ( ".cgi" )
# This is where the magic happens :)
$HTTP["url"] =~ "/charts/.*\.png$" {
server.error-handler-404 = "/chart.cgi"
}
chart.cgi
#!/usr/bin/env python
'''This CGI script is called by lighttpd 404 error handler when it tries to find
/charts/XXX.png.
This way the image is generated once and then we let lighttpd serve it as static
content.
'''
# The below is needed to tell pylab to work without a screen
import matplotlib; matplotlib.use("Agg")
from pylab import hist, randn, title, savefig
from os import environ
from os.path import basename
from urlparse import urlparse
def generate_chart(name, outfile):
# Generate a random chart
hist(randn(1000), 100)
title("Chart for \"%s\"" % name)
savefig(outfile)
def main():
import cgitb; cgitb.enable()
o = urlparse(environ.get("REQUEST_URI", ""))
if not o.path:
print "Content-type: text/plain\n"
print "ERROR: Can't find path"
raise SystemExit
outfile = "charts/%s" % basename(o.path)
name = basename(o.path).replace(".png", "")
if not name:
print "Content-type: text/plain\n"
print "ERROR: 'name' not specified"
raise SystemExit
generate_chart(name, outfile)
image = open(outfile, "rb").read()
print "Content-type: image/png\n"
print image
if __name__ == "__main__":
main()
Tuesday, August 04, 2009
Enabling Flash on Google Chrom (OSX)
The current release of Google Chrom For Mac has support for Flash, but you need to enable it with the --enable-plugins command line switch.
Here's how to make it persistent (well, until the next update):
And the content of the new Google Chrome is:
Happy surfing ...
Here's how to make it persistent (well, until the next update):
[17:31] ~ $cd /Applications/Google\ Chrome.app/Contents/MacOS/
[17:32] MacOS $sudo mv Google\ Chrome _chrome
[17:32] MacOS $sudo gvim Google\ Chrome
[17:33] MacOS $sudo chmod +x Google\ Chrome
And the content of the new Google Chrome is:
#!/bin/bash
"/Applications/Google Chrome.app/Contents/MacOS/_chrome" --enable-plugins $*
Happy surfing ...
Wednesday, July 29, 2009
Tuesday, July 28, 2009
pyhelp
#!/usr/bin/env python
# Show help for Python
__author__ = "Miki Tebeka <miki.tebeka@gmail.com>"
__license__ = "BSD"
def main(argv=None):
import sys
from optparse import OptionParser
import webbrowser
argv = argv or sys.argv
parser = OptionParser("%prog [MODULE[.FUNCITON]]")
opts, args = parser.parse_args(argv[1:])
if len(args) not in (0, 1):
parser.error("wrong number of arguments") # Will exit
root = "http://docs.python.org"
if not args:
url = "%s/%s" % (root, "modindex.html")
elif "." in args[0]:
module, function = args[0].split(".")
url = "%s/library/%s.html#%s.%s" % (root, module, module, function)
else:
url = "%s/library/%s.html" % (root, args[0])
webbrowser.open(url)
if __name__ == "__main__":
main()
Tuesday, July 21, 2009
Two gnupg Wrappers
encrypt
#!/bin/bash
# Encrypt input using gnupg and shred it
# THIS DELETES THE ORIGINAL FILE
err=0
for file in $@
do
if [ $file != ${file/.gpg/} ]; then
echo "$file already encrypted"
continue
fi
if [ -d $file ]; then
fname=$file.tar.bz2
tar -cjf $fname $file
find $file -type f -exec shred -u {} \;
rm -r $file
elif [ -f $file ]; then
fname=$file
else
echo "ERROR: Unknown file type $file"
err=1
continue
fi
gpg -r miki -e $fname
if [ $? -ne 0 ]; then
echo "ERROR: can't encrypt"
err=1
continue
fi
shred -u $fname
if [ $? -ne 0 ]; then
echo "ERROR: can't shred"
err=1
continue
fi
done
exit $err
decrypt
#!/bin/bash
# Decrypt a file encrypted with gpg
# Check command line
if [ $# -lt 1 ]; then
echo "usage: `basename $0` FILE[s]"
exit 1
fi
err=0
for file in $@
do
if [ ! -f $file ]; then
echo "$file: Not found"
err=1
continue
fi
# Output to current directory
bfile=`basename $file`
ext=`echo $file | sed -e 's/.*\.\([!.]*\)/\1/'`
if [ "$ext" != "gpg" ] && [ "$ext" != "pgp" ]; then
echo "Cowardly refusing to decrypt a file without gpg/pgp extension"
err=1
continue
fi
outfile=${bfile%.$ext}
echo $file '->' $outfile
gpg -d -o $outfile $file
if [ $? -ne 0 ]; then
echo "error decrypting $file"
err=1
fi
done
exit $err
Friday, July 10, 2009
What's My IP?
#!/usr/bin/env python
'''Find local machine IP (cross platform)'''
from subprocess import Popen, PIPE
from sys import platform
import re
COMMANDS = {
"darwin" : "/sbin/ifconfig",
"linux" : "/sbin/ifconfig",
"linux2" : "/sbin/ifconfig",
"win32" : "ipconfig",
}
def my_ip():
command = COMMANDS.get(platform, "")
assert command, "don't know how to get IP for current platform"
pipe = Popen([command], stdout=PIPE)
pipe.wait()
output = pipe.stdout.read()
for ip in re.findall("(\d+\.\d+\.\d+\.\d+)", output):
if ip.startswith("127.0.0"):
continue
return ip
if __name__ == "__main__":
print my_ip()
Friday, July 03, 2009
Little Genetics

#!/usr/bin/env python
'''Playing with genetic algorithms, see
http://en.wikipedia.org/wiki/Genetic_programming.
The main idea that the "chromosome" represents variables in our algorithm and we
have a fitness function to check how good is it. For each generation we keep the
best and then mutate and crossover some of them.
Since the best chromosomes move from generation to generation, we cache the
fitness function results.
I'm pretty sure I got the basis for this from somewhere on the net, just don't
remeber where :)
'''
from itertools import starmap
from random import random, randint, choice
from sys import stdout
MUTATE_PROBABILITY = 0.1
def mutate_gene(n, range):
if random() > MUTATE_PROBABILITY:
return n
while 1:
# Make sure we mutated something
new = randint(range[0], range[1])
if new != n:
return new
def mutate(chromosome, ranges):
def mutate(gene, range):
return mutate_gene(gene, range)
while 1:
new = tuple(starmap(mutate, zip(chromosome, ranges)))
if new != chromosome:
return new
def crossover(chromosome1, chromosome2):
return tuple(map(choice, zip(chromosome1, chromosome2)))
def make_chromosome(ranges):
return tuple(starmap(randint, ranges))
def breed(population, size, ranges):
new = population[:]
while len(new) < size:
new.append(crossover(choice(population), choice(population)))
new.append(mutate(choice(population), ranges))
return new[:size]
def evaluate(fitness, chromosome, data, cache):
if chromosome not in cache:
cache[chromosome] = fitness(chromosome, data)
return cache[chromosome]
def update_score_cache(population, fitness, data, cache):
for chromosome in population:
if chromosome in cache:
continue
cache[chromosome] = fitness(chromosome, data)
def find_solution(fitness, data, ranges, popsize, nruns, verbose=0):
score_cache = {}
population = [make_chromosome(ranges) for i in range(popsize)]
for generation in xrange(nruns):
update_score_cache(population, fitness, data, score_cache)
population.sort(key=score_cache.get, reverse=1)
if verbose:
best = population[0]
err = score_cache[best]
print "%s: a=%s, b=%s, err=%s" % (generation, best[0], best[1], err)
base = population[:popsize/4]
population = breed(base, popsize, ranges)
population.sort(key=score_cache.get, reverse=1)
return population[0], score_cache[population[0]]
def test(show_graph=1):
'''Try to find a linear equation a*x + b that is closest to log(x)'''
from math import log
xs = range(100)
data = map(lambda i: log(i+1) * 100, xs)
def fitness(chromosome, data):
'''Calculate average error'''
a, b = chromosome
def f(x):
return a * x + b
values = map(f, xs)
diffs = map(lambda i: abs(values[i] - data[i]), xs)
# We want minimal error so return 1/error
return 1 / (sum(diffs) / len(diffs))
# Show a nice plot
(a, b), err = find_solution(fitness, data, ((0, 100), (0, 100)), 10, 100, 1)
print "best: a=%s, b=%s (error=%s)" % (a, b, err)
data2 = map(lambda x: a * x + b, range(100))
if not show_graph:
return
import pylab
l1, l2 = pylab.plot(xs, data, xs, data2)
pylab.legend((l1, l2), ("log(x+1)", "%s * x + %s" % (a, b)))
pylab.show()
if __name__ == "__main__":
test()
Friday, June 12, 2009
A Twitter Trends / Google News meshup
This tried to automatically explain why things are trending on Twitter.
The Python Server (trends.py)
#!/usr/bin/env python
'''A twitter trends/google news mesh
loader_thread get news trends every 1min and set _TRENDS_HTML
The web server serves _TRENDS_HTML with is refresed via JavaScript every 30sec
'''
from urllib import urlopen, urlencode
import feedparser
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
from os.path import dirname, join, splitext
import json
from threading import Thread
from time import sleep
def ascii_clean(text):
return text.encode("ascii", "ignore") # Pure ACII
def trend_news(trend):
query = {
"q" : ascii_clean(trend),
"output" : "rss"
}
url = "http://news.google.com/news?" + urlencode(query)
return feedparser.parse(url).entries
def current_trends():
url = "http://search.twitter.com/trends.json"
return json.load(urlopen(url))["trends"]
def news_html(news):
html = '<li><a href="%(link)s">%(title)s</a></li>'
chunks = map(lambda e: html % e, news)
return "\n".join(["<ul>"] + chunks + ["</ul>"])
def trend_html(trend):
thtml = '<a href="%(url)s">%(name)s' % trend
nhtml = news_html(trend_news(trend["name"]))
return '<tr><td>%s</td><td>%s</td><tr>' % (thtml, nhtml)
def table_html(trends):
return ("<table>" +
"<tr><th>Trend</th><th>Related News</th></tr>" +
"".join(map(trend_html, trends)) +
"</table>"
)
_TRENDS_HTML = ""
def loader_thread():
global _TRENDS_HTML
while 1:
_TRENDS_HTML = ascii_clean(table_html(current_trends()))
sleep(60)
def run_loader_thread():
t = Thread(target=loader_thread)
t.daemon = 1
t.start()
def trends_html():
while not _TRENDS_HTML:
sleep(0.1)
return _TRENDS_HTML
def index_html():
return open(join(dirname(__file__), "index.html")).read()
class RequestHandler(BaseHTTPRequestHandler):
def do_GET(self):
if self.path == "/":
self.wfile.write(index_html())
elif self.path.startswith("/trends"):
self.wfile.write(trends_html())
elif splitext(self.path)[1] in (".js", ".css"):
self.wfile.write(open(".%s" % self.path).read())
else:
self.send_error(404, "Not Found")
if __name__ == "__main__":
run_loader_thread()
server = HTTPServer(("", 8888), RequestHandler)
server.serve_forever()
The Auto-Refreshing Web Client (index.html)
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Trends</title>
<link rel="stylesheet" href="trends.css" />
<style>
body {
font-family: Serif, Helvetica;
}
table {
width: 100%;
border: 2px solid gray;
}
td {
vertical-align: top;
border-bottom: 1px solid black;
}
h1 {
text-align: center;
font-variant: small-caps;
}
a {
text-decoration: none;
color: black;
}
a:hover {
background: silver;
}
ul {
margin: 0px;
}
</style>
</head>
<body>
<h1>Trends</h1>
<div id="trends">
Loading ...
</div>
</body>
<script src="jquery.js"></script>
<script>
function load() {
$('#trends').load('/trends');
}
$(document).ready(function() {
load();
setInterval(load, 30 * 1000);
});
</script>
</html>
Wednesday, June 10, 2009
Show "digits only" version of phone number
#!/bin/bash
# Convert phone numbers to numeric (1-800-T-MOBILE -> 1-800-8-662453)
if [ $# -ne 1 ]; then
echo "usage: $(basename $0) PHONE-NUMBER"
exit 1
fi
echo $1 | \
tr '[:lower:]' '[:upper:]' | \
tr ABCDEFGHIJKLMNOPQRSTUVWXYZ 22233344455566677778889999
Saturday, June 06, 2009
JSON Pretiffier
#!/usr/bin/env python
'''JSON prettifier'''
def shift(array):
try:
return array.pop(0)
except IndexError:
return None
def fopen(name, mode, default):
if name in (None, "-"):
return default
return open(name, mode)
def main(argv=None):
import sys
from optparse import OptionParser
argv = argv or sys.argv
indent = 4
parser = OptionParser("%prog [INFILE [OUTFILE]]")
parser.add_option("-i", "--indent", help="indent size (%s)" % indent,
dest="indent", default=indent, type="int")
opts, args = parser.parse_args(argv[1:])
if len(args) not in (0, 1, 2):
parser.error("wrong number of arguments") # Will exit
try:
info = fopen(shift(args), "r", sys.stdin)
outfo = fopen(shift(args), "w", sys.stdout)
except IOError, e:
raise SystemExit("error: %s" % e)
import json
json.dump(json.load(info), outfo, indent=opts.indent)
if __name__ == "__main__":
main()
Update (Aug 14, 2009): Just found out about python -mjson.tool, oh well ...
Thursday, June 04, 2009
strftime for JavaScript
/* strftime for JavaScript
Field description (taken from http://tinyurl.com/65s2qw)
%a Locale’s abbreviated weekday name.
%A Locale’s full weekday name.
%b Locale’s abbreviated month name.
%B Locale’s full month name.
%c Locale’s appropriate date and time representation.
%d Day of the month as a decimal number [01,31].
%H Hour (24-hour clock) as a decimal number [00,23].
%I Hour (12-hour clock) as a decimal number [01,12].
%j Day of the year as a decimal number [001,366].
%m Month as a decimal number [01,12].
%M Minute as a decimal number [00,59].
%p Locale’s equivalent of either AM or PM.
%S Second as a decimal number [00,61].
%U Week number of the year (Sunday as the first day of the week) as a
decimal number [00,53]. All days in a new year preceding the first
Sunday are considered to be in week 0.
%w Weekday as a decimal number [0(Sunday),6].
%W Week number of the year (Monday as the first day of the week) as a
decimal number [00,53]. All days in a new year preceding the first
Monday are considered to be in week 0.
%x Locale’s appropriate date representation.
%X Locale’s appropriate time representation.
%y Year without century as a decimal number [00,99].
%Y Year with century as a decimal number.
%Z Time zone name (no characters if no time zone exists).
%% A literal '%' character.
*/
var days = [
'Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday',
'Saturday'
];
var months = [
'January', 'February', 'March', 'April', 'May', 'June', 'July',
'August', 'September', 'October', 'November', 'December'
];
function shortname(name) {
return name.substr(0, 3);
}
function zeropad(n, size) {
n = '' + n; /* Make sure it's a string */
size = size || 2;
while (n.length < size) {
n = '0' + n;
}
return n;
}
function twelve(n) {
return (n <= 12) ? n : 24 - n;
}
function strftime(format, date) {
date = date || new Date();
var fields = {
a: shortname(days[date.getDay()]),
A: days[date.getDay()],
b: shortname(months[date.getMonth()]),
B: months[date.getMonth()],
c: date.toString(),
d: zeropad(date.getDate()),
H: zeropad(date.getHours()),
I: zeropad(twelve(date.getHours())),
/* FIXME: j: */
m: zeropad(date.getMonth() + 1),
M: zeropad(date.getMinutes()),
p: (date.getHours() >= 12) ? 'PM' : 'AM',
S: zeropad(date.getSeconds()),
w: zeropad(date.getDay() + 1),
/* FIXME: W: */
x: date.toLocaleDateString(),
X: date.toLocaleTimeString(),
y: ('' + date.getFullYear()).substr(2, 4),
Y: '' + date.getFullYear(),
/* FIXME: Z: */
'%' : '%'
};
var result = '', i = 0;
while (i < format.length) {
if (format[i] === '%') {
result = result + fields[format[i + 1]];
++i;
}
else {
result = result + format[i];
}
++i;
}
return result;
}
Quick search using xapian "omega"
A little hack to search current directory using Xapian's omega.
#!/bin/bash
# Minimal search using Xpians Omega (http://xapian.org/docs/omega/overview.html)
# Use "omsearch -i" to index current directoy, then use "omsearch QUERY" to
# search
index() {
mkdir .omega
cat >> .omega/omega.conf << EOF
database_dir $PWD/.omega
template_dir /var/lib/xapian-omega/templates
log_dir /tmp
EOF
omindex --db .omega/default .
}
search() {
omega=/usr/lib/cgi-bin/omega/omega
config=$PWD/.omega/omega.conf
results=.omega/results.html
OMEGA_CONFIG_FILE=$config $omega "P=$1" "HITSPERPAGE=1000" > $results
egrep -o '<B><A HREF=".*"' $results | cut -d\" -f2 | \
xargs -i printf ".%s\n" {}
}
USAGE="usage: $(basename $0) [-i | QUERY]"
index=no
while getopts "ih" opt
do
case $opt in
h | H ) echo $USAGE; exit;;
i ) index=yes;;
* ) echo "error: unknown option" >&2; exit 1;;
esac
done
shift $(($OPTIND - 1))
if [ "$index" == "yes" ]; then
if [ $# -gt 0 ]; then
echo "error: -i don't take any parameters"
exit 1
fi
index
exit
fi
if [ $# -ne 1 ]; then
echo $USAGE
exit 1
fi
if [ ! -d .omega ]; then
echo "error: can't find .omega, run with -i to index first"
exit 1
fi
search $1
Monday, May 11, 2009
Avoiding "peek" with "itertools.chain"
Sometimes you need to filter out some header information. However the only clear anchor you have is the start of real data - you need a "peek" function.
It's easy to avoid writing the "peek" logic, just use itertools.chain.
It's easy to avoid writing the "peek" logic, just use itertools.chain.
#!/usr/bin/env python
'''Avoiding the need for "peek" with itertools.chain'''
from itertools import chain
def is_first_data(line):
return line.startswith("Name:")
def skip_header(data):
data = iter(data)
for line in data:
if is_first_data(line):
return chain([line], data)
# FIXME: We might want to raise something here
return []
if __name__ == "__main__":
data = [
"this is the header",
"it might change every time",
"and you'll never find a good regexp for it",
"The first line of data is easy to know",
"Name: Duffy",
"Type: Duck",
"Anger: 10",
"",
"Name: Bugs",
"Type: Bunny",
"Anger: 0",
]
data = skip_header(data)
for line in data:
print line
Friday, May 01, 2009
Subversion IRC Bot
#!/usr/bin/env python
'''Push changes in subversion repository to IRC channel'''
from twisted.words.protocols import irc
from twisted.internet.protocol import ReconnectingClientFactory
import re
from subprocess import Popen, PIPE
from xml.etree.cElementTree import parse as xmlparse
from cStringIO import StringIO
class IRCClient(irc.IRCClient):
nickname = "svnbot"
realname = "Subversion Bot"
channel = "#dev"
instance = None # Running instance
def signedOn(self):
IRCClient.instance = self
self.join(self.channel)
def svn(self, revision, author, comment):
comment = (comment[:57] + "...") if len(comment) > 60 else comment
message = "SVN revision %s by %s: %s" % (revision, author, comment)
self.say(self.channel, message)
class SVNPoller:
def __init__(self, root, user, password):
self.pre = ["svn", "--xml", "--username", user, "--password", password]
self.root = root
self.last_revision = self.get_last_revision()
def check(self):
if not IRCClient.instance:
return
try:
last_revision = self.get_last_revision()
if (not last_revision) or (last_revision == self.last_revision):
return
for rev in range(self.last_revision + 1, last_revision + 1):
author, comment = self.revision_info(rev)
IRCClient.instance.svn(rev, author, comment)
self.last_revision = last_revision
except Exception, e:
print "ERROR: %s" % e
def svn(self, *cmd):
pipe = Popen(self.pre + list(cmd) + [self.root], stdout=PIPE)
try:
data = pipe.communicate()[0]
except IOError:
data = ""
return xmlparse(StringIO(data))
def get_last_revision(self):
tree = self.svn("info")
revision = tree.find("//commit").get("revision")
return int(revision)
def revision_info(self, revision):
tree = self.svn("log", "-r", str(revision))
author = tree.find("//author").text
comment = tree.find("//msg").text
return author, comment
if __name__ == "__main__":
from twisted.internet import reactor
from twisted.internet.task import LoopingCall
factory = ReconnectingClientFactory()
factory.protocol = IRCClient
reactor.connectTCP("irc.mycompany.com", 6667, factory)
poller = SVNPoller("http://svn.mycompany.com", "bugs", "carrot")
task = LoopingCall(poller.check)
task.start(1)
reactor.run()
Subscribe to:
Posts (Atom)