Page MenuHomec4science

Attribute.py
No OneTemporary

File Metadata

Created
Wed, Jun 19, 12:05

Attribute.py

# This file is part of pybliographer
#
# Copyright (C) 1998-2006 Frederic GOBRY
# Email : gobry@pybliographer.org
#
# This program 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.
#
# This program 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.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
""" Basic data types that can be used as attributes for a L{Record
<Pyblio.Store.Record>}.
Basic attributes of a record can be B{qualified} by one or more
additional sub-attributes. For instance, an attribute I{author} of
type L{Person} can have, for every Person instance, a sub-attribute of
type L{Date} that represents its birth date."""
import string, re, urlparse, os
from gettext import gettext as _
from xml import sax
from xml.sax.saxutils import escape, quoteattr
from Pyblio import I18n
re_split = re.compile (r'\W+', re.UNICODE)
class _Qualified(object):
""" Mix-in class that provides qualifiers to attributes, making
them behave like composite data types (but not arbitrarily nested
data, though)"""
def _xmlsubwrite(self, fd, offset = 1):
ws = ' ' * offset
for k, vs in self.q.items ():
fd.write (ws + '<attribute id=%s>\n' % quoteattr (k))
for v in vs:
v.xmlwrite (fd, offset + 1)
fd.write ('\n')
fd.write (ws + '</attribute>\n')
return
def deep_equal(self, other):
for k in self.q:
if not k in other.q or not len (self.q [k]) == len (other.q [k]):
return False
for x, y in zip (self.q [k], other.q [k]):
if not x.deep_equal (y):
return False
for k in other.q:
if not k in self.q:
return False
return True
def is_complete(self):
"""Returns True if the field has an actual value (ie, hasn't
been created by adding qualifiers only)"""
return True
class UnknownContent(_Qualified):
"""
An invalid type.
It is used, when you add qualifiers before you add the main field
to a record. Trying to store it will raise an error.
"""
def __init__ (self):
self.q = {}
def xmlwrite (self, fd, offset = 0):
raise Exceptions.ParserError ("Attribute.UnknownContent has qualifiers, "\
"but is empty: %s" % self.q)
def deep_equal (self, other):
if not isinstance (other, UnknownContent): return False
return _Qualified.deep_equal (self, other)
def is_complete(self):
return False
class Person(_Qualified):
''' A person name. '''
def __init__ (self, honorific = None, first = None, last = None, lineage = None,
xml = None):
self.q = {}
self.honorific = honorific
self.first = first
self.last = last
self.lineage = lineage
return
def xmlread (k, xml, inside = False):
p = k ()
for f in ('honorific', 'first', 'last', 'lineage'):
setattr (p, f, xml.attrib.get (f, None))
return p
xmlread = classmethod (xmlread)
def xmlwrite (self, fd, offset = 0):
ws = ' ' * offset
data = []
for f in ('honorific', 'first', 'last', 'lineage'):
v = getattr (self, f)
if v:
data.append ('%s=%s' % (f, quoteattr (v.encode ('utf-8'))))
data = ' '.join (data)
if not self.q:
fd.write (ws + '<person %s/>' % data)
else:
fd.write (ws + '<person %s>\n' % data)
self._xmlsubwrite (fd, offset + 1)
fd.write (ws + '</person>')
return
def index (self):
idx = []
for x in (self.first, self.last):
if x: idx = idx + map (string.lower, re_split.split(x))
return filter (None, idx)
def sort (self):
return (u'%s\0%s' % (self.last or '', self.first or '')).lower ()
def __eq__ (self, other):
return self.last == other.last and \
self.first == other.first and \
self.honorific == other.honorific and \
self.lineage == other.lineage
def __ne__ (self, other):
return self.last != other.last or \
self.first != other.first or \
self.honorific != other.honorific or \
self.lineage != other.lineage
def deep_equal (self, other):
if not self == other or not isinstance (other, Person):
return False
return _Qualified.deep_equal (self, other)
def __repr__ (self):
return "Person (%s, %s)" % (repr(self.last), repr(self.first))
def __hash__ (self):
return hash ((self.last, self.first, self.lineage, self.honorific))
class Date (_Qualified):
''' A date. '''
def __init__ (self, year = None, month = None, day = None):
self.q = {}
self.year = year
self.month = month
self.day = day
return
def xmlread (k, xml):
d = k ()
for f in ('year', 'month', 'day'):
v = xml.attrib.get (f, None)
if v: setattr (d, f, int (v))
return d
xmlread = classmethod (xmlread)
def xmlwrite (self, fd, offset = 0):
ws = ' ' * offset
data = []
for f in ('year', 'month', 'day'):
v = getattr (self, f)
if v:
data.append ('%s="%d"' % (f, v))
fd.write (ws + '<date %s' % string.join (data, ' '))
if self.q:
fd.write ('>\n')
self._xmlsubwrite (fd, offset + 1)
fd.write (ws + '</date>')
else:
fd.write ('/>')
return
def index (self):
return []
def sort (self):
return '%.4d%.2d%.2d' % (self.year or 0,
self.month or 0,
self.day or 0)
def __cmp__ (self, other):
if not isinstance (other, Date): return 1
for x, y in ((self.year, other.year),
(self.month, other.month),
(self.day, other.day)):
a = cmp (x, y)
if a: return a
return 0
def deep_equal (self, other):
if not self == other or not isinstance (other, Date):
return False
return _Qualified.deep_equal (self, other)
def __hash__ (self):
return hash ((self.year, self.month, self.day))
def __repr__ (self):
return 'Date (year = %s, month = %s, day = %s)' % (
repr (self.year), repr (self.month), repr (self.day))
class Text (unicode, _Qualified):
''' Textual data '''
def __new__(cls, *args, **kwargs):
obj = unicode.__new__(cls, *args, **kwargs)
obj.q = {}
return obj
def xmlread (k, xml):
content = xml.find ('./content')
if content is not None:
return k (content.text)
else:
return k (xml.text)
xmlread = classmethod (xmlread)
def xmlwrite (self, fd, offset = 0):
ws = ' ' * offset
if self.q:
fd.write (ws + '<text>\n')
fd.write (ws + ' <content>%s</content>\n' % escape (self.encode('utf-8')))
self._xmlsubwrite (fd, offset + 1)
fd.write (ws + '</text>')
else:
fd.write (ws + '<text>%s</text>' % escape (self.encode('utf-8')))
return
def index (self):
idx = map (string.lower, re_split.split(self))
return filter (None, idx)
def sort (self):
return self.lower ()
def deep_equal (self, other):
if self != other or not isinstance (other, Text):
return False
return _Qualified.deep_equal (self, other)
class URL (unicode, _Qualified):
''' An URL '''
def __new__(cls, *args, **kwargs):
obj = unicode.__new__(cls, *args, **kwargs)
obj.q = {}
return obj
def xmlread (k, xml):
return k (xml.attrib ['href'])
xmlread = classmethod (xmlread)
def xmlwrite (self, fd, offset = 0):
ws = ' ' * offset
fd.write (ws + '<url href=%s' % quoteattr (self.encode('utf-8')))
if self.q:
fd.write ('>\n')
self._xmlsubwrite (fd, offset + 1)
fd.write (ws + '</url>')
else:
fd.write ('/>')
return
def index (self):
# do not index the document suffix, only the server name and document page
url = urlparse.urlparse (self)
idx = re_split.split (url [1]) + \
re_split.split (os.path.splitext (url [2]) [0])
return filter (None, idx)
def sort (self):
return self
def deep_equal (self, other):
if self != other or not isinstance (other, URL):
return False
return _Qualified.deep_equal (self, other)
class ID (unicode, _Qualified):
''' An external identifier '''
def __new__(cls, *args, **kwargs):
obj = unicode.__new__(cls, *args, **kwargs)
obj.q = {}
return obj
def xmlread (k, xml):
return k (xml.attrib ['value'])
xmlread = classmethod (xmlread)
def xmlwrite (self, fd, offset = 0):
ws = ' ' * offset
fd.write (ws + '<id value=%s' % quoteattr (self.encode('utf-8')))
if self.q:
fd.write ('>\n')
self._xmlsubwrite (fd, offset + 1)
fd.write (ws + '</id>')
else:
fd.write ('/>')
return
def index (self):
return []
def sort (self):
return self
def deep_equal (self, other):
if self != other or not isinstance (other, ID):
return False
return _Qualified.deep_equal (self, other)
class Txo (_Qualified):
""" Element of a taxonomy.
In the simplest case, this can be seen as a value in a enumerated
set of possible values. The possible values are defined as
L{Pyblio.Schema.TxoItem}s, and are stored in the
L{Store.Database}, in the B{txo} attribute, and L{Store.Record}s
can contain Txo attributes which refer to these
L{Pyblio.Schema.TxoItem}s. Say you have a list of known document
types in the I{document-type} taxonomy. You can then affect the
document type to the I{type} attribute of a record with the
following operations:
>>> item = db.txo['document-type'].byname('article')
>>> record.add('type', item, Attribute.Txo)
"""
def __init__ (self, item = None):
self.q = {}
if item:
self.group = item.group
self.id = item.id
else:
self.group = None
self.id = None
return
def xmlread (k, xml):
txo = k ()
txo.group = xml.attrib ['group']
txo.id = int (xml.attrib ['id'])
return txo
xmlread = classmethod (xmlread)
def xmlwrite (self, fd, offset = 0):
ws = ' ' * offset
fd.write (ws + '<txo group="%s" id="%d"' % (self.group, self.id))
if self.q:
fd.write ('>\n')
self._xmlsubwrite (fd, offset + 1)
fd.write (ws + '</txo>')
else:
fd.write ('/>')
return
def index (self):
return [ '%s/%d' % (self.group, self.id) ]
def sort (self):
return '%s/%d' % (self.group, self.id)
def __repr__ (self):
return 'Txo (%s, %s)' % (`self.group`, `self.id`)
def __cmp__ (self, other):
try:
return cmp (self.group, other.group) or cmp (self.id, other.id)
except AttributeError:
# If 'other' is not of the proper type, simply consider
# 'self' as superior.
return 1
def deep_equal (self, other):
if not self == other or not isinstance (other, Txo):
return False
return _Qualified.deep_equal (self, other)
def __hash__ (self):
return hash ((self.group, self.id))
# A mapping between class names and class objects
N_to_C = {
'person' : Person,
'date' : Date,
'text' : Text,
'url' : URL,
'id' : ID,
'txo' : Txo,
}
# A mapping from class objects to class names
C_to_N = {}
for _k, _v in N_to_C.items (): C_to_N [_v] = _k
del _k, _v

Event Timeline