retrieval.py 2.79 KB
Newer Older
Gerion Entrup's avatar
Gerion Entrup committed
1
2
3
4
5
6
7
8
9
import logging
import sqlalchemy

from retrieval import fetcher
from contextlib import contextmanager


@contextmanager
def session_scope(Session):
Gerion Entrup's avatar
Gerion Entrup committed
10
11
12
13
14
    """Provide a transactional scope around a series of operations.

    Arguments:
    Session -- a SQLAlchemy session class (not an instance)
    """
Gerion Entrup's avatar
Gerion Entrup committed
15
16
17
18
19
20
21
22
23
24
25
26
27
    session = Session()
    try:
        yield session
        session.commit()
    except:
        session.rollback()
        raise
    finally:
        session.close()


class Retrieval():
    def __init__(self, structure, Session):
Gerion Entrup's avatar
Gerion Entrup committed
28
        """See retrieval.init"""
Gerion Entrup's avatar
Gerion Entrup committed
29
30
31
32
33
34
35
36
        self._logger = logging.getLogger('retrieval')
        self._Session = Session
        self._structure = structure
        self._cache = {}
        for key in structure.keys():
            self._cache[key] = {}

    def _check_state(self, objects):
Gerion Entrup's avatar
Gerion Entrup committed
37
38
39
        """Check if foreign key objects already in database. If not the case a
        query would be meaningless.
        """
Gerion Entrup's avatar
Gerion Entrup committed
40
41
42
43
44
45
46
47
48
49
50
        state_ok = False
        for obj in objects:
            try:
                i = sqlalchemy.inspect(obj)
                state_ok |= i.persistent
            except sqlalchemy.exc.NoInspectionAvailable:
                # ignore non database objects
                pass
        return state_ok

    def create(self, table, *args):
Gerion Entrup's avatar
Gerion Entrup committed
51
        """See retrieval.create"""
Gerion Entrup's avatar
Gerion Entrup committed
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
        entity = self._structure[table]
        assert(len(args) == len(entity.parameter))
        # konstruct keys
        kwargs = entity.query_filter.get_kwargs()
        for key in kwargs:
            kwargs[key] = args[entity.parameter.index(kwargs[key])]
        cache_key = tuple(kwargs.values())
        # search the cache if object exists
        if cache_key in self._cache[table]:
            rs = self._cache[table][cache_key]
            entity.reverse_mapping(rs, *args)
            return rs
        # search the database if object exists
        elif self._check_state(kwargs.values()):
            with session_scope(self._Session) as session:
67
                rs = session.query(table).filter_by(**kwargs).first()
Gerion Entrup's avatar
Gerion Entrup committed
68
69
70
71
                if rs is not None:
                    entity.reverse_mapping(rs, *args)
                    return rs

72
        obj = table()
Gerion Entrup's avatar
Gerion Entrup committed
73
74
75
76
77
78
79
80
81
82
83
84
85

        # fetch the data from web
        if entity.web is not None:
            value = args[entity.parameter.index(entity.web)]
            result = fetcher.get_table_by_id(value, table)
            entity.mapping(obj, result, *args)
        else:
            entity.mapping(obj, *args)

        self._cache[table][cache_key] = obj
        return obj

    def commit(self):
Gerion Entrup's avatar
Gerion Entrup committed
86
        """See retrieval.commit"""
Gerion Entrup's avatar
Gerion Entrup committed
87
88
89
90
        with session_scope(self._Session) as session:
            for table in self._cache.values():
                for entity in table.values():
                    session.add(entity)
91
92
                # clean the cache
                table = {}