sqlalchemy has a storage
[tiramisu.git] / tiramisu / storage / sqlalchemy / option.py
1 # -*- coding: utf-8 -*-
2 ""
3 # Copyright (C) 2014 Team tiramisu (see AUTHORS for all contributors)
4 #
5 # This program is free software; you can redistribute it and/or modify
6 # it under the terms of the GNU General Public License as published by
7 # the Free Software Foundation; either version 2 of the License, or
8 # (at your option) any later version.
9 #
10 # This program is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 # GNU General Public License for more details.
14 #
15 # You should have received a copy of the GNU General Public License
16 # along with this program; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18 #
19 # ____________________________________________________________
20 from tiramisu.setting import multitypes
21
22
23 from sqlalchemy.ext.declarative import declarative_base, declared_attr
24 from sqlalchemy import create_engine, Column, Integer, String, Boolean, \
25     PickleType, ForeignKey, Table
26 from sqlalchemy.orm import relationship, backref
27 from sqlalchemy.orm import sessionmaker
28
29
30 #FIXME
31 engine = create_engine('sqlite:///:memory:')
32 SqlAlchemyBase = declarative_base()
33 #____________________________________________________________
34 #
35 # require
36 require_table = Table('require', SqlAlchemyBase.metadata,
37                       Column('left_id', Integer, ForeignKey('requireoption.id')),
38                       Column('right_id', Integer, ForeignKey('baseoption.id'))
39                       )
40
41 class _RequireExpected(SqlAlchemyBase):
42     __tablename__ = 'expected'
43     id = Column(Integer, primary_key=True)
44     expected = Column(PickleType)
45     require = Column(Integer, ForeignKey('requireoption.id'))
46
47     def __init__(self, expected):
48         self.expected = expected
49
50
51 class _RequireOption(SqlAlchemyBase):
52     __tablename__ = 'requireoption'
53     id = Column(Integer, primary_key=True)
54     r_opt = Column(Integer)
55     expected = relationship("_RequireExpected")
56     action = Column(String, nullable=False)
57     inverse = Column(Boolean, default=False)
58     transitive = Column(Boolean, default=True)
59     same_action = Column(Boolean, default=True)
60
61     def __init__(self, option, expected, action, inverse, transitive,
62                  same_action):
63         self.r_opt = option.id
64         for expect in expected:
65             self.expected.append(_RequireExpected(expect))
66         self.action = action
67         self.inverse = inverse
68         self.transitive = transitive
69         self.same_action = same_action
70
71     def get_expected(self):
72         for expected in self.expected:
73             yield(expected.expected)
74
75     #def get_option(self, config):
76     #    return config.cfgimpl_get_description().impl_get_opt_by_id(self.r_opt)
77
78
79 #____________________________________________________________
80 #
81 # properties
82 property_table = Table('property', SqlAlchemyBase.metadata,
83                        Column('left_id', Integer, ForeignKey('propertyoption.name')),
84                        Column('right_id', Integer, ForeignKey('baseoption.id'))
85                        )
86
87
88 class _PropertyOption(SqlAlchemyBase):
89     __tablename__ = 'propertyoption'
90     name = Column(String, primary_key=True)
91
92     def __init__(self, name):
93         self.name = name
94
95
96 #____________________________________________________________
97 #
98 # information
99 class _Information(SqlAlchemyBase):
100     __tablename__ = 'information'
101     id = Column(Integer, primary_key=True)
102     option = Column(Integer, ForeignKey('baseoption.id'))
103     key = Column(String)
104     value = Column(PickleType)
105
106     def __init__(self, key, value):
107         self.key = key
108         self.value = value
109
110
111 #____________________________________________________________
112 #
113 # callback
114 class _CallbackParamOption(SqlAlchemyBase):
115     __tablename__ = 'callback_param_option'
116     id = Column(Integer, primary_key=True)
117     callback_param = Column(Integer, ForeignKey('callback_param.id'))
118     option = Column(Integer)
119     force_permissive = Column(Boolean)
120     value = Column(PickleType)
121
122     def __init__(self, option=None, force_permissive=None,  value=None):
123         if value is not None:
124             self.value = value
125         else:
126             self.option = option.id
127             self.force_permissive = force_permissive
128
129
130 class _CallbackParam(SqlAlchemyBase):
131     __tablename__ = 'callback_param'
132     id = Column(Integer, primary_key=True)
133     callback = Column(Integer, ForeignKey('baseoption.id'))
134     name = Column(String)
135     params = relationship('_CallbackParamOption')
136
137     def __init__(self, name, params):
138         self.name = name
139         for param in params:
140             if isinstance(param, tuple):
141                 self.params.append(_CallbackParamOption(option=param[0],
142                                                         force_permissive=param[1]))
143             else:
144                 self.params.append(_CallbackParamOption(value=param))
145
146
147 #____________________________________________________________
148 #
149 # consistency
150 consistency_table = Table('consistencyopt', SqlAlchemyBase.metadata,
151                           Column('left_id', Integer, ForeignKey('consistency.id')),
152                           Column('right_id', Integer, ForeignKey('baseoption.id'))
153                           )
154
155
156 class _Consistency(SqlAlchemyBase):
157     __tablename__ = 'consistency'
158     id = Column(Integer, primary_key=True)
159     func = Column(PickleType)
160
161     def __init__(self, func, all_cons_opts):
162         self.func = func
163         for option in all_cons_opts:
164             option._consistencies.append(self)
165
166
167 #____________________________________________________________
168 #
169 # Base
170 class _Base(SqlAlchemyBase):
171     __tablename__ = 'baseoption'
172     id = Column(Integer, primary_key=True)
173     _name = Column(String)
174     _informations = relationship('_Information')
175     _default = Column(PickleType)
176     _default_multi = Column(PickleType)
177     _requires = relationship('_RequireOption', secondary=require_table,
178                              backref=backref('option', enable_typechecks=False))
179     _multi = Column(Boolean)
180     _multitype = Column(String)
181     _callback = Column(PickleType)
182     _callback_params = relationship('_CallbackParam')
183     _validator = Column(PickleType)
184     _validator_params = relationship('_CallbackParam')
185     _parent = Column(Integer, ForeignKey('baseoption.id'))
186     _children = relationship('BaseOption', enable_typechecks=False)
187     _properties = relationship('_PropertyOption', secondary=property_table,
188                                backref=backref('options', enable_typechecks=False))
189     _warnings_only = Column(Boolean)
190     _readonly = Column(Boolean, default=False)
191     _consistencies = relationship('_Consistency', secondary=consistency_table,
192                                   backref=backref('options', enable_typechecks=False))
193     _choice_values = Column(PickleType)
194     _choice_open_values = Column(Boolean)
195     _type = Column(String(50))
196     __mapper_args__ = {
197         'polymorphic_identity': 'option',
198         'polymorphic_on': _type
199     }
200     #FIXME devrait etre une table
201     _optiondescription_group_type = Column(String)
202
203     def __init__(self):
204         self.commit()
205
206     def commit(self):
207         session.add(self)
208         session.commit()
209
210     def _get_property_object(self, propname):
211         prop_obj = session.query(_PropertyOption).filter(_PropertyOption.name == propname).first()
212         if prop_obj is None:
213             prop_obj = _PropertyOption(propname)
214         return prop_obj
215
216     def _add_require(self, require):
217         self._requires.append(_RequireOption(*require))
218
219     def _add_callback(self, key, values):
220         self._callback_params.append(_CallbackParam(key, values))
221         
222     def _add_validator(self, key, values):
223         self._validator_params.append(_CallbackParam(key, values))
224
225     def _add_consistency(self, func, all_cons_opts):
226         _Consistency(func, all_cons_opts)
227     # ____________________________________________________________
228     # information
229     def impl_set_information(self, key, value):
230         """updates the information's attribute
231         (which is a dictionary)
232
233         :param key: information's key (ex: "help", "doc"
234         :param value: information's value (ex: "the help string")
235         """
236         info = session.query(_Information).filter_by(option=self.id, key=key).first()
237         #FIXME pas append ! remplacer !
238         if info is None:
239             self._informations.append(_Information(key, value))
240         else:
241             info.value = value
242
243     def impl_get_information(self, key, default=None):
244         """retrieves one information's item
245
246         :param key: the item string (ex: "help")
247         """
248         info = session.query(_Information).filter_by(option=self.id, key=key).first()
249         if info is not None:
250             return info.value
251         elif default is not None:
252             return default
253         else:
254             raise ValueError(_("information's item not found: {0}").format(
255                 key))
256
257
258 class StorageOptionDescription(object):
259     def impl_get_opt_by_path(self, path):
260         try:
261             #FIXME
262             idx = self._cache_paths[1].index(path)
263             opt_id = self._cache_paths[0][idx]
264             return session.query(_Base).filter_by(id=opt_id).first()
265         except ValueError:
266             raise AttributeError(_('no option for path {0}').format(path))
267
268     def impl_get_path_by_opt(self, opt):
269         try:
270             return self._cache_paths[1][self._cache_paths[0].index(opt)]
271         except ValueError:
272             raise AttributeError(_('no option {0} found').format(opt))
273
274
275 class StorageBase(_Base):
276     @declared_attr
277     def __mapper_args__(self):
278         return {'polymorphic_identity': self.__name__.lower()}
279
280
281 #FIXME
282 SqlAlchemyBase.metadata.create_all(engine)
283 Session = sessionmaker(bind=engine)
284 session = Session()