graphalchemy / graphalchemy / models /

# -*- coding: utf-8 -*-
'''graph model manager'''

from itertools import count
from functools import partial

from markupsafe import escape
from stuf.utils import getcls
from appspace import AppLookupError

from graphalchemy.mixins.workers import WriterMixin
from graphalchemy.core import octopus, app, factory, defer

__all__ = ['Nodes']
# settings
conf = octopus.G
backends = conf.backends
db = conf.key.db

class Nodes(WriterMixin, octopus.workflow.Worker):

    '''graph node worker'''

    # graph source
    _db = app(conf.key.backend, conf.userspace)
    # model finder
    _finder = app(conf.model.finder.node, conf.models)
    # direct model
    _model = app(, conf.directs)
    # model query
    _query = app(conf.model.reader.node, conf.models)
    # model user
    _using = app(conf.model.writer.node, conf.models)
    # node reader
    _r = factory(, backends, db)
    # node writer
    _w = factory(conf.write.node, backends, db)
    # direct
    direct = factory(, conf.directs, conf.key.session)
    # raw
    raw = factory(conf.raw, conf.appspace, conf.key.session)

    def _prepare(self, element):
        # snapshot if flag set
        if element.L.versioned:
        super(Nodes, self)._prepare(element)

    def _preprocess(self, element, model):
        pre-process graph element

        @param element: graph object
        esc = escape
        for k in model.L.escaped:
            element[k] = esc(element[k])
        element['_modified'] =
        # slug field
        self._autoslug(element, model)

    def _autoslug(self, properties, model):
        auto slug an element

        @param properties: graph element properties
        L = model.L
        slug_from = L.slug_from
        if not slug_from:
        slug = tmpslug = self.Q.slugify(properties[slug_from])
        slug_field = L.slug_field
        # loop until unique slug is located
        filter_by = self._r.filter_by
        index = L.index
        for cnt in count(1):
            prev = filter_by(index, slug_field, tmpslug)
            if not prev:
            tmpslug = '{slug}-{count}'.format(slug=slug, count=cnt)
        # add slug
        properties[slug_field] = tmpslug

    def _postprocess(self, element, model):
        postprocess graph node

        @param element: graph object
        element = model(element)
        L = model.L
        index = L.index
        index_one = self.index_one
        # index created date
        if index:
            index_one(index, 'created',, element)
        # index any listed properties
        if L.indexed:
            self.index_many(index, element, L.indexed)
        # add a slug if present
        slug_from = L.slug_from
        if slug_from:
            index_one(index, slug_from, getattr(element, slug_from), element)
        # index properties that support full text search
        if L.fts_indexed:
            self.index_many(L.fts_index, element, L.fts_indexed)
        return element

    def _create(self, element):
        create a new graph node

        @param data: data
        model = getcls(element)
        L = model.L
        data =
        data.update(,, _uuid=self.Q.uuid())
        # number of versions to maintain
        if L.versioned:
            data['_versions'] = 0
        self._preprocess(data, model)
        element = self._w.create(
            partial(self._postprocess, model=model),

    def _update(self, element):
        update a graph element's properties

        @param element: graph object
        model = getcls(element)
        data =
        self._w.update(element, data, partial(self._postprocess, model=model))

    def query(self, model):
        query using a graph model

        @param model: graph model
        return self._query(model)

    def remove(self, element):
        delete a graph element

        @param model: graph model
        del element

    def root(self):
        '''reference root for graph'''
            G = self.G
            return, G.userspace)
        except AppLookupError:
            root = self._r.root
            root = self._model(root)
            self.Q.add(root, G.root, G.userspace)
            return root

    def using(self, model):
        use graph model

        @param model: graph model
        return self._using(model)