Commit 56ee772f authored by Jim Fulton's avatar Jim Fulton

major overhall to add record addressing, brains, and support for new interface

parent ca9ecce4
......@@ -10,11 +10,12 @@
__doc__='''Generic Database Connection Support
$Id: Connection.py,v 1.1 1997/12/02 17:22:02 jim Exp $'''
__version__='$Revision: 1.1 $'[11:-2]
$Id: Connection.py,v 1.2 1997/12/05 21:33:12 jim Exp $'''
__version__='$Revision: 1.2 $'[11:-2]
import Globals, OFS.SimpleItem, AccessControl.Role, Persistence, Acquisition
from DateTime import DateTime
from App.Dialogs import MessageDialog
connection_page=Globals.HTMLFile('AqueductDA/connection')
......@@ -45,6 +46,26 @@ class Connection(
_v_connected=''
connection_string=''
def __setstate__(self, state):
Persistence.Persistent.__setstate__(self, state)
if self.connection_string: self.connect(self.connection_string)
def title_and_id(self):
s=Connection.inheritedAttribute('title_and_id')(self)
if hasattr(self, '_v_connected') and self._v_connected:
s="%s, which is connected" % s
else:
s="%s, which is <font color=red> not connected</font>" % s
return s
def title_or_id(self):
s=Connection.inheritedAttribute('title_or_id')(self)
if hasattr(self, '_v_connected') and self._v_connected:
s="%s (connected)" % s
else:
s="%s (<font color=red> not connected</font>)" % s
return s
def manage(self, REQUEST):
"Change the database connection string"
return connection_page(self, REQUEST, action='manage_connection',
......@@ -57,14 +78,22 @@ class Connection(
if check: self.connect(value)
else: self.manage_close_connection(REQUEST)
self.connection_string=value
return 'This needs to be changed'
if REQUEST: return MessageDialog(
title='Connection Modified',
message='The connection information has been changed',
action='manage',
)
def manage_close_connection(self, REQUEST):
" "
try: self._v_database_connection.close()
except: pass
self._v_connected=''
return 'This needs to be changed'
if REQUEST: return MessageDialog(
title='Connection Closed',
message='The connection has been closed',
action='manage',
)
def __call__(self, v=None):
try: return self._v_database_connection
......@@ -91,6 +120,9 @@ class Connection(
##############################################################################
#
# $Log: Connection.py,v $
# Revision 1.2 1997/12/05 21:33:12 jim
# major overhall to add record addressing, brains, and support for new interface
#
# Revision 1.1 1997/12/02 17:22:02 jim
# initial
#
......
......@@ -11,11 +11,11 @@
__doc__='''Generic Database adapter
$Id: DA.py,v 1.15 1997/11/26 20:06:03 jim Exp $'''
__version__='$Revision: 1.15 $'[11:-2]
$Id: DA.py,v 1.16 1997/12/05 21:33:13 jim Exp $'''
__version__='$Revision: 1.16 $'[11:-2]
import string, OFS.SimpleItem, Aqueduct.Aqueduct, Aqueduct.RDB
import DocumentTemplate, marshal, md5, zlib, base64, DateTime, Acquisition
import OFS.SimpleItem, Aqueduct.Aqueduct, Aqueduct.RDB
import DocumentTemplate, marshal, md5, base64, DateTime, Acquisition, os
from Aqueduct.Aqueduct import quotedHTML, decodestring, parse, Rotor
from Aqueduct.Aqueduct import custom_default_report, default_input_form
from Globals import HTMLFile, MessageDialog
......@@ -24,6 +24,11 @@ log_file=None
import sys, traceback
from DocumentTemplate import HTML
import Globals, OFS.SimpleItem, AccessControl.Role, Persistence
from string import atoi, find
import IOBTree
from time import time
from zlib import compress, decompress
md5new=md5.new
addForm=HTMLFile('AqueductDA/daAdd')
......@@ -31,7 +36,7 @@ def add(self,klass,id,title,key,arguments,template,REQUEST=None):
'Add a query'
q=klass()
q.id=id
q.manage_edit(key,title,arguments,template)
q.manage_edit(title,arguments,template,key)
self._setObject(id,q)
if REQUEST: return self.manage_main(self,REQUEST)
......@@ -44,11 +49,32 @@ class DA(
'Database Adapter'
icon ='AqueductDA/DBAdapter_icon.gif'
hasAqueductClientInterface=1
_col=None
sql_delimiter='\0'
max_rows_=1000
cache_time_=0
max_cache_=100
rotor=None
key=''
class_name_=class_file_=''
manage=HTMLFile('AqueductDA/edit')
manage_options=(
{'icon':icon, 'label':'Basic',
'action':'manage_main', 'target':'manage_main'},
{'icon':icon, 'label':'Advanced',
'action':'manage_advancedForm',
'target':'manage_main'},
{'icon':'AccessControl/AccessControl_icon.gif',
'label':'Access Control',
'action':'manage_rolesForm', 'target':'manage_main'},
{'icon':icon,
'label':'Try It',
'action':'index_html', 'target':'manage_main'},
)
manage_main=HTMLFile('AqueductDA/edit')
manage_advancedForm=HTMLFile('AqueductDA/advanced')
test_url___roles__=None
def test_url_(self):
......@@ -57,34 +83,50 @@ class DA(
def quoted_src(self): return quotedHTML(self.src)
def manage_edit(self,key,title,arguments,template,REQUEST=None):
def _setKey(self, key):
if key:
self.key=key
self.rotor=Rotor(key)
elif self.__dict__.has_key('key'):
del self.key
del self.rotor
def manage_edit(self,title,arguments,template,key=None, REQUEST=None):
'change query properties'
self.title=title
self.key=key
self.rotor=Rotor(key)
if key is not None: self._setKey(key)
self.arguments_src=arguments
self._arg=parse(arguments)
self.src=template
self.template=DocumentTemplate.HTML(template)
if REQUEST:
return MessageDialog(
title=self.id+' changed',
message=self.id+' has been changed sucessfully.',
action=REQUEST['URL2']+'/manage_main',
)
if REQUEST: return self.manage_editedDialog(REQUEST)
def manage_advanced(self, key, max_rows, max_cache, cache_time,
class_name, class_file,
REQUEST):
'Change advanced parameters'
self._setKey(key)
self.max_rows_ = max_rows
self.max_cache_, self.cache_time_ = max_cache, cache_time
self.class_name_, self.class_file_ = class_name, class_file
getBrain(self)
if REQUEST: return self.manage_editedDialog(REQUEST)
def manage_testForm(self, REQUEST):
"""Provide testing interface"""
input_src=default_input_form(self.title_or_id(),
self._arg, 'manage_test')
self._arg, 'manage_test',
'<!--#var manage_tabs-->')
return HTML(input_src)(self, REQUEST)
def manage_test(self, REQUEST):
'Perform an actual query'
result=self(REQUEST)
report=HTML(custom_default_report(self.id, result))
report=HTML(
'<html><BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">\n'
'<!--#var manage_tabs-->\n%s\n</body></html>'
% custom_default_report(self.id, result))
return apply(report,(self,REQUEST),{self.id:result})
def index_html(self, PARENT_URL):
......@@ -95,15 +137,56 @@ class DA(
def _searchable_result_columns(self): return self._col
def __call__(self,REQUEST):
def _cached_result(self, DB__, query, compressed=0):
# Try to fetch from cache
if hasattr(self,'_v_cache'): cache=self._v_cache
else: cache=self._v_cache={}, IOBTree.Bucket()
cache, tcache = cache
max_cache=self.max_cache_
now=time()
t=now-self.cache_time_
if len(cache) > max_cache / 2:
keys=tcache.keys()
keys.reverse()
while keys and (len(keys) > max_cache or keys[-1] < t):
key=keys[-1]
q=tcache[key]
del tcache[key]
del cache[q]
del keys[-1]
if cache.has_key(query):
k, r = cache[query]
if k > t:
result=r
if not compressed: result=decompress(r)
else:
result=apply(DB__.query, query)
r=compress(result)
cache[query]= now, r
if compressed: return r
return result
def __call__(self,REQUEST=None):
if REQUEST is None: REQUEST=self.REQUEST
try: DB__=getattr(self, self.connection_id)()
except: raise 'Database Error', (
'%s is not connected to a database' % self.id)
argdata=self._argdata(REQUEST)
query=self.template(self,argdata)
result=DB__.query(query)
result=Aqueduct.RDB.File(StringIO(result))
if self.cache_time_:
result=self._cached_result(DB__, (query, self.max_rows_))
else: result=DB__.query(query, self.max_rows_)
if hasattr(self, '_v_brain'): brain=self._v_brain
else: brain=getBrain(self)
result=Aqueduct.RDB.File(StringIO(result),brain)
columns=result._searchable_result_columns()
if columns != self._col: self._col=columns
return result
......@@ -119,13 +202,16 @@ class DA(
argdata=decodestring(argdata)
argdata=self.rotor.decrypt(argdata)
digest,argdata=argdata[:16],argdata[16:]
if md5.new(argdata).digest() != digest:
if md5new(argdata).digest() != digest:
raise 'Bad Request', 'Corrupted Data'
argdata=marshal.loads(argdata)
query=apply(self.template,(self,),argdata)
result=DB__.query(query)
result=zlib.compress(result,1)
result=md5.new(result).digest()+result
if self.cache_time_:
result=self._cached_result(DB__, query, 1)
else:
result=DB__.query(query, self.max_rows_)
result=compress(result,1)
result=md5new(result).digest()+result
result=self.rotor.encrypt(result)
result=base64.encodestring(result)
RESPONSE['content-type']='text/X-PyDB'
......@@ -136,22 +222,108 @@ class DA(
result=str(RESPONSE.exception())
serial=str(DateTime.now())
if log_file:
l=string.find(result,"Traceback (innermost last):")
l=find(result,"Traceback (innermost last):")
if l >= 0: l=result[l:]
else: l=v
log_file.write("%s\n%s %s, %s:\n%s\n" % (
'-'*30, serial, self.id, t, l))
log_file.flush()
serial="Error number: %s\n" % serial
serial=zlib.compress(serial,1)
serial=md5.new(serial).digest()+serial
serial=compress(serial,1)
serial=md5new(serial).digest()+serial
serial=self.rotor.encrypt(serial)
serial=base64.encodestring(serial)
RESPONSE.setBody(serial)
def __getitem__(self, key):
self._arg[key] # raise KeyError if not an arg
return Traverse(self,{},key)
class Traverse:
"""Helper class for 'traversing' searches during URL traversal
"""
_r=None
_da=None
def __init__(self, da, args, name=None):
self._da=da
self._args=args
self._name=name
def __bobo_traverse__(self, REQUEST, key):
name=self._name
da=self._da
args=self._args
if name:
args[name]=key
return self.__class__(da, args)
elif da._arg.has_key(key): return self.__class__(da, args, key)
results=da(args)
if results:
if len(results) > 1:
try: return results[atoi(key)].__of__(da)
except: raise KeyError, key
else: raise KeyError, key
r=self._r=results[0].__of__(da)
if hasattr(r,'__bobo_traverse__'):
try: return r.__bobo_traverse__(REQUEST, key)
except: pass
try: return getattr(r,key)
except AttributeError, v:
if v!=key: raise AttributeError, v
return r[key]
def __getattr__(self, name):
r=self._r
if hasattr(r, name): return getattr(r,name)
return getattr(self._da, name)
braindir=SOFTWARE_HOME+'/Extensions'
def getBrain(self,
modules={},
):
'Check/load a class'
module=self.class_file_
class_name=self.class_name_
if not module and not class_name:
c=Aqueduct.RDB.NoBrains
self._v_brain=c
return c
if modules.has_key(module):
m=modules[module]
else:
d,n = os.path.split(module)
if d: raise ValueError, (
'The file name, %s, should be a simple file name' % module)
m={}
exec open("%s/%s.py" % (braindir, module)) in m
modules[module]=m
if not m.has_key(class_name): raise ValueError, (
'The class, %s, is not defined in file, %s' % (class_name, module))
c=m[class_name]
if not hasattr(c,'__bases__'):raise ValueError, (
'%s, is not a class' % class_name)
self._v_brain=c
return c
##############################################################################
#
# $Log: DA.py,v $
# Revision 1.16 1997/12/05 21:33:13 jim
# major overhall to add record addressing, brains, and support for new interface
#
# Revision 1.15 1997/11/26 20:06:03 jim
# New Architecture, note that backward compatibility tools are needed
#
......
......@@ -4,5 +4,5 @@
# install connection.dtml
# install daAdd.dtml
# install edit.dtml
# install main.dtml
# install advanced.dtml
# install www www/%(package)s
<HTML><HEAD><TITLE><!--#var title_or_id--> Advanced Options</TITLE></HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<!--#var manage_tabs-->
<H2><!--#var title_or_id--> Advanced Options</H2>
<form action="manage_advanced" method="POST">
<table>
<tr> <th>ID</th>
<td><!--#var id--></td></tr>
<tr> <th>Title</th>
<td><!--#var title--></td></tr>
<tr> <th>Remote access encyption key</th>
<td><input name="key" size="40"
value="<!--#var key-->"></td></tr>
<tr> <th>Maximum number of rows retrieved</th>
<td><input name="max_rows:int" size="10"
value="<!--#var max_rows_-->"></td></tr>
<tr> <th>Maximum number of results in the cache</th>
<td><input name="max_cache:int" size="10"
value="<!--#var max_cache_-->"></td></tr>
<tr> <th>Maximum time (seconds) to cache results</th>
<td><input name="cache_time:int" size="10"
value="<!--#var cache_time_-->"></td></tr>
<tr><td> </td></tr>
<tr><td colspan=2><br><br>
You may specify a <strong>class</strong> for the data records. This
class must be defined in a file that resides in the <code>Extensions</code>
directory of this Principia installation.
</td>
<tr> <th>Class name</th>
<td><input name="class_name" size="40"
value="<!--#var class_name_-->"></td></tr>
<tr> <th>Class file</th>
<td><input name="class_file" size="40"
value="<!--#var class_file_-->"></td></tr>
<tr><td> </td></tr>
<tr><td></td><td><input type="SUBMIT" name="SUBMIT" value="Change"></td></tr>
</table>
</form>
</body></html>
......@@ -7,21 +7,19 @@
<table>
<tr> <td><strong>Id:</strong></td>
<tr> <th>Id</th>
<td><input name="id" size="40"></td></tr>
<tr> <td><strong>Title:</strong></td>
<tr> <th>Title</th>
<td><input name="title" size="40"></td></tr>
<tr> <td><strong>Access key:</strong></td>
<td><input name="key" size="40"></td></tr>
<tr> <td><strong>Arguments:</strong></td>
<tr> <th>Arguments</th>
<td><input name="arguments" size="40"></td></tr>
<tr> <td colspan=2><strong>Query template:</strong><br>
<tr> <td colspan=2><strong>Query template</strong><br>
<textarea name="template:text" rows=10 cols=60>
select *
from data
</textarea></td></tr>
<tr><td></td><td>
<tr><td><input type="hidden" name="key" value=""></td><td>
<input type="SUBMIT" name="SUBMIT" value="Add Server Query">
</td></tr>
......
......@@ -4,78 +4,17 @@
__doc__='''short description
$Id: dbi_db.py,v 1.3 1997/09/11 23:53:28 jim Exp $'''
$Id: dbi_db.py,v 1.4 1997/12/05 21:33:16 jim Exp $'''
# Copyright
#
# Copyright 1996 Digital Creations, L.C., 910 Princess Anne
# Copyright 1997 Digital Creations, Inc, 910 Princess Anne
# Street, Suite 300, Fredericksburg, Virginia 22401 U.S.A. All
# rights reserved. Copyright in this software is owned by DCLC,
# unless otherwise indicated. Permission to use, copy and
# distribute this software is hereby granted, provided that the
# above copyright notice appear in all copies and that both that
# copyright notice and this permission notice appear. Note that
# any product, process or technology described in this software
# may be the subject of other Intellectual Property rights
# reserved by Digital Creations, L.C. and are not licensed
# hereunder.
#
# Trademarks
#
# Digital Creations & DCLC, are trademarks of Digital Creations, L.C..
# All other trademarks are owned by their respective companies.
#
# No Warranty
#
# The software is provided "as is" without warranty of any kind,
# either express or implied, including, but not limited to, the
# implied warranties of merchantability, fitness for a particular
# purpose, or non-infringement. This software could include
# technical inaccuracies or typographical errors. Changes are
# periodically made to the software; these changes will be
# incorporated in new editions of the software. DCLC may make
# improvements and/or changes in this software at any time
# without notice.
#
# Limitation Of Liability
#
# In no event will DCLC be liable for direct, indirect, special,
# incidental, economic, cover, or consequential damages arising
# out of the use of or inability to use this software even if
# advised of the possibility of such damages. Some states do not
# allow the exclusion or limitation of implied warranties or
# limitation of liability for incidental or consequential
# damages, so the above limitation or exclusion may not apply to
# you.
#
#
# If you have questions regarding this software,
# contact:
#
# Jim Fulton, jim@digicool.com
#
# (540) 371-6909
#
# $Log: dbi_db.py,v $
# Revision 1.3 1997/09/11 23:53:28 jim
# Made RDB rendered remove 'l' from ends if ints.
#
# Revision 1.2 1997/08/06 14:26:45 jim
# *** empty log message ***
#
# Revision 1.1 1997/08/06 14:24:41 jim
# *** empty log message ***
#
# Revision 1.1 1997/07/25 15:49:42 jim
# initial
#
# Revision 1.1 1997/01/27 22:11:16 jim
# *** empty log message ***
#
#
# rights reserved.
#
__version__='$Revision: 1.3 $'[11:-2]
__version__='$Revision: 1.4 $'[11:-2]
import string, sys
from string import strip, split, find, join
failures=0
calls=0
......@@ -108,13 +47,12 @@ class DB:
if r[-1:]=='L' and type(v) is not StringType: r=r[:-1]
return r
def query(self,query_string):
def query(self,query_string, max_rows=9999999):
global failures, calls
calls=calls+1
try:
c=self.cursor
queries=filter(None,
map(string.strip,string.split(query_string, '\0')))
queries=filter(None, map(strip,split(query_string, '\0')))
if not queries: raise 'Query Error', 'empty query'
if len(queries) > 1:
result=[]
......@@ -129,7 +67,7 @@ class DB:
query_string=queries[0]
r=c.execute(query_string)
if r is None:
result=c.fetchall()
result=c.fetchmany(max_rows)
desc=c.description
else:
result=((query_string, str(`r`), calls),)
......@@ -141,12 +79,14 @@ class DB:
c.close()
self.db.rollback()
failures=failures+1
if ((string.find(mess,": invalid") < 0 and
string.find(mess,"PARSE") < 0) or
if ((find(mess,": invalid") < 0 and
find(mess,"PARSE") < 0) or
# DBI IS stupid
string.find(mess,"Error while trying to retrieve text for error") > 0 or
# If we have a large number of consecutive failures, our connection
# is probably dead.
find(mess,
"Error while trying to retrieve text for error") > 0
or
# If we have a large number of consecutive failures,
# our connection is probably dead.
failures > 100
):
# Hm. maybe the db is hosed. Let's try once to restart it.
......@@ -162,10 +102,10 @@ class DB:
else: raise sys.exc_type, sys.exc_value, sys.exc_traceback
if result:
result=string.joinfields(
result=join(
map(
lambda row, self=self:
string.joinfields(map(self.str,row),'\t'),
join(map(self.str,row),'\t'),
result),
'\n')+'\n'
else:
......@@ -173,8 +113,8 @@ class DB:
return (
"%s\n%s\n%s" % (
string.joinfields(map(lambda d: d[0],desc), '\t'),
string.joinfields(
join(map(lambda d: d[0],desc), '\t'),
join(
map(
lambda d, defs=self.defs: "%d%s" % (d[2],defs[d[1]]),
desc),
......
<HTML>
<HEAD>
<TITLE>
Edit
<!--#if title-->
<!--#var title-->
<!--#else title-->
<!--#var id-->
<!--#/if title-->
</TITLE>
</HEAD>
<HTML><HEAD><TITLE>Edit <!--#var title_or_id--></TITLE></HEAD>
<BODY BGCOLOR="#FFFFFF" LINK="#000099" VLINK="#555555">
<H2>
Edit
<!--#if title-->
<!--#var title-->
<!--#else title-->
<!--#var id-->
<!--#/if title-->
</H2>
<P>
<!--#var manage_tabs-->
<H2>Edit <!--#var title_or_id--></H2>
<form action="<!--#var URL2-->/<!--#var id-->/manage_edit"
method="<!--#var web__form__method-->">
<form action="manage_edit" method="POST">
<table>
<tr> <td><strong>ID:</strong></td>
<tr> <th>ID</th>
<td><!--#var id--></td></tr>
<tr> <td><strong>Title:</strong></td>
<tr> <th>Title</th>
<td><input name="title" size="40"
value="<!--#var title-->"></td></tr>
<tr> <td><strong>Access key:</strong></td>
<td><input name="key" size="40" value="<!--#var key-->"></td></tr>
<tr> <td><strong>Arguments:</strong></td>
<td><input name="arguments" size="40" value="<!--#var arguments_src-->"></td>
<tr> <th>Arguments</th>
<td><input name="arguments" size="40"
value="<!--#var arguments_src-->"></td>
</tr>
<tr> <td colspan=2><strong>Query template:</strong><br>
<tr> <td colspan=2><strong>Query template</strong><br>
<textarea name="template:text" rows=10 cols=60>
<!--#var quoted_src-->
</textarea></td></tr>
<tr><td></td><td>
<input type="SUBMIT" name="SUBMIT" value="OK">
<input type="SUBMIT" name="SUBMIT" value="Cancel">
<!--#if HTTP_REFERER-->
<INPUT NAME="CANCEL_ACTION" TYPE="HIDDEN" VALUE="<!--#var HTTP_REFERER-->">
<!--#else HTTP_REFERER-->
<INPUT NAME="CANCEL_ACTION" TYPE="HIDDEN" VALUE="<!--#var URL2-->/manage">
<!--#/if HTTP_REFERER-->
</td></tr>
<tr><td></td><td><input type="SUBMIT" name="SUBMIT" value="Change"></td></tr>
</table>
</form>
</body>
</html>
</body></html>
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment