예제 #1
0
def test_transitive_dep_null_context_triples_no_imports(custom_bundle):
    dep_dep_desc = Descriptor.load('''
    id: dep_dep
    includes:
      - http://example.com/ctx
    ''')

    dep_desc = Descriptor.load('''
    id: dep
    dependencies:
      - dep_dep
    ''')

    test_desc = Descriptor.load('''
    id: test
    dependencies:
      - dep
    ''')

    depgraph = ConjunctiveGraph()
    ctx_graph = depgraph.get_context('http://example.com/ctx')
    quad = (URIRef('http://example.org/sub'),
            URIRef('http://example.org/prop'),
            URIRef('http://example.org/obj'), ctx_graph)
    depgraph.add(quad)

    with custom_bundle(dep_dep_desc, graph=depgraph) as depdepbun, \
            custom_bundle(dep_desc, bundles_directory=depdepbun.bundles_directory) as depbun, \
            custom_bundle(test_desc, bundles_directory=depbun.bundles_directory) as testbun, \
            Bundle('test', bundles_directory=testbun.bundles_directory) as bnd:
        assert set([quad[:3]]) == set(bnd.rdf.triples((None, None, None)))
예제 #2
0
def test_add_to_graph_not_supported(custom_bundle):
    dep_desc = Descriptor.load('''
    id: dep
    includes:
      - http://example.com/ctx
    ''')

    test_desc = Descriptor.load('''
    id: test
    dependencies:
      - dep
    ''')

    depgraph = ConjunctiveGraph()
    ctx_graph = depgraph.get_context('http://example.com/ctx')
    quad = (URIRef('http://example.org/sub'),
            URIRef('http://example.org/prop'),
            URIRef('http://example.org/obj'), ctx_graph)
    depgraph.add(quad)

    with custom_bundle(dep_desc, graph=depgraph) as depbun, \
            custom_bundle(test_desc, bundles_directory=depbun.bundles_directory) as testbun, \
            Bundle('test', bundles_directory=testbun.bundles_directory) as bnd:

        with pytest.raises(ZODB.POSException.ReadOnlyError):
            with transaction.manager:
                bnd.rdf.add((URIRef('http://example.org/sub'),
                             URIRef('http://example.org/prop'),
                             URIRef('http://example.org/obj')))
예제 #3
0
def test_triples_choices_context_not_included(custom_bundle):
    dep_desc = Descriptor.load('''
    id: dep
    includes:
      - http://example.com/ctxg
    ''')

    test_desc = Descriptor.load('''
    id: test
    dependencies:
      - dep
    ''')

    depgraph = ConjunctiveGraph()
    ctx_graph = depgraph.get_context('http://example.com/ctx')
    quad = (URIRef('http://example.org/sub'),
            URIRef('http://example.org/prop'),
            URIRef('http://example.org/obj'), ctx_graph)
    depgraph.add(quad)

    with custom_bundle(dep_desc, graph=depgraph) as depbun, \
            custom_bundle(test_desc, bundles_directory=depbun.bundles_directory) as testbun, \
            Bundle('test', bundles_directory=testbun.bundles_directory) as bnd:
        match = False
        for x in bnd.rdf.triples_choices((URIRef('http://example.org/sub'),
                                          URIRef('http://example.org/prop'),
                                          [URIRef('http://example.org/obj')]),
                                         context=ctx_graph):
            match = True
        assert not match
예제 #4
0
def bundle_helper(descriptor,
                  graph=None,
                  bundles_directory=None,
                  homedir=None,
                  **kwargs):
    '''
    Helper for creating bundles for testing.

    Uses `~owmeta_core.bundle.Installer` to lay out a bundle

    Parameters
    ----------
    descriptor : Descriptor
        Describes the bundle
    graph : rdflib.graph.ConjunctiveGraph, optional
        Graph from which the bundle contexts will be generated. If not provided, a graph
        will be created with the triple ``(ex:a, ex:b, ex:c)`` in a context named ``ex:ctx``,
        where ``ex:`` expands to ``http://example.org/``
    bundles_directory : str, optional
        The directory where the bundles should be installed. If not provided, creates a
        temporary directory to house the bundles and cleans them up afterwards
    homedir : str, optional
        Test home directory. If not provided, one will be created based on test directory
    '''
    res = BundleData()
    with tempfile.TemporaryDirectory(prefix=__name__ + '.') as testdir:
        res.testdir = testdir
        res.test_homedir = homedir or p(res.testdir, 'homedir')
        res.bundle_source_directory = p(res.testdir, 'bundle_source')
        res.bundles_directory = bundles_directory or p(res.test_homedir,
                                                       '.owmeta', 'bundles')
        if not homedir:
            os.mkdir(res.test_homedir)
        os.mkdir(res.bundle_source_directory)
        if not bundles_directory:
            os.makedirs(res.bundles_directory)

        # This is a bit of an integration test since it would be a PITA to maintain the bundle
        # format separately from the installer
        res.descriptor = descriptor
        if graph is None:
            graph = ConjunctiveGraph()
            ctxg = graph.get_context(URIRef('http://example.org/ctx'))
            ctxg.add((URIRef('http://example.org/a'),
                      URIRef('http://example.org/b'),
                      URIRef('http://example.org/c')))
        res.installer = Installer(res.bundle_source_directory,
                                  res.bundles_directory,
                                  graph=graph,
                                  **kwargs)
        res.bundle_directory = res.installer.install(res.descriptor)
        yield res
