# Copyright (c) 2009, Digital Enterprise Research Institute (DERI),
# NUI Galway
# All rights reserved.
# author: Cosmin Basca
# email: cosmin.basca@gmail.com
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# * Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# * Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer
# in the documentation and/or other materials provided with
# the distribution.
# * Neither the name of DERI nor the
# names of its contributors may be used to endorse or promote
# products derived from this software without specific prior
# written permission.
# THIS SOFTWARE IS PROVIDED BY DERI ''AS IS'' AND ANY
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
# THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
# PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL DERI BE
# LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
# OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
# ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
# OF THE POSSIBILITY OF SUCH DAMAGE.
# -*- coding: utf-8 -*-
__author__ = 'Cosmin Basca'
from datetime import datetime, date, time
import new
import re
from urlparse import urlparse
from uuid import uuid4
from surf.namespace import get_namespace, get_namespace_url
from surf.namespace import get_fallback_namespace, SURF
from surf.rdf import BNode, Literal, Namespace, URIRef
pattern_direct = re.compile('^[a-z0-9]{1,}_[a-zA-Z0-9_\-]{1,}$', re.DOTALL)
pattern_inverse = re.compile('^is_[a-z0-9]{1,}_[a-zA-Z0-9_\-]{1,}_of$', re.DOTALL)
[docs]def namespace_split(uri):
""" Same as `uri_split`, but instead of the base of the uri, returns the
registered `namespace` for this uri
.. code-block:: python
>>> print util.namespace_split('http://mynamespace/ns#some_property')
(rdflib.URIRef('http://mynamespace/ns#'), 'some_property')
"""
sp = uri.rfind('#') != -1 and '#' or '/'
base, predicate = uri.rsplit(sp, 1)
return get_namespace('%s%s' % (base, sp))[1], predicate
[docs]def uri_split(uri):
""" Split the `uri` into base path and remainder,
the base is everything that comes before the last *#*' or */* including it
.. code-block:: python
>>> print util.uri_split('http://mynamespace/ns#some_property')
('NS1', 'some_property')
"""
sp = uri.rfind('#') != -1 and '#' or '/'
base, predicate = uri.rsplit(sp, 1)
return get_namespace('%s%s' % (base, sp))[0], predicate
[docs]def uri_to_classname(uri):
'''handy function to convert a `uri` to a Python valid `class name`
.. code-block:: python
>>> # prints Ns1some_class, where Ns1 is the namespace (not registered, assigned automatically)
>>> print util.uri_to_classname('http://mynamespace/ns#some_class')
Ns1some_class
'''
ns_key, predicate = uri_split(uri)
return '%s%s' % (ns_key.title().replace('-', '_'), predicate)
[docs]def attr2rdf(attrname):
""" Convert an `attribute name` in the form:
.. code-block:: python
# direct predicate
instance1.foaf_name
# inverse predicate
instance2.if_foaf_title_of
to
.. code-block:: xml
<!-- direct predicate -->
<http://xmlns.com/foaf/spec/#term_name>
<!-- inverse predicate -->
<http://xmlns.com/foaf/spec/#term_title>
The function returns two values, the `uri` representation and True if it's
a direct predicate or False if its an inverse predicate.
"""
def tordf(attrname):
prefix, predicate = attrname.split('_', 1)
ns = get_namespace_url(prefix)
try:
return ns[predicate]
except:
return None
if pattern_inverse.match(attrname):
return tordf(attrname.replace('is_', '').replace('_of', '')), False
elif pattern_direct.match(attrname):
return tordf(attrname), True
return None, None
[docs]def rdf2attr(uri, direct):
""" Inverse of `attr2rdf`, return the attribute name,
given the URI and whether it is `direct` or not.
.. code-block:: python
>>> print rdf2attr('http://xmlns.com/foaf/spec/#term_name',True)
foaf_name
>>> print rdf2attr('http://xmlns.com/foaf/spec/#term_title',False)
if_foaf_title_of
"""
ns, predicate = uri_split(uri)
attribute = '%s_%s' % (ns.lower(), predicate)
return direct and attribute or 'is_%s_of' % attribute
[docs]def is_attr_direct(attrname):
""" True if it's a direct `attribute`
.. code-block:: python
>>> util.is_attr_direct('foaf_name')
True
>>> util.is_attr_direct('is_foaf_name_of')
False
"""
return not pattern_inverse.match(attrname)
[docs]def uri_to_class(uri):
'''returns a `class object` from the supplied `uri`, used `uri_to_class` to
get a valid class name
.. code-block:: python
>>> print util.uri_to_class('http://mynamespace/ns#some_class')
surf.util.Ns1some_class
'''
return new.classobj(str(uri_to_classname(uri)), (), {'uri':uri})
[docs]def uuid_subject(namespace = None):
'''the function generates a unique subject in the provided `namespace` based on
the :meth:`uuid.uuid4()` method,
If `namespace` is not specified than the default `SURF` namespace is used
.. code-block:: python
>>> print util.uuid_subject(ns.SIOC)
http://rdfs.org/sioc/ns#1b6ca1d5-41ed-4768-b86a-42185169faff
'''
if not namespace:
namespace = get_fallback_namespace()
if not isinstance(namespace, Namespace):
namespace = Namespace(namespace)
return namespace[str(uuid4())]
DE_CAMEL_CASE_DEFAULT = 2 ** 0
DE_CAMEL_CASE_FORCE_LOWER_CASE = 2 ** 1
pattern = re.compile('([A-Z][A-Z][a-z])|([a-z][A-Z])')
[docs]def de_camel_case(camel_case, delim = ' ', method = DE_CAMEL_CASE_FORCE_LOWER_CASE):
'''Adds spaces to a camel case string. Failure to space out string returns the original string.'''
if camel_case is None:
return None
normalize = lambda s:s
if (method == DE_CAMEL_CASE_FORCE_LOWER_CASE):
normalize = lambda s:s.lower()
return normalize(pattern.sub(lambda m: m.group()[:1] + delim + m.group()[1:], camel_case))
[docs]def is_uri(uri):
'''True if the specified string is a URI reference False otherwise'''
scheme, netloc, path, params, query, fragment = urlparse(uri)
if scheme and netloc and path:
return True
return False
[docs]def pretty_rdf(uri):
'''Returns a string of the given URI under the form `namespace:symbol`, if `namespace` is registered,
else returns an empty string'''
if hasattr(uri, 'subject'):
uri = uri.subject
if type(uri) is URIRef:
NS, symbol = uri_split(uri)
if unicode(NS).startswith('NS'):
pretty = symbol
else:
pretty = NS.lower() + ':' + symbol
return pretty
return ''
[docs]def value_to_rdf(value):
""" Convert the value to an `rdflib` compatible type if appropriate. """
if type(value) in [str, unicode, basestring, float, int, long, bool, datetime, date, time]:
return Literal(value)
elif type(value) in [list, tuple]:
language = len(value) > 1 and value[1] or None
datatype = len(value) > 2 and value[2] or None
return Literal(value[0], lang = language, datatype = datatype)
elif type(value) is dict:
val = value.get("value")
language = value.get("language")
datatype = value.get("datatype")
if val:
return Literal(val, lang = language, datatype = datatype)
return value
return value
[docs]def json_to_rdflib(obj):
"""Convert a json result entry to an rdfLib type."""
try:
type = obj["type"]
except KeyError:
raise ValueError("No type specified")
if type == 'uri':
return URIRef(obj["value"])
elif type == 'literal':
if "xml:lang" in obj:
return Literal(obj["value"], lang=obj['xml:lang'])
else:
return Literal(obj["value"])
elif type == 'typed-literal':
return Literal(obj["value"], datatype=URIRef(obj['datatype']))
elif type == 'bnode':
return BNode(obj["value"])
else:
return None
[docs]class single(object):
""" Descriptor for easy access to attributes with single value. """
def __init__(self, attr):
if isinstance(attr, URIRef):
attr = rdf2attr(attr, True)
self.attr = attr
def __get__(self, obj, type = None):
return getattr(obj, self.attr).first
def __set__(self, obj, value):
setattr(obj, self.attr, value)
def __delete__(self, obj):
setattr(obj, self.attr, [])