changeset 364:ea6d35cb6c73

images work, except brackets get escaped
author catherine@cordelia
date Fri, 03 Jul 2009 07:32:40 -0400
parents 5e98e7917de8
children 84070815e44e
files docs/source/index.rst sqlpython/imagedetect.py sqlpython/output_templates.py sqlpython/sqlpyPlus.py
diffstat 4 files changed, 92 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/docs/source/index.rst	Tue Jun 02 07:42:09 2009 -0400
+++ b/docs/source/index.rst	Fri Jul 03 07:32:40 2009 -0400
@@ -5,6 +5,8 @@
 Welcome to SQLPython's documentation!
 =====================================
 
+Dayton *Dynamic* Languages Group is **awesome**
+
 Contents:
 
 .. toctree::
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sqlpython/imagedetect.py	Fri Jul 03 07:32:40 2009 -0400
@@ -0,0 +1,45 @@
+# Small utilities for detecting common image types based on the first
+# few bytes of their content.
+
+# by Ole Laursen, sponsored by IOLA 2009
+
+def is_jpg(data):
+    """True if data is the first 11 bytes of a JPEG file."""
+    return data[:4] == '\xff\xd8\xff\xe0' and data[6:11] == 'JFIF\0'
+
+def is_png(data):
+    """True if data is the first 8 bytes of a PNG file."""
+    return data[:8] == '\x89PNG\x0d\x0a\x1a\x0a'
+
+def is_tiff(data):
+    """True if data is the first 4 bytes of a TIFF file."""
+    return data[:4] == 'MM\x00\x2a' or data[:4] == 'II\x2a\x00'
+
+def is_gif(data):
+    """True if data is the first 4 bytes of a GIF file."""
+    return data[:4] == 'GIF8'
+
+def extension_from_data(data):
+    """Returns extension (like '.jpg') from first 11 bytes of image data.
+
+    An empty string is returned if no match is found."""
+    if is_jpg(data):
+        return ".jpg"
+    elif is_png(data):
+        return ".png"
+    elif is_tiff(data):
+        return ".tif"
+    elif is_gif(data):
+        return ".gif"
+    
+    return ''
+
+def extension_from_file(path):
+    """Returns extension (like '.jpg') based on content of image file at path.
+
+    An empty string is returned if no match is found."""
+    f = file(path, 'r')
+    ext = extension_from_data(f.read(11))
+    f.close()
+    return ext
+    
--- a/sqlpython/output_templates.py	Tue Jun 02 07:42:09 2009 -0400
+++ b/sqlpython/output_templates.py	Fri Jul 03 07:32:40 2009 -0400
@@ -41,7 +41,7 @@
       </tr>
       <tr py:for="row in rows">
         <td py:for="(colname, itm) in zip(colnames, row)" py:attrs="{'headers':'header_' + colname.lower()}">
-          <span py:replace="str(itm)">Value</span>
+          <span py:replace="(hasattr(itm, 'html') and itm.html()) or str(itm)">Value</span>
         </td>
       </tr>
     </table>
--- a/sqlpython/sqlpyPlus.py	Tue Jun 02 07:42:09 2009 -0400
+++ b/sqlpython/sqlpyPlus.py	Fri Jul 03 07:32:40 2009 -0400
@@ -23,12 +23,13 @@
 
 - catherinedevlin.blogspot.com  May 31, 2006
 """
-import sys, os, re, sqlpython, cx_Oracle, pyparsing, re, completion, datetime, pickle, binascii, subprocess
+import sys, os, re, sqlpython, cx_Oracle, pyparsing, re, completion, datetime, pickle, binascii, subprocess, time, itertools
 from cmd2 import Cmd, make_option, options, Statekeeper, Cmd2TestCase
 from output_templates import output_templates
 from metadata import metaqueries
 from plothandler import Plot
 from sqlpython import Parser
+import imagedetect
 import warnings
 warnings.filterwarnings('ignore', 'BaseException.message', DeprecationWarning)
 try:
@@ -281,6 +282,41 @@
             return lineNum, line, offset
         lineNum += 1
         offset -= len(line)
+
+class BlobDisplayer(object):
+    start_timestamp = int(time.time())
+    folder_name = 'sqlpython_blob_store_%d' % start_timestamp
+    file_index = itertools.count()
+    def folder_ok(self):
+        if not os.access(self.folder_name, os.F_OK):
+            try:
+                os.mkdir(self.folder_name)
+                readme = open(os.path.join(self.folder_name, 'README.txt'),'w')
+                readme.write('''
+                                Temporary files for display of BLOBs from within
+                                sqlpython.  Delete when sqlpython is closed.''')
+                readme.close()
+            except:
+                return False
+        return True
+    def __init__(self, blob):
+        self.url = ''
+        self.timestamp = time.time()
+        self.blob = blob.read()
+        self.extension = imagedetect.extension_from_data(self.blob)
+        if self.folder_ok():
+            self.file_name = '%s/blob%d%s' % (
+                os.path.join(os.getcwd(), self.folder_name), 
+                self.file_index.next(), self.extension)
+            self.url = 'file://%s' % self.file_name
+            outfile = open(self.file_name, 'wb')
+            outfile.write(self.blob)
+            outfile.close()
+    def __str__(self):
+        return '(BLOB at %s)' % self.url
+    def html(self):
+        return '<a href="%s"><img src="%s" width="200" /></a>' % (
+            self.url, self.url)
         
 class sqlpyPlus(sqlpython.sqlpython):
     defaultExtension = 'sql'
@@ -449,7 +485,6 @@
         self.colnames = [d[0] for d in self.curs.description]
         if outformat in output_templates:
             self.colnamelen = max(len(colname) for colname in self.colnames)
-            self.coltypes = [d[1] for d in self.curs.description]
             result = output_templates[outformat].generate(formattedForSql=self.formattedForSql, **self.__dict__)        
         elif outformat == '\\t': # transposed
             rows = [self.colnames]
@@ -668,7 +703,7 @@
                 return
             total_len -= len(rset)
             self.pystate['r'][i] = []
-            
+        
     def do_select(self, arg, bindVarsIn=None, terminator=None):
         """Fetch rows from a table.
 
@@ -698,6 +733,12 @@
         else: # this is an ugly workaround for the evil paramstyle curse upon DB-API2
             self.curs.execute(self.querytext)
         self.rows = self.curs.fetchmany(min(self.maxfetch, (rowlimit or self.maxfetch)))
+        self.coltypes = [d[1] for d in self.curs.description]
+        if cx_Oracle.BLOB in self.coltypes:
+            self.rows = [
+                [((coltype == cx_Oracle.BLOB) and BlobDisplayer(datum)) or datum
+                 for (datum, coltype) in zip(row, self.coltypes)]
+                for row in self.rows]
         self.rc = len(self.rows)
         if self.rc != 0:
             resultset = ResultSet()