예제 #5
0
def test_quad_not_in_dependency(custom_bundle):
    dep_desc = Descriptor.load('''
    id: dep
    includes:
      - http://example.com/ctx
    ''')

    test_desc = Descriptor.load('''
    id: test
    dependencies:
      - dep
    ''')

    depgraph = ConjunctiveGraph()
    ctx_graph = depgraph.get_context('http://example.com/other_ctx')
    quad = (URIRef('http://example.org/sub'),
            URIRef('http://example.org/prop'),
            URIRef('http://example.org/obj'), ctx_graph)
    depgraph.add(quad)

    with custom_bundle(dep_desc, graph=depgraph) as depbun, \
            custom_bundle(test_desc, bundles_directory=depbun.bundles_directory) as testbun, \
            Bundle('test', bundles_directory=testbun.bundles_directory) as bnd:
        assert quad not in bnd.rdf
class py_drone_graph_core:
    '''
    sample instantiation,
    d_graph = ldg.py_drone_graph(ontology_myID, load_graph_file)
    where,
    1. ontology_myID, uuid for this drone
      e.g. "MjlmNmVmZTAtNGU1OS00N2I4LWI3MzYtODZkMDQ0MTRiNzcxCg=="
    2. load_graph_file, turtle file or (folder) for db initialization
      e.g. base.ttl

    has the following sections,
    1. initialization and graph i/o
    2. utility functions like generating uuid etc.
    '''
    #################
    # class variables
    #################
    g = None  # graph
    Id = None  # local drone id
    files_loaded = False  # flag to prevent ontology reload
    my_host_name = None  # host_name
    BASE = None  # base namespace

    # initialization and graph i/o #############################################
    #######################
    # class initialization
    #######################
    def __init__(self, ontology_myid, graph_dict, my_base, my_host_name):
        '''
        Args:
            ontology_myid (str):    uuid for this drone
            graph_dict (dict.):     configuration data
        '''
        # save hostname
        self.my_host_name = my_host_name + '/'

        # fix base
        self.BASE = rdflib.Namespace(my_base)

        # set base id
        self.Id = ontology_myid

        # load graph, include ttl to load if required
        self.setup_graph(graph_dict)

    ##########################
    # setup and load graph
    ##########################
    def setup_graph(self, graph_dict):
        '''
        Args:
            graph_dict (dict.):     configuration data
        '''
        # get config for graph name, physical db location and it's format
        # added extraction of load_graph_file
        self.graph_name = graph_dict.get('name', ontology_db)
        graph_location = graph_dict.get('db_location', ontology_db_location)
        graph_file_format = graph_dict.get('file_format',
                                           ontology_landrs_file_format)
        load_graph_file = graph_dict.get('file', ontology_db_file)

        # test created instances with pyshacl?
        pshac = graph_dict.get('pyshacl', 'False')
        if pshac == 'False':
            self.pyshacl = False
        else:
            self.pyshacl = True

        shacl_filename = graph_dict.get('shacl_filename', '*shape.ttl')
        ontology_filename = graph_dict.get('ontology_filename', 'ontology.ttl')

        # other shape files
        flight_shacl_filename = graph_dict.get('flight_shacl_filename',
                                               extra_shape_file)
        shacl_constraint_filename = graph_dict.get('shacl_constraint_filename',
                                                   '*shapes.ttl')

        # added file reload startegy
        graph_file_reload = graph_dict.get('file_reload', 'False')

        # does the db exist?
        reload_db = True
        if graph_file_reload == 'False' and os.path.isfile(graph_location +
                                                           '.sqlite'):
            reload_db = False

        # check any folders exist
        os.makedirs(os.path.dirname(graph_location), exist_ok=True)

        # store location
        uri = Literal("sqlite:///%(here)s/%(loc)s.sqlite" % {
            "here": os.getcwd(),
            "loc": graph_location
        })

        # create store
        store_ident = URIRef('store_' + self.graph_name)
        self.store = plugin.get("SQLAlchemy", Store)(identifier=store_ident)

        # was self.g.open
        self.store.open(uri, create=True)

        # and ConjunctiveGraph
        self.g = ConjunctiveGraph(self.store)

        # vars for first graph context
        ident = self.BASE.term(self.graph_name)

        # create and load graph
        self.g1 = Graph(self.store, identifier=ident)

        # vars for shape graph context
        ident2 = self.BASE.term(self.graph_name + '_shape')

        # create and load shape graph
        self.g2 = Graph(self.store, identifier=ident2)

        # vars for config shape graph context
        ident3 = self.BASE.term(self.graph_name + '_config')

        # create and load shape graph
        self.g_config = Graph(self.store, identifier=ident3)

        # print graphs
        print("Graphs")
        for c in self.g.contexts():
            print("-- %s " % c)

        # add LANDRS and other namespaces, this converts the pythonized names to
        # something more readable
        self.g.namespace_manager.bind('landrs', LANDRS)
        self.g.namespace_manager.bind('sosa', SOSA)
        #self.g.namespace_manager.bind('base', BASE)
        #self.g.namespace_manager.bind('qudt-unit-1-1', QUDT_UNIT)
        self.g.namespace_manager.bind('qudt-1-1', QUDT)
        self.g.namespace_manager.bind('geo', GEO)
        self.g.namespace_manager.bind('rdfg', RDFG)

        # Load graph?
        if load_graph_file and not self.files_loaded and reload_db:
            # folder or file?
            if os.path.isdir(load_graph_file):

                # get the list of files
                files_in_graph_folder = os.walk(load_graph_file)
                print("Folder provided for import.")
                # loop
                for (dirpath, dirnames, filenames) in files_in_graph_folder:
                    for file in filenames:
                        file_path = os.path.join(dirpath, file)
                        # each file if turtle
                        if os.path.splitext(file_path)[-1].lower(
                        ) == "." + graph_file_format:
                            if os.path.isfile(file_path):
                                print("file", file_path)
                                self.files_loaded = True
                                # load the individual file
                                try:
                                    # test for shacl files, seperate graph
                                    if fnmatch.fnmatch(
                                            os.path.basename(file_path),
                                            shacl_filename):
                                        self.g2.load(
                                            file_path,
                                            format=graph_file_format,
                                            publicID=self.my_host_name)
                                    else:
                                        self.g1.load(
                                            file_path,
                                            format=graph_file_format,
                                            publicID=self.my_host_name)
                                except Exception as ex:
                                    print("Could not load graph file: " +
                                          str(ex))

            else:
                print("File provided for import.")
                if os.path.isfile(load_graph_file):
                    self.files_loaded = True
                    # load the file
                    try:
                        self.g1.load(load_graph_file,
                                     format=graph_file_format,
                                     publicID=self.my_host_name)
                    except Exception as ex:
                        print("Could not load graph file: " + str(ex))

                    # turn off pyshacl if no seperate shape graph
                    self.pyshacl = False

            # additional config shape files, folder?
            if os.path.isdir(flight_shacl_filename):
                file_list = glob.glob(flight_shacl_filename +
                                      shacl_constraint_filename)
                for fn in file_list:
                    # extract target graph name
                    pos = fn.find(shacl_constraint_filename[1:]) - 1
                    pos2 = fn.rfind('/') + 1

                    # set name
                    g_name = fn[pos2:pos]
                    print("file", fn, g_name)

                    # create graph
                    # vars for config shape graph context
                    identn = self.BASE.term(g_name)

                    # create and load shape graph
                    self.g_tmp = Graph(self.store, identifier=identn)

                    # try to create
                    try:
                        self.g_tmp.load(fn,
                                        format=graph_file_format,
                                        publicID=self.my_host_name)
                    except Exception as ex:
                        print("Could not load shape file: " + str(ex))

            # stand alone?
            elif os.path.isfile(flight_shacl_filename):
                print("file", flight_shacl_filename)
                try:
                    self.g_config.load(flight_shacl_filename,
                                       format=graph_file_format,
                                       publicID=self.my_host_name)
                except Exception as ex:
                    print("Could not load shape file: " + str(ex))

    #############
    # create uuid
    #############
    def generate_uuid(self):
        return base64.urlsafe_b64encode(
            uuid.uuid4().bytes)[:-2].decode('utf-8')

    ######################
    # dump graph as turtle
    ######################
    def dump_graph(self, id):
        graph = self.g.get_context(self.BASE.term(id))
        if graph:
            return graph.serialize(format="turtle", base=self.my_host_name)
        else:
            return None

    #######################
    # dump graphs as turtle
    #######################
    def list_graphs(self):
        ret = '@prefix rdfg: <http://www.w3.org/2004/03/trix/rdfg-1/> .\n' + \
            '@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .\n' + \
            '@prefix rdflib: <http://rdflib.net/projects#> .\n\n'
        # loop over graphs and append
        for c in self.g.contexts():
            ret = ret + str(c) + '\n'

        # return it
        return ret

    ###########################################
    # find namespace for node from uuid
    # solves problem of having objects created
    # on ld.landrs.org OR the drone.
    # Also test existance.
    ###########################################
    def find_node_from_uuid(self, uuid, id_type=None):
        '''
        Args:
            uuid (str):    uuid to find

        Returns:
           URIRef: node associated with uuid
        '''
        # check drone definition exists and if it is local or on ld.landrs.org
        id_node = LDLBASE.term(uuid)
        if not (id_node, RDF.type, id_type) in self.g:
            # from myself?
            id_node = self.BASE.term(uuid)
            if not (id_node, RDF.type, id_type) in self.g:
                # return info
                return None

        # if here, exists and node resolved
        return id_node

    ##########################################
    # recursive drill down through blank nodes
    ##########################################
    def blank_node_recursion(self, blnk, grph):
        # check blank
        if isinstance(blnk, BNode):
            # get nodes
            for sn, pn, on in self.g.triples((blnk, None, None)):
                grph.add((sn, pn, on))
                # recurse
                self.blank_node_recursion(on, grph)

    #########################################
    # get graph with node and its blank nodes
    #########################################
    def get_graph_with_node(self, id_node):
        '''
        Args:
            id_node (str): node id to put into graph

        Returns:
           graph: graph of id_node
        '''
        node_graph = Graph()
        # get id's triples
        for s, p, o in self.g.triples((id_node, None, None)):
            node_graph.add((s, p, o))
            # if associated blank not, get its tripples
            self.blank_node_recursion(o, node_graph)

        # return the new graph
        return node_graph

    ##########################
    # get triples for an id
    ##########################
    def get_id_data(self, id, json=False):
        '''
        Args:
            id (str): uuid to query

        Returns:
           dict.: query result
        '''
        # dictionary
        id_data = {}

        # is the id a local graph?
        # if so return the graph as turtle
        g = self.g.get_context(self.BASE.term(id))
        if g:
            # return info
            return g.serialize(format="turtle",
                               base=self.my_host_name)  # id_data

        # check drone definition exists and if it is local or on ld.landrs.org
        # we will support ld.landrs.org ids due to potential connectivity problems
        id_node = self.find_node_from_uuid(id)
        if not id_node:
            # return info
            return {"status": "id: " + id + " not found."}

        if not json:
            node_graph = self.get_graph_with_node(id_node)

            # return info
            return node_graph.serialize(format="turtle",
                                        base=self.my_host_name)  # id_data
        else:
            # get id's triples
            for s, p, o in self.g.triples((id_node, None, None)):
                print("{} is a {}".format(p, o))
                id_data.update({p: o})

            # return json here
            return id_data

    ##################################
    # routine to convert graph to json
    ##################################
    def graph_to_json(self, g):
        """
        Pass in a rdflib.Graph and get back a chunk of JSON using
        the Talis JSON serialization for RDF:
        http://n2.talis.com/wiki/RDF_JSON_Specification
        """
        g_json = {}

        # go through all the triples in the graph
        for s, p, o in g:

            # initialize property dictionary if we've got a new subject
            if not s in g_json.keys():
                # if not json.has_key(s):
                g_json[s] = {}

            # initialize object list if we've got a new subject-property combo
            if not p in g_json[s].keys():
                # if not json[s].has_key(p):
                g_json[s][p] = []

            # determine the value dictionary for the object
            v = {'value': o}
            if isinstance(o, rdflib.URIRef):
                v['type'] = 'uri'
            elif isinstance(o, rdflib.BNode):
                v['type'] = 'bnode'
            elif isinstance(o, rdflib.Literal):
                v['type'] = 'literal'
                if o.language:
                    v['lang'] = o.language
                if o.datatype:
                    v['datatype'] = o.datatype

            # add the triple
            g_json[s][p].append(v)

        return json.dumps(g_json, indent=4)
