1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved. |
|
2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
3 # |
|
4 # This file is part of CubicWeb. |
|
5 # |
|
6 # CubicWeb is free software: you can redistribute it and/or modify it under the |
|
7 # terms of the GNU Lesser General Public License as published by the Free |
|
8 # Software Foundation, either version 2.1 of the License, or (at your option) |
|
9 # any later version. |
|
10 # |
|
11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT |
|
12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
|
13 # FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more |
|
14 # details. |
|
15 # |
|
16 # You should have received a copy of the GNU Lesser General Public License along |
|
17 # with CubicWeb. If not, see <http://www.gnu.org/licenses/>. |
|
18 """data source related entities""" |
|
19 |
|
20 __docformat__ = "restructuredtext en" |
|
21 |
|
22 import re |
|
23 from socket import gethostname |
|
24 import logging |
|
25 |
|
26 from logilab.common.textutils import text_to_dict |
|
27 from logilab.common.configuration import OptionError |
|
28 from logilab.mtconverter import xml_escape |
|
29 |
|
30 from cubicweb.entities import AnyEntity, fetch_config |
|
31 |
|
32 class _CWSourceCfgMixIn(object): |
|
33 @property |
|
34 def dictconfig(self): |
|
35 return self.config and text_to_dict(self.config) or {} |
|
36 |
|
37 def update_config(self, skip_unknown=False, **config): |
|
38 from cubicweb.server import SOURCE_TYPES |
|
39 from cubicweb.server.serverconfig import (SourceConfiguration, |
|
40 generate_source_config) |
|
41 cfg = self.dictconfig |
|
42 cfg.update(config) |
|
43 options = SOURCE_TYPES[self.type].options |
|
44 sconfig = SourceConfiguration(self._cw.vreg.config, options=options) |
|
45 for opt, val in cfg.items(): |
|
46 try: |
|
47 sconfig.set_option(opt, val) |
|
48 except OptionError: |
|
49 if skip_unknown: |
|
50 continue |
|
51 raise |
|
52 cfgstr = unicode(generate_source_config(sconfig), self._cw.encoding) |
|
53 self.cw_set(config=cfgstr) |
|
54 |
|
55 |
|
56 class CWSource(_CWSourceCfgMixIn, AnyEntity): |
|
57 __regid__ = 'CWSource' |
|
58 fetch_attrs, cw_fetch_order = fetch_config(['name', 'type']) |
|
59 |
|
60 @property |
|
61 def host_config(self): |
|
62 dictconfig = self.dictconfig |
|
63 host = gethostname() |
|
64 for hostcfg in self.host_configs: |
|
65 if hostcfg.match(host): |
|
66 self.info('matching host config %s for source %s', |
|
67 hostcfg.match_host, self.name) |
|
68 dictconfig.update(hostcfg.dictconfig) |
|
69 return dictconfig |
|
70 |
|
71 @property |
|
72 def host_configs(self): |
|
73 return self.reverse_cw_host_config_of |
|
74 |
|
75 def init_mapping(self, mapping): |
|
76 for key, options in mapping: |
|
77 if isinstance(key, tuple): # relation definition |
|
78 assert len(key) == 3 |
|
79 restrictions = ['X relation_type RT, RT name %(rt)s'] |
|
80 kwargs = {'rt': key[1]} |
|
81 if key[0] != '*': |
|
82 restrictions.append('X from_entity FT, FT name %(ft)s') |
|
83 kwargs['ft'] = key[0] |
|
84 if key[2] != '*': |
|
85 restrictions.append('X to_entity TT, TT name %(tt)s') |
|
86 kwargs['tt'] = key[2] |
|
87 rql = 'Any X WHERE %s' % ','.join(restrictions) |
|
88 schemarset = self._cw.execute(rql, kwargs) |
|
89 elif key[0].isupper(): # entity type |
|
90 schemarset = self._cw.execute('CWEType X WHERE X name %(et)s', |
|
91 {'et': key}) |
|
92 else: # relation type |
|
93 schemarset = self._cw.execute('CWRType X WHERE X name %(rt)s', |
|
94 {'rt': key}) |
|
95 for schemaentity in schemarset.entities(): |
|
96 self._cw.create_entity('CWSourceSchemaConfig', |
|
97 cw_for_source=self, |
|
98 cw_schema=schemaentity, |
|
99 options=options) |
|
100 |
|
101 @property |
|
102 def repo_source(self): |
|
103 """repository only property, not available from the web side (eg |
|
104 self._cw is expected to be a server session) |
|
105 """ |
|
106 return self._cw.repo.sources_by_eid[self.eid] |
|
107 |
|
108 |
|
109 class CWSourceHostConfig(_CWSourceCfgMixIn, AnyEntity): |
|
110 __regid__ = 'CWSourceHostConfig' |
|
111 fetch_attrs, cw_fetch_order = fetch_config(['match_host', 'config']) |
|
112 |
|
113 @property |
|
114 def cwsource(self): |
|
115 return self.cw_host_config_of[0] |
|
116 |
|
117 def match(self, hostname): |
|
118 return re.match(self.match_host, hostname) |
|
119 |
|
120 |
|
121 class CWSourceSchemaConfig(AnyEntity): |
|
122 __regid__ = 'CWSourceSchemaConfig' |
|
123 fetch_attrs, cw_fetch_order = fetch_config(['cw_for_source', 'cw_schema', 'options']) |
|
124 |
|
125 def dc_title(self): |
|
126 return self._cw._(self.cw_etype) + ' #%s' % self.eid |
|
127 |
|
128 @property |
|
129 def schema(self): |
|
130 return self.cw_schema[0] |
|
131 |
|
132 @property |
|
133 def cwsource(self): |
|
134 return self.cw_for_source[0] |
|
135 |
|
136 |
|
137 class CWDataImport(AnyEntity): |
|
138 __regid__ = 'CWDataImport' |
|
139 repo_source = _logs = None # please pylint |
|
140 |
|
141 def init(self): |
|
142 self._logs = [] |
|
143 self.repo_source = self.cwsource.repo_source |
|
144 |
|
145 def dc_title(self): |
|
146 return '%s [%s]' % (self.printable_value('start_timestamp'), |
|
147 self.printable_value('status')) |
|
148 |
|
149 @property |
|
150 def cwsource(self): |
|
151 return self.cw_import_of[0] |
|
152 |
|
153 def record_debug(self, msg, path=None, line=None): |
|
154 self._log(logging.DEBUG, msg, path, line) |
|
155 self.repo_source.debug(msg) |
|
156 |
|
157 def record_info(self, msg, path=None, line=None): |
|
158 self._log(logging.INFO, msg, path, line) |
|
159 self.repo_source.info(msg) |
|
160 |
|
161 def record_warning(self, msg, path=None, line=None): |
|
162 self._log(logging.WARNING, msg, path, line) |
|
163 self.repo_source.warning(msg) |
|
164 |
|
165 def record_error(self, msg, path=None, line=None): |
|
166 self._status = u'failed' |
|
167 self._log(logging.ERROR, msg, path, line) |
|
168 self.repo_source.error(msg) |
|
169 |
|
170 def record_fatal(self, msg, path=None, line=None): |
|
171 self._status = u'failed' |
|
172 self._log(logging.FATAL, msg, path, line) |
|
173 self.repo_source.fatal(msg) |
|
174 |
|
175 def _log(self, severity, msg, path=None, line=None): |
|
176 encodedmsg = u'%s\t%s\t%s\t%s<br/>' % (severity, path or u'', |
|
177 line or u'', xml_escape(msg)) |
|
178 self._logs.append(encodedmsg) |
|
179 |
|
180 def write_log(self, session, **kwargs): |
|
181 if 'status' not in kwargs: |
|
182 kwargs['status'] = getattr(self, '_status', u'success') |
|
183 self.cw_set(log=u'<br/>'.join(self._logs), **kwargs) |
|
184 self._logs = [] |
|