pysesame-0.24/ 0000755 0001750 0001750 00000000000 10636042477 012240 5 ustar nacho nacho pysesame-0.24/examples.py 0000755 0001750 0001750 00000016325 10354522632 014433 0 ustar nacho nacho # 20051228 *pike
# 20050628 *pike
# very simple example scripts for pySesame
# this does the same as tests.py, but in a human way.
import pySesame
# CONFIG ---------------------
# select an *empty* test database, plain rdf sail
# on which the user has read and write access
url = 'http://localhost:8080'
path = '/sesame/'
user = 'yourusername'
password = 'youruserpassword'
repository = 'yourrepositoryid'
# toggle tests to perform
tests = {}
tests["anon"] = True
tests["login"] = True
tests["clear"] = True
tests["upload"] = True
tests["uploadbroken"] = True
tests["uploadurl"] = True
tests["extract"] = True
tests["rdql"] = True
tests["rdqlrdf"] = True
tests["serqls"] = True
tests["serqlc"] = True
tests["remove"] = True
# ----------------------------
#import some example data
from tests_data import w3c, w3cRemoved, w3cResult, postURL
from tests_data import serqlSQuery, serqlCQuery, rdqlQuery, rdqlQueryResult
from tests_data import rdqlQueryRDF, serqlCQueryRDF, postcon
print "Note: \nmake sure your repository (%s@%s%s) is *empty*. \nIt will be trashed during the tests."%(repository,url,path)
if tests["anon"]:
resp = raw_input("\nNext test: anonymous login. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
# test some features on anonymous acces
print 24*'='
print 'Testing anonymous connection'
connection = pySesame.SesameConnection(url, path, 'debug')
repos = connection.repositories()
print 'There are %s repositories available for the anonymous user:'%len(repos)
print "\tsesame id\t:read\t:write\t:title"
for repo in repos:
print "\t%(id)s\t:%(readable)s\t:%(writeable)s\t:%(title)s"%repo
print 'closing anonymous connection'
connection.close()
# test login / logoff
if tests["login"]:
resp = raw_input("\nNext test: login / logoff. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'Testing login connection'
connection = pySesame.SesameConnection(url, path, 'debug')
connection.login(user, password)
print 'received sessionid %s '%connection.cookie
repos = connection.repositories()
print 'There are %s repositories available for user %s:'%(len(repos),user)
print "\tsesame id\t:read\t:write\t:title"
for repo in repos:
print "\t%(id)s\t:%(readable)s\t:%(writeable)s\t:%(title)s"%repo
print 'logging out'
connection.logout()
print 'closing sesame connection .. which is useless because I already logged out'
connection.close()
# more tests require logging in
resp = raw_input("\nNext test: clear. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print
print 'loggin in again, for the next set of tests:'
connection = pySesame.SesameConnection(url, path, 'debug')
connection.login(user, password)
if tests["clear"]:
print 24*'='
print 'Testing clearing repo %s'%repository
connection.clear(repository)
print "....ok"
if tests["upload"]:
resp = raw_input("\nNext test: upload. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'Testing uploading good rdf'
connection.uploadData(repository, postcon)
print 'good, clearing repo %s'%repository
connection.clear(repository)
if tests["uploadbroken"]:
resp = raw_input("\nNext test: uploadbroken. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'Testing uploading broken rdf'
try:
connection.uploadData(repository,postcon[:100])
print 'bad ! .. I didn\'t get get an error ?'
except:
print 'good !.. I got an error :)'
if tests["uploadurl"]:
resp = raw_input("\nNext test: uploadurl. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'Testing uploading from url'
connection.uploadURL(repository, postURL)
print 'good, clearing repo %s'%repository
connection.clear(repository)
if tests["extract"]:
resp = raw_input("\nNext test: extract. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'Testing extracting the repo'
print 'uploading----------'
print w3c
connection.uploadData(repository, w3c)
result = connection.extract(repository, 'on', 'on')
print '--------------------'
if result!=w3cResult:
print "unexpected result ? ----------"
print result
print '--------------------'
else:
print "ok"
print 'clearing repo %s'%repository
connection.clear(repository)
if tests["rdql"]:
resp = raw_input("\nNext test: rdql. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'testing rdqlquery with table result'
print 'uploading some dummy data ..'
connection.uploadData(repository, postcon)
print 'submitting query----------'
print rdqlQuery
result = connection.tableQuery(repository, 'RDQL', rdqlQuery)
if result != rdqlQueryResult:
print "unexpected result ? ----------"
print result
print '--------------------'
else:
print "ok"
print 'clearing repo %s'%repository
connection.clear(repository)
if tests["rdqlrdf"]:
resp = raw_input("\nNext test: rdqlrdf. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'testing rdqlquery with rdf result'
print 'uploading some dummy data ..'
connection.uploadData(repository, postcon)
print 'submitting query----------'
print rdqlQuery
result = connection.tableQuery(repository, 'RDQL', rdqlQuery,'rdf', 'rdfxml')
if result != rdqlQueryRDF:
print "unexpected result ? ----------"
print result
print '--------------------'
else:
print "ok"
print 'clearing repo %s'%repository
connection.clear(repository)
if tests["serqls"]:
resp = raw_input("\nNext test: serqls. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'testing serql select query'
print 'uploading some dummy data ..'
connection.uploadData(repository, postcon)
print 'submitting query----------'
print serqlSQuery
result = connection.tableQuery(repository, 'SeRQL', serqlSQuery)
if result != rdqlQueryResult:
print "unexpected result ? ----------"
print result
print '--------------------'
else:
print "ok"
print 'clearing repo %s'%repository
connection.clear(repository)
if tests["serqlc"]:
resp = raw_input("\nNext test: serqlc. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'testing serql construct query'
print 'uploading some dummy data ..'
connection.uploadData(repository, postcon)
print 'submitting query----------'
print serqlCQuery
result = connection.graphQuery(repository, 'SeRQL', serqlCQuery, 'rdfxml')
if result != serqlCQueryRDF:
print "unexpected result ? ----------"
print result
print '--------------------'
else:
print "ok"
print 'clearing repo %s'%repository
connection.clear(repository)
if tests["remove"]:
resp = raw_input("\nNext test: remove. Continue? [Y/n] ")
if (resp.lower()=='n'): exit()
print 24*'='
print 'testing remove query'
print 'uploading some dummy data ..'
connection.uploadData(repository, w3c)
print 'submitting query----------'
connection.remove(repository, '',
'',
'"World Wide Web Consortium"')
result = connection.extract(repository, 'on', 'on')
if result != w3cRemoved:
print "unexpected result ? ----------"
print result
print '--------------------'
else:
print "ok"
print 'clearing repo %s'%repository
connection.clear(repository)
connection.close()
pysesame-0.24/pySesame.log 0000644 0001750 0001750 00000000462 10262714616 014527 0 ustar nacho nacho 2005-06-28 18:01:51,684 INFO SesameConnection starting up...
2005-06-28 18:01:51,685 DEBUG GET:http://localhost:7080/sesame/servlets/listRepositories
2005-06-28 18:01:51,691 DEBUG OK
2005-06-28 18:01:51,749 DEBUG POST:http://localhost:7080/sesame/servlets/logout
2005-06-28 18:01:51,754 DEBUG OK: 23 bytes
pysesame-0.24/pySesame.py 0000755 0001750 0001750 00000027703 10354521655 014411 0 ustar nacho nacho #
# pySesame is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# pySesame is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# A copy of the full General Public License is available at
# http://www.gnu.org/copyleft/gpl.html and from the Free Software
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
#
# 20050606 *pike
# This version was modified to work with python 2>, which has
# notable differences in the httplib module. I have kept the
# interface the same.
#
import logging
from string import split
from urllib2 import urlopen
from urllib2 import Request
from urllib import urlencode
from xml.sax.handler import ContentHandler
from xml.sax import make_parser
query_paths = {'login':'servlets/login',
'logout':'servlets/logout',
'repositories':'servlets/listRepositories',
'query':'servlets/evaluateTableQuery',
'construct':'servlets/evaluateGraphQuery',
'extract':'servlets/extractRDF',
'uploadData':'servlets/uploadData',
'uploadURL':'servlets/uploadURL',
'clear':'servlets/clearRepository',
'remove':'servlets/removeStatements?',
}
class SesameConnection:
"The main class for communicating with a Sesame Server."
def __init__(self, url, path, logLevel="warning", logPath=""):
self.hdlr = None
self.setLogger(logLevel, logPath)
self.url = url
self.path = path
self.cookie = ""
self.resetQuery()
self.logger.info('SesameConnection starting up...')
def close(self):
self.logout()
self.logger.info('...SesameConnection closing down.')
self.logger.removeHandler(self.hdlr)
def setLogger(self, level, path):
if self.hdlr: self.logger.removeHandler(self.hdlr)
levelDict = {'debug':logging.DEBUG,
'info':logging.INFO,
'warning':logging.WARNING,
'error':logging.ERROR,
'critical':logging.CRITICAL}
self.logger = logging.getLogger()
self.hdlr = logging.FileHandler(path + 'pySesame.log')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
self.hdlr.setFormatter(formatter)
self.logger.addHandler(self.hdlr)
self.logger.setLevel(levelDict[level])
def resetQuery(self):
self.query = {}
def doRequest(self, request, handler=None, post=None):
# Sesame uses the text/plain content type for XML responses
request.add_header("Accept", "text/plain")
request.add_header("User-Agent", "PySesame 0.2")
if self.cookie != "":
cookie_string = "sesame_sid=%s" % self.cookie
request.add_header("Cookie", cookie_string)
if post != None:
request.add_data(post)
self.logger.debug(request.get_method()+":" + request.get_full_url())
try:
response = urlopen(request)
# debug alls ok. i'd like to print
# the Status header, but urllib2 has already eaten it
# and thrown its appropriate errors or followed
# any redirects etc
if response.headers.has_key('Content-Length'):
self.logger.debug("OK: "+response.headers['Content-Length']+" bytes")
else:
self.logger.debug("OK")
except Exception, error:
self.logger.exception(error)
raise SesameServerError(error)
if response:
if handler == "login":
if not self.CookieHandler(response):
raise SesameServerError("No SessionID in cookie")
# returns None on succes ....
elif handler:
saxparser = make_parser()
saxparser.setContentHandler(handler)
parsed = saxparser.parse(response)
response.close()
return handler.results
else:
results = response.readlines()
resultString = ""
resultString = resultString.join(results)
return resultString
def login(self, user, password):
"""
Gets your cookie.
Returns None if successful.
Throws an error if not
"""
self.resetQuery()
self.query['user'] = user
self.query['password'] = password
query = urlencode(self.query)
base = self.path + query_paths['login']
request = Request(self.url+base)
return self.doRequest(request, "login",query)
def logout(self):
"""
Logs you out.
Returns 'Logged out successfully' if successful.
"""
self.resetQuery()
base = self.path + query_paths['logout']
request = Request(self.url+base)
return self.doRequest(request,None,"post")
def repositories(self):
"""
Returns a list of dictionaries about available repositories.
The dictionary keys are "id", "title", "readable" and "writeable"
"""
self.resetQuery()
base = self.path + query_paths['repositories']
query = urlencode(self.query)
request = Request(self.url + base + query)
handler = RepositoriesHandler()
return self.doRequest(request, handler)
def tableQuery(self, repository, queryLanguage, query,
resultFormat="xml", serialization = "rdfxml"):
"""
xml format results are returned as a data structure as follows:
* a dictionary with two keys, "header" and "tuples"
* header is a list of string values
* tuples is a list of lists.
* each sub-list is like a row in the table
* each sub-list is made up of dictionaries, wherein:
* the key a string made from the text of the node
* the value is the type ('uri', 'literal' or 'bNode')
All other formats are returned as a string.
"""
self.resetQuery()
self.query['repository'] = repository
self.query['queryLanguage'] = queryLanguage
self.query['resultFormat'] = resultFormat
self.query['query'] = query
if resultFormat == 'rdf':
self.query['serialization'] = serialization
handler = None
elif resultFormat == 'html':
handler = None
else:
handler = SelectQueryHandler()
base = self.path + query_paths['query']
request = Request(self.url+base)
query = urlencode(self.query)
return self.doRequest(request, handler, query)
def graphQuery(self, repository, queryLanguage, query,
serialization = "rdfxml"):
"""
Returns a string.
"""
self.resetQuery()
self.query['repository'] = repository
self.query['queryLanguage'] = queryLanguage
self.query['query'] = query
self.query['serialization'] = serialization
handler = None
# query_path *was* 'query' .. seemed wrong ..
base = self.path + query_paths['construct']
request = Request(self.url+base)
query = urlencode(self.query)
return self.doRequest(request, handler, query)
def extract(self, repository, schema="no", data="no", explicitOnly="no",
niceOutput="no", serialization="rdfxml"):
"""
Returns a string.
"""
self.resetQuery()
self.query['repository'] = repository
self.query['schema'] = schema
self.query['data'] = data
self.query['explicitOnly'] = explicitOnly
self.query['niceOutput'] = niceOutput
self.query['serialization'] = serialization
base = self.path + query_paths['extract']
query = urlencode(self.query)
request = Request(self.url + base + '?' + query)
return self.doRequest(request)
def uploadData(self, repository, data = "", dataFormat="rdfxml",
baseURL="", format="xml", verifyData="yes"):
"""
Adds a string ("data") of RDF data to the repository.
Returns 1 if successful.
"""
self.resetQuery()
self.query['data'] = data
self.query['repository'] = repository
self.query['dataFormat'] = dataFormat
self.query['baseURL'] = baseURL
self.query['format'] = format
base = self.path + query_paths['uploadData']
request = Request(self.url+base)
query = urlencode(self.query)
handler = PostHandler()
return self.doRequest(request, handler, query)
def uploadURL(self, repository, dataURL, dataFormat="rdfxml",
baseURL="", format="xml", verifyData="yes"):
"""
Attempts to add whatever is at dataURL to the repository.
Returns 1 if successful.
"""
self.resetQuery()
self.query['url'] = dataURL
self.query['repository'] = repository
self.query['dataFormat'] = dataFormat
self.query['baseURL'] = baseURL
self.query['format'] = format
base = self.path + query_paths['uploadURL']
request = Request(self.url+base)
query = urlencode(self.query)
handler = PostHandler()
return self.doRequest(request, handler, query)
def clear(self, repository, format="xml"):
"""
Clears a repository.
Returns 1 if successful.
"""
self.resetQuery()
self.query['repository'] = repository
base = self.path + query_paths['clear']
request = Request(self.url+base)
query = urlencode(self.query)
handler = PostHandler()
return self.doRequest(request, handler, query)
def remove(self, repository, subject, predicate, object,
resultFormat="xml"):
"""
Removes statements from repository.
Returns 1 if successful.
"""
self.resetQuery()
self.query['repository'] = repository
base = self.path + query_paths['remove']
request = Request(self.url+base)
if subject:
self.query['subject'] = subject
if predicate:
self.query['predicate'] = predicate
if object:
self.query['object'] = object
self.query['resultFormat'] = resultFormat
query = urlencode(self.query)
handler = PostHandler()
return self.doRequest(request, handler, query)
def CookieHandler(self, response):
value = False
info = response.info()
cookies = info.getheaders("Set-Cookie")
for cookie in cookies:
if 'sesame_sid' in cookie:
half = split(cookie,';')[0]
value = split(half,'=')[-1]
self.logger.debug('Using cookie %s' % value)
self.cookie = value
return value
class RepositoriesHandler(ContentHandler):
"""Creates a list of dictionaries describing available servers."""
def __init__(self):
self.inRepository = 0
self.inTitle = 0
self.repository = {}
self.results = []
def startElement(self, name, attrs):
if self.inRepository and name == "title":
self.inTitle = 1
self.title = ""
elif name == "repository":
self.repository['id'] = attrs.get('id', '')
self.repository['readable'] = attrs.get('readable', '')
self.repository['writeable'] = attrs.get('writeable', '')
self.inRepository = 1
def characters(self, ch):
if self.inTitle:
self.title = ch
def endElement(self, name):
if name == 'title':
self.repository['title'] = self.title
self.inTitle = 0
elif name == 'repository':
self.results.append(self.repository)
self.repository = {}
self.inRepository = 0
class SelectQueryHandler(ContentHandler):
"""."""
# this doesn't handle the header yet, right?
def __init__ (self):
self.types = ['uri', 'literal', 'bNode','null']
self.inTuple = 0
self.inResult = 0
self.inHeader = 0
self.header = []
self.tuple = []
self.tuples = []
def startElement(self, name, attrs):
if self.inTuple and name in self.types:
self.inResult = 1
self.value = ""
self.type = name
elif name == 'tuple':
self.inTuple = 1
self.inResult = 0
elif name == 'columnName':
self.inHeader = 1
def characters(self, ch):
if self.inResult:
self.value=ch
self.tuple.append({'value':self.value,'type':self.type})
if self.inHeader:
self.header.append(ch)
def endElement(self, name):
if name == 'tuple':
self.tuples.append(self.tuple)
self.tuple = []
self.inTuple = 0
if name in self.types:
self.inResult = 0
elif name == 'columnName':
self.inHeader = 0
elif name == 'tableQueryResult':
self.results = {'header':self.header,
'tuples':self.tuples}
class PostHandler(ContentHandler):
"""Returns 1 if successful. Throws an exception if not."""
def __init__(self):
self.results = 1
self.inMsg = 0
self.line = ""
self.elements = ['msg', 'line', 'column']
def startElement(self, name, attrs):
if name == "error":
self.results = 0
if name in ['status', 'notification', 'warning', 'error']:
self.tag = name
if name in self.elements:
self.inMsg = 1
def characters(self, ch):
if self.inMsg:
self.line += "%s: %s, " % (self.tag, ch)
def endElement(self, name):
if name == "error":
raise SesameServerError(self.line)
self.line = ""
if name in self.elements:
self.inMsg = 0
class SesameServerError(Exception):
pass
pysesame-0.24/tests.py 0000755 0001750 0001750 00000011105 10354522713 013746 0 ustar nacho nacho
# py_sesame tests.py
#
# this is a testsuite as used by python's unittest module
# it runs a dozen of tests, catches exceptions, and prints reports
#
# if the test fails it doesn't necessarily mean Sesame is broken,
# it only means the test failed. please check out the purpose
# of the test and the nature of the failure. usually, Sesame
# returned a result in a slightly different format then the test
# expected; e.g. rdf/xml with some extra whitespace, which is
# harmless.
import unittest
import pySesame
# CONFIG ---------------------
# select an *empty* test database, plain rdf sail
# on which the user has read and write access
url = 'http://localhost:8080'
path = '/sesame/'
user = 'yourusername'
password = 'youruserpassword'
repository = 'yourrepositoryid'
# ----------------------------
#20050628 *pike moved data out of the file
#20050628 *pike added serql tests
#import some example data
from tests_data import w3c, w3cRemoved, w3cResult, postURL
from tests_data import serqlSQuery, serqlCQuery, rdqlQuery, rdqlQueryResult
from tests_data import rdqlQueryRDF, serqlCQueryRDF, postcon
class AnonymousCase(unittest.TestCase):
def setUp(self):
self.connection = pySesame.SesameConnection(url, path, 'debug')
def tearDown(self):
self.connection.close()
def testRepositories(self):
# This assumes that the server has no repositories
# accessible to anonymous. You can probably ignore it.
assert self.connection.repositories() == [], \
'There seem to be repositories available for the anonymous user.'
def testLogin(self):
assert self.connection.login(user, password) == None, \
'Login error.'
def testLogout(self):
correct = 'Logged out successfully'
self.connection.login(user, password)
assert self.connection.logout() == correct, \
'Logout error.'
class TestUserCase(unittest.TestCase):
def setUp(self):
self.connection = pySesame.SesameConnection(url, path, 'debug')
self.connection.login(user, password)
def tearDown(self):
self.connection.clear(repository)
self.connection.logout()
self.connection.close()
def testRepositories(self):
repos = self.connection.repositories()
ok = True
for repo in repos:
if repo["id"]==repository:
ok = ok and repo["writeable"]
ok = ok and repo["readable"]
assert ok, 'Repository %s is not readable or writable'%repository
def testClearRepository(self):
assert self.connection.clear(repository) == 1, \
'Error in clear.'
def testUploadBadData(self):
self.assertRaises(pySesame.SesameServerError,
self.connection.uploadData, postcon[:100])
def testUploadData(self):
assert self.connection.uploadData(repository, postcon) == 1, \
'Error in upload.'
self.connection.clear(repository)
def testUploadURL(self):
assert self.connection.uploadURL(repository, postURL) == 1, \
'Error in URL load.'
def testExtract(self):
"This is rather sensitive to the formatting of the result."
self.connection.clear(repository)
self.connection.uploadData(repository, w3c)
assert self.connection.extract(repository, 'on', 'on') == w3cResult, \
'Error in RDF extract.'
def testRDQLtable(self):
self.connection.clear(repository)
self.connection.uploadData(repository, postcon)
assert self.connection.tableQuery(repository, 'RDQL', rdqlQuery) == \
rdqlQueryResult, 'Error in RDQL table query.'
def testRDQLrdf(self):
self.connection.clear(repository)
self.connection.uploadData(repository, postcon)
assert self.connection.tableQuery(repository, 'RDQL', rdqlQuery, \
'rdf', 'rdfxml') == rdqlQueryRDF, \
'RDQL Query, RDF output does not return expected results'
def testSeRQLselect(self):
self.connection.clear(repository)
self.connection.uploadData(repository, postcon)
assert self.connection.tableQuery(repository, 'SeRQL', serqlSQuery) == \
rdqlQueryResult, 'SeRQL select query does not return expected results'
def testSeRQLconstruct(self):
self.connection.clear(repository)
self.connection.uploadData(repository, postcon)
assert self.connection.graphQuery(repository, 'SeRQL', serqlCQuery, \
'rdfxml') == serqlCQueryRDF, \
'SeRQL Construct query, RDF format does not return expected results'
def testRemove(self):
self.connection.clear(repository)
self.connection.uploadData(repository, w3c)
self.connection.remove(repository, '', \
'', \
'"World Wide Web Consortium"')
assert self.connection.extract(repository, 'on', 'on') == \
w3cRemoved, 'Remove does not return expected results'
if __name__ == "__main__":
unittest.main()
pysesame-0.24/tests_data.py 0000644 0001750 0001750 00000015746 10263307171 014751 0 ustar nacho nacho
w3c = """
World Wide Web Consortium
"""
w3cRemoved = '''
'''
w3cResult = '''
World Wide Web Consortium
'''
# Using Shelley Powers examples from _Practical_RDF_. Should be
# pretty stable.
postURL = "http://burningbird.net/articles/monsters1.rdf"
serqlSQuery = """
select date
from {Resource} rdf:type {pstcn:Movement};
pstcn:movementType {"Add"};
dc:date {date}
using namespace
pstcn = ,
dc =
"""
serqlCQuery = """
construct
{Resource} dc:date {date}
from
{Resource} rdf:type {pstcn:Movement};
pstcn:movementType {"Add"};
dc:date {date}
using namespace
pstcn = ,
dc =
"""
rdqlQuery = """SELECT ?date
WHERE
(?resource, , ),
(?resource, ,?value),
(?resource, , ?date)
AND (?value eq "Add")
USING pstcn FOR ,
rdf for ,
dc for """
rdqlQueryResult = {'header':[u'date'], 'tuples': [[{'type':u'literal','value': '1998-01-01T00:00:00-05:00'}]]}
rdqlQueryRDF = """
1998-01-01T00:00:00-05:00
"""
serqlCQueryRDF = """
1998-01-01T00:00:00-05:00
"""
postcon = """
Tale of Two Monsters: Legends
When I think of "monsters" I think of the creatures of
legends and tales, from the books and movies, and
I think of the creatures that have entertained me for years.
Part 1 of four-part series on cryptozoology, legends,
Nessie the Loch Ness Monster and the giant squid.
1999-08-01T00:00:00-06:00
Shelley Powers
Burningbird Network
Active
2003-12-01T00:00:00-06:00
legends
giant squid
loch ness monster
Architeuthis Dux
Nessie
http://www.pibburns.com/cryptozo.htm
http://www.nrcc.utmb.edu/
text/html
XHTML 1.0 Strict
CSS Validation
HTML User agent
stylesheet
http://burningbird.net/de.css
logo
http://burningbird.net/mm/dynamicearth.jpg
Cryptozooloy
First in the Tale of Two Monsters series.
A Tale of Two Monsters: Architeuthis Dux (Giant Squid)
Second in the Tale of Two Monsters series.
Nessie, the Loch Ness Monster
Fourth in the Tale of Two Monsters series.
Add
New Article
1998-01-01T00:00:00-05:00
Move
Moved to separate dynamicearth.com domain
1999-10-31:T00:00:00-05:00
Move
Collapsed into Burningbird
2002-11-01
"""