예제 #7
0
class InMemoryStorage(object):

    def __init__(self):

        store = IOMemory()

        self.g = ConjunctiveGraph(store=store)

        self.g.bind("lada",ns_lada)
        self.g.bind('data', ns_data)
        self.g.bind('cube', ns_cube)
        self.g.bind('qb', ns_cube)
        self.g.bind('lcd', ns_lcd)
        self.g.bind('xsd', ns_xsd)
        self.g.bind('qb4cc', ns_qb4cc)
        self.g.bind('skos', ns_skos)

        self.initNs = {
            'lada': ns_lada,
            'data': ns_data,
            'qb': ns_cube,
            'lcd': ns_lcd,
            'xsd': ns_xsd,
            'qb4cc': ns_qb4cc,
            'skos': ns_skos
        }


    def _concatenate_graphs(self, graphs):
        source = Graph()
        for g in graphs:
            if g in graph_dict:
                source += self.g.get_context(graph_dict[g])
            elif type(g) is URIRef:
                source += self.g.get_context(g)
        return source

    def add_triple(self, triple, context):
        if context:
            if type(context) is str:
                self.g.get_context(graph_dict[context]).add(triple)
            else:
                self.g.get_context(context).add(triple)
        else:
            self.g.add(triple)

    def add_graph(self, graph, context):
        if context:
            g = None
            if type(context) is str:
                g = self.g.get_context(graph_dict[context])
            else:
                g = self.g.get_context(context)
            g += graph
        else:
            self.g += graph

    def add_file(self, file, format, context):
        if context:
            if type(context) is str:
                self.g.get_context(graph_dict[context]).parse(file, format=format)
            else:
                self.g.get_context(context).parse(file, format=format)
        else:
            self.g.parse(file, format=format)


    def query(self, queryString, contexts):

        if contexts:
            if type(contexts) is list:
                return self._concatenate_graphs(contexts).query(queryString, initNs=self.initNs)
            elif type(contexts) is str:
                return self.g.get_context(graph_dict[contexts]).query(queryString, initNs=self.initNs)
            else:
                return self.g.get_context(contexts).query(queryString, initNs=self.initNs)
        else:
            return self.g.query(queryString, initNs=self.initNs)

    def value(self, subject, predicate, context):
        if context:
            if type(context) is str:
                return self.g.get_context(graph_dict[context]).value(subject, predicate)
            else:
                return self.g.get_context(context).value(subject, predicate)
        else:
            return self.g.value(subject, predicate)

    def remove(self, triple_pattern, contexts):
        if contexts:
            if type(contexts) is list:
                self._concatenate_graphs(contexts).remove(triple_pattern)
            else:
                self.g.get_context(graph_dict[contexts]).remove(triple_pattern)
        else:
            self.g.remove(triple_pattern)

    def clear(self, context):
        if context:
            if type(context) is str:
                self.g.remove_context(self.g.get_context(graph_dict[context]))
            else:
                self.g.remove_context(self.g.get_context(context))
        else:
            self.g.remove( (None, None, None) )

    def count_triples(self):
        c = 0;
        for s, p, o in self.g:
            c = c +1;
        return c

    def export(self, context):
        if type(context) is str:
            self.g.get_context(graph_dict[context]).serialize(context + ".ttl", format="turtle")
