<?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>iamtgc &#187; Python</title>
	<atom:link href="http://iamtgc.com/category/python/feed/" rel="self" type="application/rss+xml" />
	<link>http://iamtgc.com</link>
	<description></description>
	<lastBuildDate>Thu, 02 Feb 2012 00:15:10 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>PostgreSQL Database Encryption</title>
		<link>http://iamtgc.com/2011/09/13/postgresql-database-encryption/</link>
		<comments>http://iamtgc.com/2011/09/13/postgresql-database-encryption/#comments</comments>
		<pubDate>Tue, 13 Sep 2011 14:01:49 +0000</pubDate>
		<dc:creator>tgc</dc:creator>
				<category><![CDATA[Postgres]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://iamtgc.com/?p=194</guid>
		<description><![CDATA[For last decade data breaches and data losses of Social Security numbers, credit card numbers, or one of a slew of other pieces of private information, have landed businesses on the front page of major newspapers, and in some instances &#8230; <a href="http://iamtgc.com/2011/09/13/postgresql-database-encryption/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>For last decade data breaches and data losses of Social Security numbers, credit card numbers, or one of a slew of other pieces of private information, have landed businesses on the front page of major newspapers, and in some instances with legal liability.</p>
<p>I knew when I had an opportunity to work an application which handled sensitive information, that it had to be done with the protection of this information as the number one priority.<br />
<span id="more-194"></span><br />
I began familiarizing myself with the application by starting with the PostgreSQL database back-end.  The <a href="http://www.postgresql.org/docs/8.4/static/pgcrypto.html">pgcrypto</a> module was present, but none of it&#8217;s functions were currently in use.</p>
<p>For the remainder of the article, we will take a look at how to use some of the pgcrypto <a href="http://www.postgresql.org/docs/8.4/static/pgcrypto.html#AEN116836" title="F.23.3. PGP encryption functions" target="_blank">PGP encryption functions</a> to encrypt sensitive data.  </p>
<p>First we will need to create our table.  For the purposes of our example, the table will contain only one column.</p>
<pre class="brush:sql;">
CREATE TABLE messages (
   message bytea
);
</pre>
<p>Next we need to create our PGP keys.  This process is outlined in the PostgreSQL pgcrypto documentation <a href="http://www.postgresql.org/docs/8.4/static/pgcrypto.html#AEN116992" title="F.23.3.8. Generating PGP keys with GnuPG" target="_blank">here</a>.</p>
<p>Once we&#8217;ve created our PGP keys we can test the encryption functionality using the below Python code.  Here we are using the public key to encrypt the word &#8220;testing&#8221; and inserting the result into our table.</p>
<pre class="brush:python;">
#!/usr/bin/env python

import psycopg2

conn = psycopg2.connect("dbname='crypto_test' user='tgc' host='localhost'")

curs = conn.cursor()

f = open("public.key", "r")
key = f.read()

curs.execute("INSERT INTO messages (message) VALUES (pgp_pub_encrypt('testing', dearmor('%s')))" % (key))

conn.commit()
</pre>
<p>Did it work?  Let&#8217;s see what was actually inserted into our table.</p>
<pre class="brush:plain;">
crypto_test=# select * from messages;
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             message
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 \301\301N\003~_\240V\321\032$\006\020\010\000\271H9\327\224\0336\245:\242\202\352B\232\314\261\2263\032#Z\343\344\365;m\333\337\3226`\352\344\017H\304\373|\213\022\246\343$\351Z\307\366`&lt;\266\206&lt;\330\332\024\203xX\270S\261\317\\\317\336bz\241\307\337\333`\203\020\311J?Y\013\331\021b{\220[S\011\032eu\374s\265l\336JiD\217\364P\275\272\004i\220\214\205\3111\206&lt;\361\025%\235+U\253\202\340\250V\247\361\310\334f\307\322x=&gt;\315x\263;\307w\335\351~--\363\350\361]5)\203\327"\254vk4o\211\244s^r\301\310\374=\312L\347\233\263\232\256~\337\033tM&gt;\012\001C\200\345\303r\326\354{\005\3149\314\211\246\210\273\002~Z\317\270p\237\376\307\025\377r\370\221G\324\306\303\355'\301\020\357/\2049\342\222}\327K\321\347\322+\343Y\220C\027vn\027\356V-\203\355\311\033\355A)\277\266\224\256\022\007\377_\031:\015\345-\264Rj~n\207\240/\370D\3143z\305k\245/f'\004\230r[\207\321H\240a^\247\015\037\351\276!~\354\031\314~)\224\364r\354\036\337\376\006\024K\300ih\335\377\011\256C\262\253\016\210ZN\272\231{\332\304\303\332\003;\221\022\004\275\335\330$\027v\355\004,f\2713\266\352\311\027\326\316\3742LMM\353\037\222C\264\240\275&gt;,\210o\326\212F_@G\306\276\274\332\247\256\336\027U\377\224\313\221&gt;b\355\003K\300\231c&lt;H\236;[\027!\263\311\260V\230\200Jp\333\023\027\004Y\273\321\306N\024%\376\010\317\351\016\332&#038;\030\213\225\017#\006\243nb\332r\363~\013\336\207\337\005\024\322\351$KG\334\376\260\366\025]K\031\037w\016O\2358\277\032?s%\300R\263\031R\273S:\376&#038;\377\326\365\240_\252+;\002+\243\034\257\364\377\322U\320F\005\260\230OS\373~\3228\001*\304\265\204\273\335\236\022\305\312\233\350\011\231\211\250\2103\273\305\214\1770g\305k\254\245!X\262\216\000\020K%~OsJ\365m"\330*\246KX"K4\350\013\214#
(1 row)
</pre>
<p>Well, that&#8217;s certainly a far cry from &#8220;testing&#8221;.</p>
<p>Now for the code that decrypts our message.</p>
<pre class="brush:python;">
#!/usr/bin/env python

import psycopg2

conn = psycopg2.connect("dbname='crypto_test' user='tgc' host='localhost'")

curs = conn.cursor()

f = open("secret.key", "r")
key = f.read()

curs.execute("SELECT pgp_pub_decrypt(message, dearmor('%s')) from messages" % (key))

rows = curs.fetchall()

for row in rows:
    print row[0]
</pre>
<p>Let&#8217;s try running our decryption script</p>
<pre class="brush:shell;">
$ python ./test-decrypt.py
testing
</pre>
<p><strong>Note:</strong> depending on the type of data you are encrypting, the functions <strong>pgp_pub_encrypt_bytea</strong> and <strong>pgp_pub_decrypt_bytea</strong> are also available.</p>
]]></content:encoded>
			<wfw:commentRss>http://iamtgc.com/2011/09/13/postgresql-database-encryption/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Importing XML into a database with Python and SQLAlchemy</title>
		<link>http://iamtgc.com/2008/01/29/importing-xml-into-a-database-with-python-and-sqlalchemy/</link>
		<comments>http://iamtgc.com/2008/01/29/importing-xml-into-a-database-with-python-and-sqlalchemy/#comments</comments>
		<pubDate>Tue, 29 Jan 2008 21:19:47 +0000</pubDate>
		<dc:creator>tgc</dc:creator>
				<category><![CDATA[MySQL]]></category>
		<category><![CDATA[Postgres]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://iamtgc.com/2008/01/29/importimg-xml-into-a-database-with-python-and-sqlalchemy/</guid>
		<description><![CDATA[Let&#8217;s begin by analyzing the XML we want to import into our database, it consists of a book&#8217;s ISBN, title, and author. &#60;!-- books.xml --&#62; &#60;catalog&#62; &#60;book isbn="1-880985-26-8"&#62; &#60;title&#62;The Consumer&#60;/title&#62; &#60;author&#62;M. Gira&#60;/author&#62; &#60;/book&#62; &#60;book isbn="0-679775-43-9"&#62; &#60;title&#62;The Wind-Up Bird Chronicle&#60;/title&#62; &#60;author&#62;Haruki &#8230; <a href="http://iamtgc.com/2008/01/29/importing-xml-into-a-database-with-python-and-sqlalchemy/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Let&#8217;s begin by analyzing the XML we want to import into our database, it consists of a book&#8217;s ISBN, title, and author.</p>
<pre class="brush:xml;">
&lt;!-- books.xml --&gt;
&lt;catalog&gt;
  &lt;book isbn="1-880985-26-8"&gt;
    &lt;title&gt;The Consumer&lt;/title&gt;
    &lt;author&gt;M. Gira&lt;/author&gt;
  &lt;/book&gt;
  &lt;book isbn="0-679775-43-9"&gt;
    &lt;title&gt;The Wind-Up Bird Chronicle&lt;/title&gt;
    &lt;author&gt;Haruki Murakami&lt;/author&gt;
  &lt;/book&gt;
  &lt;!-- imagine more entries here... --&gt;
&lt;/catalog&gt;
</pre>
<p><span id="more-13"></span><br />
Now we can create the database table.</p>
<pre class="brush:sql;">
create table books
(
isbn varchar(14) primary key not null,
title varchar(50),
author varchar(50)
);
</pre>
<p>Our example depends on <a href="http://sqlalchemy.org">SQLAlchemy</a>, which is a &#8220;Python SQL toolkit and Object Relational Mapper that gives application developers the full power and flexibility of SQL.&#8221;</p>
<p>While our example leverages a Postgres database, given SQLAlchemy&#8217;s database agnostic approach, it can trivially be modified to access a MySQL database.</p>
<p>This code is derived from a SAX parser originally found in <a href="http://www.amazon.com/gp/redirect.html?ie=UTF8&#038;location=http%3A%2F%2Fwww.amazon.com%2FProgramming-Python-Mark-Lutz%2Fdp%2F0596009259%3Fie%3DUTF8%26s%3Dbooks%26qid%3D1189212366%26sr%3D8-1&#038;tag=iamtgc-20&#038;linkCode=ur2&#038;camp=1789&#038;creative=9325">Programming Python 3rd Edition</a><img src="http://www.assoc-amazon.com/e/ir?t=iamtgc-20&amp;l=ur2&amp;o=1" width="1" height="1" border="0" alt="" style="border:none !important; margin:0px !important;" />.</p>
<pre class="brush:py;">
# bookhandler.py
from sqlalchemy import *
from sqlalchemy.orm import *

import xml.sax.handler

pg_db = create_engine('postgres:///testdb?user=homer')

metadata = MetaData(pg_db)

books_table = Table('books', metadata, autoload=True)

class Book(object):
    pass

mapper(Book, books_table)

class BookHandler(xml.sax.handler.ContentHandler):
    def __init__(self):
        self.buffer = ""
        self.inField = 0
        self.session = create_session(bind=pg_db)

    def startElement(self, name, attributes):
        if name == "book":
            self.isbn = attributes["isbn"]
        elif name == "title":
            self.inField = 1
        elif name == "author":
            self.inField = 1

    def characters(self, data):
        if self.inField:
            self.buffer += data

    def endElement(self, name):
        if name == "book":
            self.session.begin()
            self.newbook = Book()
            self.newbook.isbn = self.isbn
            self.newbook.title = self.title
            self.newbook.author = self.author
            self.session.save(self.newbook)
            self.session.commit()
        elif name == "title":
            self.inField = 0
            self.title = self.buffer
        elif name == "author":
            self.inField = 0
            self.author = self.buffer
        self.buffer = ""
</pre>
<p>
Now that we&#8217;ve set up our sax parser and handler to parse and load the entries from books.xml into the table, lets set up a small script to drive it:</p>
<pre class="brush:py;">
# runit.py
import bookhandler
import xml.sax

parser = xml.sax.make_parser()
handler = bookhandler.BookHandler()
parser.setContentHandler(handler)
parser.parse("books.xml")
</pre>
<p></p>
<p>Now let&#8217;s see if it works:</p>
<pre class="brush:plain;">
$ ls
bookhandler.py  books.xml       runit.py
$ python ./runit.py
$ psql testdb

testdb=# select * from books;
     isbn      |           title            |     author
---------------+----------------------------+-----------------
 1-880985-26-8 | The Consumer               | M. Gira
 0-679775-43-9 | The Wind-Up Bird Chronicle | Haruki Murakami
(2 rows)
</pre>
]]></content:encoded>
			<wfw:commentRss>http://iamtgc.com/2008/01/29/importing-xml-into-a-database-with-python-and-sqlalchemy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Using Python to Load Binary Files into Postgres</title>
		<link>http://iamtgc.com/2007/08/04/using-python-to-load-binary-files-into-postgres/</link>
		<comments>http://iamtgc.com/2007/08/04/using-python-to-load-binary-files-into-postgres/#comments</comments>
		<pubDate>Sat, 04 Aug 2007 18:47:47 +0000</pubDate>
		<dc:creator>tgc</dc:creator>
				<category><![CDATA[Postgres]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://iamtgc.com/2007/08/04/using-python-to-load-binary-files-into-postgres/</guid>
		<description><![CDATA[Many times developers may find it desirable to store binary files within a database. For example, an online store may find it easier to store the pictures of their merchandise in the database along side the item description, quantity, price, &#8230; <a href="http://iamtgc.com/2007/08/04/using-python-to-load-binary-files-into-postgres/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>Many times developers may find it desirable to store binary files within a database.  For example, an online store may find it easier to store the pictures of their merchandise in the database along side the item description, quantity, price, etc.  Doing this can have it&#8217;s advantages and disadvantages.  Advantages may include easier maintenance, as there are no image files to backup and no need to worry about filename collisions.  However the disadvantages of the overhead of database queries, especially if you are dealing with large image files, will have to be weighed.<br />
<span id="more-9"></span><br />
The primary obstacle in importing binary data into Postgres is making sure that your data is properly escaped.  For example when you look at an image file, it may contain the following binary data</p>
<pre class="brush:plain;">
\234\370\260\260\357
</pre>
<p>This data will need to be escaped as </p>
<pre class="brush:plain;">
\\234\\370\\260\\260\\357</pre>
<p>in order for Postgres to store it correctly.  Fortunately <a href="http://www.initd.org/tracker/psycopg/wiki/PsycopgTwo">Psycopg2</a>, which we will use in our import script, takes care of the escaping for you.  Below is a script that will take an image file (or any binary file) and a user name, stored in the column <i>username</i> and update the column <i>avatar_image</i> (which is of type <b>bytea</b>) with the supplied image file. Make special note of <b>psycopg2.Binary()</b> which wraps the binary representation of the image file, and takes care of the necessary escaping.</p>
<pre class="brush:py;">
#!/usr/bin/env python

import psycopg2
import sys

conn = psycopg2.connect("dbname='myforum' user='johndoe' host='localhost'")

curs = conn.cursor()

f = open(sys.argv[1], 'rb')
binary = f.read()

curs.execute("UPDATE usertable SET avatar_image = %s WHERE username = %s", (psycopg2.Binary(binary), sys.argv[2]))
conn.commit()
</pre>
]]></content:encoded>
			<wfw:commentRss>http://iamtgc.com/2007/08/04/using-python-to-load-binary-files-into-postgres/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Geocoding with Python</title>
		<link>http://iamtgc.com/2007/07/25/geocoding-with-python/</link>
		<comments>http://iamtgc.com/2007/07/25/geocoding-with-python/#comments</comments>
		<pubDate>Wed, 25 Jul 2007 20:57:37 +0000</pubDate>
		<dc:creator>tgc</dc:creator>
				<category><![CDATA[Google]]></category>
		<category><![CDATA[Python]]></category>

		<guid isPermaLink="false">http://iamtgc.com/2007/07/25/geocoding-with-python/</guid>
		<description><![CDATA[The following code snippet is intended to demonstrate how you can leverage Python and the Google Maps/Geocode API to query the coordinates of two zip codes and then determine the distance between them. Both functions have far reaching uses independently &#8230; <a href="http://iamtgc.com/2007/07/25/geocoding-with-python/">Continue reading <span class="meta-nav">&#8594;</span></a>]]></description>
			<content:encoded><![CDATA[<p>The following code snippet is intended to demonstrate how you can leverage Python and the Google Maps/Geocode API to query the coordinates of two zip codes and then determine the distance between them.  Both functions have far reaching uses independently which will be further demonstrated in future posts.</p>
<p>This example determines distance between the two points in miles, however it can be modified to return distance in kilometers by replacing 3959.0 (the average radius of the earth in miles) with 6371.0 (the radius in kilometers).</p>
<p>This code does no validation on the existence of the zip code, and will likely return valid coordinates of something (whatever Google thinks you really meant) when you pass it a non existent zip code.<br />
<span id="more-6"></span><br />
The original geocode code was found <a href="http://www.djangosnippets.org/snippets/293/">here.</a></p>
<p><b>Note:</b> You will have to provide your Google Maps API key in the key variable.</p>
<pre class="brush:py;">
import math
import urllib

def get_lat_long(location):
        key = ""
        output = "csv"
        location = urllib.quote_plus(location)
        request = "http://maps.google.com/maps/geo?q=%s&#038;output=%s&#038;key=%s" % (location, output, key)
        data = urllib.urlopen(request).read()
        dlist = data.split(',')
        if dlist[0] == '200':
                return dlist[2], dlist[3]
        else:
                return None, None

def calculate_distance(zip1, zip2):
        lat1, lon1 = get_lat_long(zip1)
        lat2, lon2 = get_lat_long(zip2)

        if (not lat1) or (not lon1) or (not lat2) or (not lon2):
                return -1

        lat1 = float(lat1) * math.pi/180
        lon1 = float(lon1) * math.pi/180
        lat2 = float(lat2) * math.pi/180
        lon2 = float(lon2) * math.pi/180

        return 3959.0 * math.acos(math.sin(lat1) * math.sin(lat2) + math.cos(lat1) * math.cos(lat2) * math.cos(lon2-lon1))
</pre>
]]></content:encoded>
			<wfw:commentRss>http://iamtgc.com/2007/07/25/geocoding-with-python/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
	</channel>
</rss>

