Source

graphalchemy / graphalchemy / models / workers.py

# -*- 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.direct.element.node, 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(conf.read.node, backends, db)
    # node writer
    _w = factory(conf.write.node, backends, db)
    # direct
    direct = factory(conf.direct.worker.node, 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:
            self.using(getcls(element)).snapshot(element)
        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'] = self.now()
        # 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:
            return
        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:
                break
            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', self.now(), 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

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

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

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

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

    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
        '''
        self._w.delete(element)
        del element

    def root(self):
        '''reference root for graph'''
        try:
            G = self.G
            return self.Q.app(G.root, 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)