예제 #8
0
파일: __init__.py 프로젝트: uholzer/sempipe
class Project:

    def __init__(self, uri, storePath):
        if (uri[-1] != "/"):
            raise SemPipeException("A Module must be a directory and its URI must end with a /")

        self.n = URIRef(uri)

        self.g = ConjunctiveGraph('IOMemory')
        self.storePath = storePath
        if storePath and os.path.exists(storePath+"/store.nquads"):
            self.g.parse(storePath + "/store.nq", format='nquads')
            self.confGraph = self.g.get_context(URIRef("sempipe:confgraph"))
            #self.storePath = storePath
            ## Get the Sleepycat plugin.
            #self.store = plugin.get('Sleepycat', Store)('rdfstore')

            ## Open previously created store, or create it if it doesn't exist yet
            #self.g = ConjunctiveGraph(store="Sleepycat",
            #               identifier = URIRef(self.default_graph_uri))
            ##path = mkdtemp()
            #rt = self.g.open(self.storePath, create=False)
            #if rt == NO_STORE:
            #    # There is no underlying Sleepycat infrastructure, create it
            #    self.g.open(self.storePath, create=True)
            #else:
            #    assert rt == VALID_STORE, "The underlying store is corrupt"
        else:
            #Aggregate graphs
            self.confGraph = self.g.get_context(URIRef("sempipe:confgraph"))
            self._loadconf()
            for graph in self.confGraph.objects(self.n, semp.dataGraph):
                self.loadData(graph)
        for updateList in self.confGraph.objects(self.n, semp.update):
            for updateInstruction in Collection(self.confGraph, updateList):
                self.updateGraph(str(updateInstruction))
        self.commit()
        # Cache HostedSpaces
        self.hostedSpaces = []
        res = self.confGraph.query("""
            SELECT ?baseURI ?mapTo ?index ?htaccess {
                ?baseURI a semp:HostedSpace ;
                semp:mapTo ?mapTo ;
                semp:mapIndexTo ?index ;
                semp:mapHTAccessTo ?htaccess .
            }
        """, initNs={"semp": semp})
        for s in res:
            self.hostedSpaces.append(HostedSpace._make(s))

    def __str__(self):
        return str(self.n)
            
    def __repr__(self):
        return "{0}({1},{2})".format(self.__class__.__name__, repr(self.n), repr(self.storePath))

    def _loadconf(self, uri=None):
        """Loads a graph and all config-graphs it references as configuration graphs

        @param uri: a URIRef, defaults to self.n+SempPipe.conffilename"""
        uri = uri or URIRef(self.n + conffilename)

        if self.g.get_context(uri):
            print("ConfGraph {} already in database".format(uri), file=sys.stderr)
            return

        print("Loading {} as config graph".format(uri), file=sys.stderr)
        newgraph = self.g.parse(uri, format="n3")
        self.confGraph += newgraph
        self.confGraph.add((uri, rdf.type, semp.ConfGraph))
        imports = set(newgraph.objects(uri, semp.confGraph))
        imports |= set(newgraph.objects(self.n, semp.confGraph))
        imports = filter(lambda x: not self.g.get_context(x), imports)
        #Recursively load additional graphs
        for imp in imports:
            self._loadconf(imp)

    def loadData(self, url):
        """Loads a data graph"""
        return parse(self.g, url)

    def updateGraph(self, sparql):
        try:
            self.g.update(sparql)
        except:
            raise SemPipeException("Update instruction failed:\n{}".format(str(sparql)))

    def hostedSpace(self, resource, reverse=False):
        """Picks the best matching hostedSpace for the given resource.

        If reverse is set, resource is considered to be a path
        relative to the buildDir and the corresponding URIRef is
        returned."""
        if reverse:
            hostedSpaces = filter(lambda s: resource.startswith(self.buildDir + s.mapTo), self.hostedSpaces)
        else:
            hostedSpaces = filter(lambda s: resource.startswith(s.baseURI), self.hostedSpaces)
        # Find the best match, which is the most specific one:
        try:
            return max(hostedSpaces, key=lambda s: len(s.baseURI))
        except ValueError:
            raise SemPipeException("No hosted space found for {}".format(resource))

    def contentLocation(self, base, ending):
        if str(base)[-1] == '/':
            index = self.hostedSpace(base).index
            return str(base) + index + ending
        else:
            return str(base) + ending

    @property
    def buildDir(self):
        return next(self.confGraph.objects(self.n, semp.buildDir))

    def buildLocation(self, resource):
        """Determines the filename in the build directory
        corresponding to a URI."""
        hs = self.hostedSpace(resource)
        return self.buildDir + hs.mapTo + resource[len(hs.baseURI):]

    def buildLocationToResource(self, buildLocation):
        """Determines the filename in the build directory
        corresponding to a URI."""
        if not buildLocation.startswith(self.buildDir):
            raise SemPipeException("{} is not in buildDir".format(buildLocation))
        
        hs = self.hostedSpace(buildLocation, reverse=True)
        return URIRef(hs.baseURI + buildLocation[len(self.buildDir + hs.mapTo):])

    def copy(self, source, dest):
        """Publish a resource by copying a file

        Note that dest is the URI where the resource should be
        published, the corresponding directory in the build directory
        is derived automatically."""
        dest = self.buildLocation(dest)
        print("copy {0} to {1}".format(source, dest), file=sys.stderr)
        directory = dest.rsplit("/",1)[0]
        directory = fileurl2path(directory)
        print("  Making shure directory {0} exists".format(directory), file=sys.stderr)
        os.makedirs(directory, mode=0o777, exist_ok=True)
        shutil.copy(fileurl2path(source), fileurl2path(dest))
        print("  done", file=sys.stderr)

    def write(self, dest, data):
        """Publishes a file with contents data"""
        dest = self.buildLocation(dest)
        print("writing data to {0}".format(dest), file=sys.stderr)
        directory = dest.rsplit("/",1)[0]
        directory = fileurl2path(directory)
        print("  Making shure directory {0} exists".format(directory), file=sys.stderr)
        os.makedirs(directory, mode=0o777, exist_ok=True)
        with open(fileurl2path(dest), mode="wb") as f:
            f.write(data)
        print("  done", file=sys.stderr)

    def buildResource(self, resource):
        """Looks up the description of the resource and builds it

        Creates all representations of the resource and adds
        information to the .htaccess if required.
   
        semp:Reource
            type of a Resource
            semp:subject
                What the page is mainly about. This is used by
                semp:Render to know which one is the root node.
            semp:source
                points to a source file
            semp:representation
                A variant of the resource, obtainable by content nogtiation
                semp:content-type
                    indicates the targetted content type
                semp:buildCommand
                    tells how to build the representation.
                    Use semp:Render to render with fresnel Lenses and
                    an XSLT. Use semp:Raw to just take the surce file.
        semp:content-type
            used on a source file or representation to indicate the content type
        """

        representations = self.confGraph.objects(resource, semp.representation)
        for r in representations:
            content_type = next(self.confGraph.objects(r, semp["content-type"]))
            try:
                source = next(self.confGraph.objects(r, semp.source))
            except(StopIteration):
                source = None
            try:
                language = next(self.confGraph.objects(r, semp.language))
            except(StopIteration):
                language = None
            try:
                quality = next(self.confGraph.objects(r, semp.quality))
            except(StopIteration):
                quality = None
            contentLocation = URIRef(self.contentLocation(resource, self.defaultEnding(content_type, language)))
            if semp.Raw in self.confGraph.objects(r, semp.buildCommand):
                self.copy(source, contentLocation)
            elif semp.Render in self.confGraph.objects(r, semp.buildCommand):
                #fresnelGraph = Graph()
                #multiparse(fresnelGraph, self.confGraph.objects(r, semp.fresnelGraph))
                #instanceGraph = Graph()
                #parse(instanceGraph, source)
                #multiparse(instanceGraph, self.confGraph.objects(r, semp.additionalData))
                fresnelGraph = self.g
                instanceGraph = self.g
                ctx = Fresnel.Context(fresnelGraph=fresnelGraph, instanceGraph=instanceGraph)
                box = Fresnel.ContainerBox(ctx)
                box.append(resource)
                box.select()
                box.portray()
                tree = box.transform()
                #Fresnel.prettify(tree) # results in bad whitespace
                self.write(contentLocation, etree.tostring(tree,encoding="UTF-8",xml_declaration=True))
            elif semp.Serialize in self.confGraph.objects(r, semp.buildCommand):
                graph = self.g.get_context(resource)
                self.write(contentLocation, graph.serialize())
            else:
                raise SemPipeException("Failed to produce representation {0} of {1}".format(r, resource))

            try:
                xslt_files = Collection(self.confGraph, next(self.confGraph.objects(r, semp.transformation)))
                buildloc = self.buildLocation(contentLocation)
                for xslt_file in xslt_files:
                    command = ["xsltproc",
                               "--output", fileurl2path(buildloc),
                               fileurl2path(str(xslt_file)),
                               fileurl2path(buildloc)]
                    print("Running transformation", *command, file=sys.stderr)
                    subprocess.call(command)
            except (StopIteration):
                pass

        #write typemap
        typemap = self.typemap(resource)
        if typemap is not None:
            self.write(resource, typemap)
            

    def typemap(self, resource):
        """
        Returns the contents of a type-map file for all
        representations of the given resource. Returns None if no
        typemap is necessary.
        """
        representations = sorted(self.confGraph.objects(resource, semp.representation))
        typemap_url = lambda url: str(url).rsplit("/", 1)[-1]
        typemap = ["URI: {}\n\n".format(typemap_url(resource))]
        typemap_needed = False
        for r in representations:
            content_type = next(self.confGraph.objects(r, semp["content-type"]))
            try:
                source = next(self.confGraph.objects(r, semp.source))
            except(StopIteration):
                source = None
            try:
                language = next(self.confGraph.objects(r, semp.language))
            except(StopIteration):
                language = None
            try:
                quality = next(self.confGraph.objects(r, semp.quality))
            except(StopIteration):
                quality = None
            contentLocation = URIRef(self.contentLocation(resource, self.defaultEnding(content_type, language)))

            typemap.append("URI: {}\n".format(typemap_url(contentLocation)))
            typemap.append("Content-type: {}".format(content_type))
            if quality is not None:
                typemap[-1] += "; q={}\n".format(quality)
                typemap_needed = True
            else:
                typemap[-1] += "\n"
            if language is not None:
                typemap.append("Content-language: {}\n".format(language))
            typemap.append("\n")

        if typemap_needed:
            return "".join(typemap).encode("UTF-8")
        else:
            return None


    def defaultEnding(self, content_type=None, language=None):
        cts = { "application/rdf+xml": ".rdf", "application/xhtml+xml": ".xhtml", "text/html": ".html", None: "" }
        if content_type:
            typeendings = list(self.confGraph.objects(URIRef("http://purl.org/NET/mediatypes/" + content_type), semp.defaultExtension))
            if len(typeendings) > 1:
                raise SemPipeException("ambiguous extension for content-type {} in confGraph.".format(content_type))
            elif len(typeendings) < 1:
                raise SemPipeException("No extension for content-type {} found".format(content_type))
            else:
                typeending = typeendings[0]
        else:
            typeending = ""
        return ("." + language if language else "") + "." + typeending

    def write_htaccess(self):
        """Writes all required .htaccess files."""

        # First generate the directives for each resource
        filesinfo = [];
        resources = self.resources
        for resource in resources:
            info = [];
            filesinfo.append((resource, info));
            if self.typemap(resource) is not None:
                info.append("SetHandler type-map\n")

        # Generate the .htaccess files
        htaccessfiles = dict()
        for resource, info in filter(lambda x: x[1], filesinfo):
            directory, filename = resource.rsplit("/", 1)
            ht = htaccessfiles.setdefault(directory, [])
            ht.append('<Files "{}">\n'.format(filename))
            ht += info
            ht.append('</Files>\n')

        for directory, ht in htaccessfiles.items():
            print("Writing a .htaccess in {}".format(directory), file=sys.stderr)
            filename = self.hostedSpace(resource).htaccess
            self.write(directory + "/" + filename, "".join(ht).encode("UTF-8"))

    def publish(self):
        import getpass
        import subprocess

        """Walks through HostedSpaces and upload the respective files
        from the build diretory.

        (Instead we should walk through the build directory. Will be
        changed later.)"""

        hostedSpacesQuery = """
        SELECT ?space ?method ?command ?invocation
        WHERE {
            ?space a semp:HostedSpace .
            ?space semp:publishMethod ?method .
            ?method semp:command ?command .
            ?method semp:invocation ?invocation .
        }"""
        askForQuery = """
        SELECT ?variable
        WHERE {
            { ?method semp:askFor ?variable . }
            UNION
            { ?method semp:askForHidden ?variable . }
        }""" 
        #?hidden
        #{ ?method semp:askFor ?variable . }
        #UNION
        #{ ?method semp:askForHidden ?variable .
        #  BIND ("true"^^xsd:boolean as ?hidden) }
        for spaceRow in self.confGraph.query(hostedSpacesQuery, initNs={"semp": semp}).bindings:
            space = spaceRow[Variable("?space")]
            method = spaceRow[Variable("?method")]
            answers = dict()
            for question in self.confGraph.query(askForQuery, initNs={"semp": semp}, initBindings={"method": method}).bindings:
                answers[question[Variable("?variable")]] = getpass.getpass("{} for method {}".format(question[Variable("?variable")], method))
            spacedir = self.buildLocation(space)
            command = []
            for arg in Collection(self.confGraph, spaceRow[Variable("command")]):
                command.append(str(arg).format("",fileurl2path(spacedir),str(space),**answers))
            print("Running {}".format(command[0]), file=sys.stderr)
            subprocess.call(command)

    @property
    def resources(self):
        return self.confGraph.subjects(rdf.type, semp.Resource)

    def commit(self):
        self.g.commit()
        if self.storePath:
            self.g.serialize(destination=self.storePath+"/store.nq", format='nquads', encoding='UTF-8')

    def serialize(self):
        return self.g.serialize()

    def close(self):
        self.g.close()