##// END OF EJS Templates
fixed caching query on repos path...
marcink -
r1727:8e9f5109 beta
parent child Browse files
Show More
@@ -1,68 +1,69 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.home
3 rhodecode.controllers.home
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Home controller for Rhodecode
6 Home controller for Rhodecode
7
7
8 :created_on: Feb 18, 2010
8 :created_on: Feb 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27
27
28 from pylons import tmpl_context as c, request
28 from pylons import tmpl_context as c, request
29 from paste.httpexceptions import HTTPBadRequest
29 from paste.httpexceptions import HTTPBadRequest
30
30
31 from rhodecode.lib.auth import LoginRequired
31 from rhodecode.lib.auth import LoginRequired
32 from rhodecode.lib.base import BaseController, render
32 from rhodecode.lib.base import BaseController, render
33 from rhodecode.model.db import RepoGroup, Repository
33 from rhodecode.model.db import RepoGroup, Repository
34
34
35 log = logging.getLogger(__name__)
35 log = logging.getLogger(__name__)
36
36
37
37
38 class HomeController(BaseController):
38 class HomeController(BaseController):
39
39
40 @LoginRequired()
40 @LoginRequired()
41 def __before__(self):
41 def __before__(self):
42 super(HomeController, self).__before__()
42 super(HomeController, self).__before__()
43
43
44 def index(self):
44 def index(self):
45
45
46 c.repos_list = self.scm_model.get_repos()
46 c.repos_list = self.scm_model.get_repos()
47
47
48 c.groups = RepoGroup.query().filter(RepoGroup.group_parent_id == None).all()
48 c.groups = RepoGroup.query()\
49 .filter(RepoGroup.group_parent_id == None).all()
49
50
50 return render('/index.html')
51 return render('/index.html')
51
52
52 def repo_switcher(self):
53 def repo_switcher(self):
53 if request.is_xhr:
54 if request.is_xhr:
54 all_repos = Repository.query().order_by(Repository.repo_name).all()
55 all_repos = Repository.query().order_by(Repository.repo_name).all()
55 c.repos_list = self.scm_model.get_repos(all_repos,
56 c.repos_list = self.scm_model.get_repos(all_repos,
56 sort_key='name_sort')
57 sort_key='name_sort')
57 return render('/repo_switcher_list.html')
58 return render('/repo_switcher_list.html')
58 else:
59 else:
59 return HTTPBadRequest()
60 return HTTPBadRequest()
60
61
61 def branch_tag_switcher(self, repo_name):
62 def branch_tag_switcher(self, repo_name):
62 if request.is_xhr:
63 if request.is_xhr:
63 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
64 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
64 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
65 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
65 return render('/switch_to_list.html')
66 return render('/switch_to_list.html')
66 else:
67 else:
67 return HTTPBadRequest()
68 return HTTPBadRequest()
68
69
@@ -1,289 +1,299 b''
1 """caching_query.py
1 """caching_query.py
2
2
3 Represent persistence structures which allow the usage of
3 Represent persistence structures which allow the usage of
4 Beaker caching with SQLAlchemy.
4 Beaker caching with SQLAlchemy.
5
5
6 The three new concepts introduced here are:
6 The three new concepts introduced here are:
7
7
8 * CachingQuery - a Query subclass that caches and
8 * CachingQuery - a Query subclass that caches and
9 retrieves results in/from Beaker.
9 retrieves results in/from Beaker.
10 * FromCache - a query option that establishes caching
10 * FromCache - a query option that establishes caching
11 parameters on a Query
11 parameters on a Query
12 * RelationshipCache - a variant of FromCache which is specific
12 * RelationshipCache - a variant of FromCache which is specific
13 to a query invoked during a lazy load.
13 to a query invoked during a lazy load.
14 * _params_from_query - extracts value parameters from
14 * _params_from_query - extracts value parameters from
15 a Query.
15 a Query.
16
16
17 The rest of what's here are standard SQLAlchemy and
17 The rest of what's here are standard SQLAlchemy and
18 Beaker constructs.
18 Beaker constructs.
19
19
20 """
20 """
21 import beaker
21 import beaker
22 from beaker.exceptions import BeakerException
22 from beaker.exceptions import BeakerException
23
23
24 from sqlalchemy.orm.interfaces import MapperOption
24 from sqlalchemy.orm.interfaces import MapperOption
25 from sqlalchemy.orm.query import Query
25 from sqlalchemy.orm.query import Query
26 from sqlalchemy.sql import visitors
26 from sqlalchemy.sql import visitors
27
27
28
28
29 class CachingQuery(Query):
29 class CachingQuery(Query):
30 """A Query subclass which optionally loads full results from a Beaker
30 """A Query subclass which optionally loads full results from a Beaker
31 cache region.
31 cache region.
32
32
33 The CachingQuery stores additional state that allows it to consult
33 The CachingQuery stores additional state that allows it to consult
34 a Beaker cache before accessing the database:
34 a Beaker cache before accessing the database:
35
35
36 * A "region", which is a cache region argument passed to a
36 * A "region", which is a cache region argument passed to a
37 Beaker CacheManager, specifies a particular cache configuration
37 Beaker CacheManager, specifies a particular cache configuration
38 (including backend implementation, expiration times, etc.)
38 (including backend implementation, expiration times, etc.)
39 * A "namespace", which is a qualifying name that identifies a
39 * A "namespace", which is a qualifying name that identifies a
40 group of keys within the cache. A query that filters on a name
40 group of keys within the cache. A query that filters on a name
41 might use the name "by_name", a query that filters on a date range
41 might use the name "by_name", a query that filters on a date range
42 to a joined table might use the name "related_date_range".
42 to a joined table might use the name "related_date_range".
43
43
44 When the above state is present, a Beaker cache is retrieved.
44 When the above state is present, a Beaker cache is retrieved.
45
45
46 The "namespace" name is first concatenated with
46 The "namespace" name is first concatenated with
47 a string composed of the individual entities and columns the Query
47 a string composed of the individual entities and columns the Query
48 requests, i.e. such as ``Query(User.id, User.name)``.
48 requests, i.e. such as ``Query(User.id, User.name)``.
49
49
50 The Beaker cache is then loaded from the cache manager based
50 The Beaker cache is then loaded from the cache manager based
51 on the region and composed namespace. The key within the cache
51 on the region and composed namespace. The key within the cache
52 itself is then constructed against the bind parameters specified
52 itself is then constructed against the bind parameters specified
53 by this query, which are usually literals defined in the
53 by this query, which are usually literals defined in the
54 WHERE clause.
54 WHERE clause.
55
55
56 The FromCache and RelationshipCache mapper options below represent
56 The FromCache and RelationshipCache mapper options below represent
57 the "public" method of configuring this state upon the CachingQuery.
57 the "public" method of configuring this state upon the CachingQuery.
58
58
59 """
59 """
60
60
61 def __init__(self, manager, *args, **kw):
61 def __init__(self, manager, *args, **kw):
62 self.cache_manager = manager
62 self.cache_manager = manager
63 Query.__init__(self, *args, **kw)
63 Query.__init__(self, *args, **kw)
64
64
65 def __iter__(self):
65 def __iter__(self):
66 """override __iter__ to pull results from Beaker
66 """override __iter__ to pull results from Beaker
67 if particular attributes have been configured.
67 if particular attributes have been configured.
68
68
69 Note that this approach does *not* detach the loaded objects from
69 Note that this approach does *not* detach the loaded objects from
70 the current session. If the cache backend is an in-process cache
70 the current session. If the cache backend is an in-process cache
71 (like "memory") and lives beyond the scope of the current session's
71 (like "memory") and lives beyond the scope of the current session's
72 transaction, those objects may be expired. The method here can be
72 transaction, those objects may be expired. The method here can be
73 modified to first expunge() each loaded item from the current
73 modified to first expunge() each loaded item from the current
74 session before returning the list of items, so that the items
74 session before returning the list of items, so that the items
75 in the cache are not the same ones in the current Session.
75 in the cache are not the same ones in the current Session.
76
76
77 """
77 """
78 if hasattr(self, '_cache_parameters'):
78 if hasattr(self, '_cache_parameters'):
79 return self.get_value(createfunc=lambda:
79 return self.get_value(createfunc=lambda:
80 list(Query.__iter__(self)))
80 list(Query.__iter__(self)))
81 else:
81 else:
82 return Query.__iter__(self)
82 return Query.__iter__(self)
83
83
84 def invalidate(self):
84 def invalidate(self):
85 """Invalidate the value represented by this Query."""
85 """Invalidate the value represented by this Query."""
86
86
87 cache, cache_key = _get_cache_parameters(self)
87 cache, cache_key = _get_cache_parameters(self)
88 cache.remove(cache_key)
88 cache.remove(cache_key)
89
89
90 def get_value(self, merge=True, createfunc=None):
90 def get_value(self, merge=True, createfunc=None):
91 """Return the value from the cache for this query.
91 """Return the value from the cache for this query.
92
92
93 Raise KeyError if no value present and no
93 Raise KeyError if no value present and no
94 createfunc specified.
94 createfunc specified.
95
95
96 """
96 """
97 cache, cache_key = _get_cache_parameters(self)
97 cache, cache_key = _get_cache_parameters(self)
98 ret = cache.get_value(cache_key, createfunc=createfunc)
98 ret = cache.get_value(cache_key, createfunc=createfunc)
99 if merge:
99 if merge:
100 ret = self.merge_result(ret, load=False)
100 ret = self.merge_result(ret, load=False)
101 return ret
101 return ret
102
102
103 def set_value(self, value):
103 def set_value(self, value):
104 """Set the value in the cache for this query."""
104 """Set the value in the cache for this query."""
105
105
106 cache, cache_key = _get_cache_parameters(self)
106 cache, cache_key = _get_cache_parameters(self)
107 cache.put(cache_key, value)
107 cache.put(cache_key, value)
108
108
109
109
110 def query_callable(manager, query_cls=CachingQuery):
110 def query_callable(manager, query_cls=CachingQuery):
111 def query(*arg, **kw):
111 def query(*arg, **kw):
112 return query_cls(manager, *arg, **kw)
112 return query_cls(manager, *arg, **kw)
113 return query
113 return query
114
114
115
115
116 def get_cache_region(name, region):
116 def get_cache_region(name, region):
117 if region not in beaker.cache.cache_regions:
117 if region not in beaker.cache.cache_regions:
118 raise BeakerException('Cache region `%s` not configured '
118 raise BeakerException('Cache region `%s` not configured '
119 'Check if proper cache settings are in the .ini files' % region)
119 'Check if proper cache settings are in the .ini files' % region)
120 kw = beaker.cache.cache_regions[region]
120 kw = beaker.cache.cache_regions[region]
121 return beaker.cache.Cache._get_cache(name, kw)
121 return beaker.cache.Cache._get_cache(name, kw)
122
122
123
123
124 def _get_cache_parameters(query):
124 def _get_cache_parameters(query):
125 """For a query with cache_region and cache_namespace configured,
125 """For a query with cache_region and cache_namespace configured,
126 return the correspoinding Cache instance and cache key, based
126 return the correspoinding Cache instance and cache key, based
127 on this query's current criterion and parameter values.
127 on this query's current criterion and parameter values.
128
128
129 """
129 """
130 if not hasattr(query, '_cache_parameters'):
130 if not hasattr(query, '_cache_parameters'):
131 raise ValueError("This Query does not have caching "
131 raise ValueError("This Query does not have caching "
132 "parameters configured.")
132 "parameters configured.")
133
133
134 region, namespace, cache_key = query._cache_parameters
134 region, namespace, cache_key = query._cache_parameters
135
135
136 namespace = _namespace_from_query(namespace, query)
136 namespace = _namespace_from_query(namespace, query)
137
137
138 if cache_key is None:
138 if cache_key is None:
139 # cache key - the value arguments from this query's parameters.
139 # cache key - the value arguments from this query's parameters.
140 args = _params_from_query(query)
140 args = [str(x) for x in _params_from_query(query)]
141 cache_key = " ".join([str(x) for x in args])
141 args.extend(filter(lambda k:k not in ['None', None, u'None'],
142 [str(query._limit), str(query._offset)]))
143 cache_key = " ".join(args)
144
145 if cache_key is None:
146 raise Exception('Cache key cannot be None')
142
147
143 # get cache
148 # get cache
144 #cache = query.cache_manager.get_cache_region(namespace, region)
149 #cache = query.cache_manager.get_cache_region(namespace, region)
145 cache = get_cache_region(namespace, region)
150 cache = get_cache_region(namespace, region)
146 # optional - hash the cache_key too for consistent length
151 # optional - hash the cache_key too for consistent length
147 # import uuid
152 # import uuid
148 # cache_key= str(uuid.uuid5(uuid.NAMESPACE_DNS, cache_key))
153 # cache_key= str(uuid.uuid5(uuid.NAMESPACE_DNS, cache_key))
149
154
150 return cache, cache_key
155 return cache, cache_key
151
156
152
157
153 def _namespace_from_query(namespace, query):
158 def _namespace_from_query(namespace, query):
154 # cache namespace - the token handed in by the
159 # cache namespace - the token handed in by the
155 # option + class we're querying against
160 # option + class we're querying against
156 namespace = " ".join([namespace] + [str(x) for x in query._entities])
161 namespace = " ".join([namespace] + [str(x) for x in query._entities])
157
162
158 # memcached wants this
163 # memcached wants this
159 namespace = namespace.replace(' ', '_')
164 namespace = namespace.replace(' ', '_')
160
165
161 return namespace
166 return namespace
162
167
163
168
164 def _set_cache_parameters(query, region, namespace, cache_key):
169 def _set_cache_parameters(query, region, namespace, cache_key):
165
170
166 if hasattr(query, '_cache_parameters'):
171 if hasattr(query, '_cache_parameters'):
167 region, namespace, cache_key = query._cache_parameters
172 region, namespace, cache_key = query._cache_parameters
168 raise ValueError("This query is already configured "
173 raise ValueError("This query is already configured "
169 "for region %r namespace %r" %
174 "for region %r namespace %r" %
170 (region, namespace)
175 (region, namespace)
171 )
176 )
172 query._cache_parameters = region, namespace, cache_key
177 query._cache_parameters = region, namespace, cache_key
173
178
174
179
175 class FromCache(MapperOption):
180 class FromCache(MapperOption):
176 """Specifies that a Query should load results from a cache."""
181 """Specifies that a Query should load results from a cache."""
177
182
178 propagate_to_loaders = False
183 propagate_to_loaders = False
179
184
180 def __init__(self, region, namespace, cache_key=None):
185 def __init__(self, region, namespace, cache_key=None):
181 """Construct a new FromCache.
186 """Construct a new FromCache.
182
187
183 :param region: the cache region. Should be a
188 :param region: the cache region. Should be a
184 region configured in the Beaker CacheManager.
189 region configured in the Beaker CacheManager.
185
190
186 :param namespace: the cache namespace. Should
191 :param namespace: the cache namespace. Should
187 be a name uniquely describing the target Query's
192 be a name uniquely describing the target Query's
188 lexical structure.
193 lexical structure.
189
194
190 :param cache_key: optional. A string cache key
195 :param cache_key: optional. A string cache key
191 that will serve as the key to the query. Use this
196 that will serve as the key to the query. Use this
192 if your query has a huge amount of parameters (such
197 if your query has a huge amount of parameters (such
193 as when using in_()) which correspond more simply to
198 as when using in_()) which correspond more simply to
194 some other identifier.
199 some other identifier.
195
200
196 """
201 """
197 self.region = region
202 self.region = region
198 self.namespace = namespace
203 self.namespace = namespace
199 self.cache_key = cache_key
204 self.cache_key = cache_key
200
205
201 def process_query(self, query):
206 def process_query(self, query):
202 """Process a Query during normal loading operation."""
207 """Process a Query during normal loading operation."""
203
208
204 _set_cache_parameters(query, self.region, self.namespace,
209 _set_cache_parameters(query, self.region, self.namespace,
205 self.cache_key)
210 self.cache_key)
206
211
207
212
208 class RelationshipCache(MapperOption):
213 class RelationshipCache(MapperOption):
209 """Specifies that a Query as called within a "lazy load"
214 """Specifies that a Query as called within a "lazy load"
210 should load results from a cache."""
215 should load results from a cache."""
211
216
212 propagate_to_loaders = True
217 propagate_to_loaders = True
213
218
214 def __init__(self, region, namespace, attribute):
219 def __init__(self, region, namespace, attribute):
215 """Construct a new RelationshipCache.
220 """Construct a new RelationshipCache.
216
221
217 :param region: the cache region. Should be a
222 :param region: the cache region. Should be a
218 region configured in the Beaker CacheManager.
223 region configured in the Beaker CacheManager.
219
224
220 :param namespace: the cache namespace. Should
225 :param namespace: the cache namespace. Should
221 be a name uniquely describing the target Query's
226 be a name uniquely describing the target Query's
222 lexical structure.
227 lexical structure.
223
228
224 :param attribute: A Class.attribute which
229 :param attribute: A Class.attribute which
225 indicates a particular class relationship() whose
230 indicates a particular class relationship() whose
226 lazy loader should be pulled from the cache.
231 lazy loader should be pulled from the cache.
227
232
228 """
233 """
229 self.region = region
234 self.region = region
230 self.namespace = namespace
235 self.namespace = namespace
231 self._relationship_options = {
236 self._relationship_options = {
232 (attribute.property.parent.class_, attribute.property.key): self
237 (attribute.property.parent.class_, attribute.property.key): self
233 }
238 }
234
239
235 def process_query_conditionally(self, query):
240 def process_query_conditionally(self, query):
236 """Process a Query that is used within a lazy loader.
241 """Process a Query that is used within a lazy loader.
237
242
238 (the process_query_conditionally() method is a SQLAlchemy
243 (the process_query_conditionally() method is a SQLAlchemy
239 hook invoked only within lazyload.)
244 hook invoked only within lazyload.)
240
245
241 """
246 """
242 if query._current_path:
247 if query._current_path:
243 mapper, key = query._current_path[-2:]
248 mapper, key = query._current_path[-2:]
244
249
245 for cls in mapper.class_.__mro__:
250 for cls in mapper.class_.__mro__:
246 if (cls, key) in self._relationship_options:
251 if (cls, key) in self._relationship_options:
247 relationship_option = \
252 relationship_option = \
248 self._relationship_options[(cls, key)]
253 self._relationship_options[(cls, key)]
249 _set_cache_parameters(
254 _set_cache_parameters(
250 query,
255 query,
251 relationship_option.region,
256 relationship_option.region,
252 relationship_option.namespace,
257 relationship_option.namespace,
253 None)
258 None)
254
259
255 def and_(self, option):
260 def and_(self, option):
256 """Chain another RelationshipCache option to this one.
261 """Chain another RelationshipCache option to this one.
257
262
258 While many RelationshipCache objects can be specified on a single
263 While many RelationshipCache objects can be specified on a single
259 Query separately, chaining them together allows for a more efficient
264 Query separately, chaining them together allows for a more efficient
260 lookup during load.
265 lookup during load.
261
266
262 """
267 """
263 self._relationship_options.update(option._relationship_options)
268 self._relationship_options.update(option._relationship_options)
264 return self
269 return self
265
270
266
271
267 def _params_from_query(query):
272 def _params_from_query(query):
268 """Pull the bind parameter values from a query.
273 """Pull the bind parameter values from a query.
269
274
270 This takes into account any scalar attribute bindparam set up.
275 This takes into account any scalar attribute bindparam set up.
271
276
272 E.g. params_from_query(query.filter(Cls.foo==5).filter(Cls.bar==7)))
277 E.g. params_from_query(query.filter(Cls.foo==5).filter(Cls.bar==7)))
273 would return [5, 7].
278 would return [5, 7].
274
279
275 """
280 """
276 v = []
281 v = []
277 def visit_bindparam(bind):
282 def visit_bindparam(bind):
278 value = query._params.get(bind.key, bind.value)
279
283
280 # lazyloader may dig a callable in here, intended
284 if bind.key in query._params:
281 # to late-evaluate params after autoflush is called.
285 value = query._params[bind.key]
282 # convert to a scalar value.
286 elif bind.callable:
283 if callable(value):
287 # lazyloader may dig a callable in here, intended
284 value = value()
288 # to late-evaluate params after autoflush is called.
289 # convert to a scalar value.
290 value = bind.callable()
291 else:
292 value = bind.value
285
293
286 v.append(value)
294 v.append(value)
287 if query._criterion is not None:
295 if query._criterion is not None:
288 visitors.traverse(query._criterion, {}, {'bindparam':visit_bindparam})
296 visitors.traverse(query._criterion, {}, {'bindparam':visit_bindparam})
297 for f in query._from_obj:
298 visitors.traverse(f, {}, {'bindparam':visit_bindparam})
289 return v
299 return v
@@ -1,1206 +1,1206 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30
30
31 from sqlalchemy import *
31 from sqlalchemy import *
32 from sqlalchemy.ext.hybrid import hybrid_property
32 from sqlalchemy.ext.hybrid import hybrid_property
33 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
33 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
34 from beaker.cache import cache_region, region_invalidate
34 from beaker.cache import cache_region, region_invalidate
35
35
36 from vcs import get_backend
36 from vcs import get_backend
37 from vcs.utils.helpers import get_scm
37 from vcs.utils.helpers import get_scm
38 from vcs.exceptions import VCSError
38 from vcs.exceptions import VCSError
39 from vcs.utils.lazy import LazyProperty
39 from vcs.utils.lazy import LazyProperty
40
40
41 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
41 from rhodecode.lib import str2bool, safe_str, get_changeset_safe, safe_unicode
42 from rhodecode.lib.exceptions import UsersGroupsAssignedException
42 from rhodecode.lib.exceptions import UsersGroupsAssignedException
43 from rhodecode.lib.compat import json
43 from rhodecode.lib.compat import json
44 from rhodecode.lib.caching_query import FromCache
44 from rhodecode.lib.caching_query import FromCache
45
45
46 from rhodecode.model.meta import Base, Session
46 from rhodecode.model.meta import Base, Session
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50 #==============================================================================
50 #==============================================================================
51 # BASE CLASSES
51 # BASE CLASSES
52 #==============================================================================
52 #==============================================================================
53
53
54 class ModelSerializer(json.JSONEncoder):
54 class ModelSerializer(json.JSONEncoder):
55 """
55 """
56 Simple Serializer for JSON,
56 Simple Serializer for JSON,
57
57
58 usage::
58 usage::
59
59
60 to make object customized for serialization implement a __json__
60 to make object customized for serialization implement a __json__
61 method that will return a dict for serialization into json
61 method that will return a dict for serialization into json
62
62
63 example::
63 example::
64
64
65 class Task(object):
65 class Task(object):
66
66
67 def __init__(self, name, value):
67 def __init__(self, name, value):
68 self.name = name
68 self.name = name
69 self.value = value
69 self.value = value
70
70
71 def __json__(self):
71 def __json__(self):
72 return dict(name=self.name,
72 return dict(name=self.name,
73 value=self.value)
73 value=self.value)
74
74
75 """
75 """
76
76
77 def default(self, obj):
77 def default(self, obj):
78
78
79 if hasattr(obj, '__json__'):
79 if hasattr(obj, '__json__'):
80 return obj.__json__()
80 return obj.__json__()
81 else:
81 else:
82 return json.JSONEncoder.default(self, obj)
82 return json.JSONEncoder.default(self, obj)
83
83
84 class BaseModel(object):
84 class BaseModel(object):
85 """Base Model for all classess
85 """Base Model for all classess
86
86
87 """
87 """
88
88
89 @classmethod
89 @classmethod
90 def _get_keys(cls):
90 def _get_keys(cls):
91 """return column names for this model """
91 """return column names for this model """
92 return class_mapper(cls).c.keys()
92 return class_mapper(cls).c.keys()
93
93
94 def get_dict(self):
94 def get_dict(self):
95 """return dict with keys and values corresponding
95 """return dict with keys and values corresponding
96 to this model data """
96 to this model data """
97
97
98 d = {}
98 d = {}
99 for k in self._get_keys():
99 for k in self._get_keys():
100 d[k] = getattr(self, k)
100 d[k] = getattr(self, k)
101 return d
101 return d
102
102
103 def get_appstruct(self):
103 def get_appstruct(self):
104 """return list with keys and values tupples corresponding
104 """return list with keys and values tupples corresponding
105 to this model data """
105 to this model data """
106
106
107 l = []
107 l = []
108 for k in self._get_keys():
108 for k in self._get_keys():
109 l.append((k, getattr(self, k),))
109 l.append((k, getattr(self, k),))
110 return l
110 return l
111
111
112 def populate_obj(self, populate_dict):
112 def populate_obj(self, populate_dict):
113 """populate model with data from given populate_dict"""
113 """populate model with data from given populate_dict"""
114
114
115 for k in self._get_keys():
115 for k in self._get_keys():
116 if k in populate_dict:
116 if k in populate_dict:
117 setattr(self, k, populate_dict[k])
117 setattr(self, k, populate_dict[k])
118
118
119 @classmethod
119 @classmethod
120 def query(cls):
120 def query(cls):
121 return Session().query(cls)
121 return Session().query(cls)
122
122
123 @classmethod
123 @classmethod
124 def get(cls, id_):
124 def get(cls, id_):
125 if id_:
125 if id_:
126 return cls.query().get(id_)
126 return cls.query().get(id_)
127
127
128 @classmethod
128 @classmethod
129 def getAll(cls):
129 def getAll(cls):
130 return cls.query().all()
130 return cls.query().all()
131
131
132 @classmethod
132 @classmethod
133 def delete(cls, id_):
133 def delete(cls, id_):
134 obj = cls.query().get(id_)
134 obj = cls.query().get(id_)
135 Session().delete(obj)
135 Session().delete(obj)
136
136
137
137
138 class RhodeCodeSetting(Base, BaseModel):
138 class RhodeCodeSetting(Base, BaseModel):
139 __tablename__ = 'rhodecode_settings'
139 __tablename__ = 'rhodecode_settings'
140 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
140 __table_args__ = (UniqueConstraint('app_settings_name'), {'extend_existing':True})
141 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
141 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
142 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
142 app_settings_name = Column("app_settings_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
143 _app_settings_value = Column("app_settings_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
144
144
145 def __init__(self, k='', v=''):
145 def __init__(self, k='', v=''):
146 self.app_settings_name = k
146 self.app_settings_name = k
147 self.app_settings_value = v
147 self.app_settings_value = v
148
148
149
149
150 @validates('_app_settings_value')
150 @validates('_app_settings_value')
151 def validate_settings_value(self, key, val):
151 def validate_settings_value(self, key, val):
152 assert type(val) == unicode
152 assert type(val) == unicode
153 return val
153 return val
154
154
155 @hybrid_property
155 @hybrid_property
156 def app_settings_value(self):
156 def app_settings_value(self):
157 v = self._app_settings_value
157 v = self._app_settings_value
158 if v == 'ldap_active':
158 if v == 'ldap_active':
159 v = str2bool(v)
159 v = str2bool(v)
160 return v
160 return v
161
161
162 @app_settings_value.setter
162 @app_settings_value.setter
163 def app_settings_value(self, val):
163 def app_settings_value(self, val):
164 """
164 """
165 Setter that will always make sure we use unicode in app_settings_value
165 Setter that will always make sure we use unicode in app_settings_value
166
166
167 :param val:
167 :param val:
168 """
168 """
169 self._app_settings_value = safe_unicode(val)
169 self._app_settings_value = safe_unicode(val)
170
170
171 def __repr__(self):
171 def __repr__(self):
172 return "<%s('%s:%s')>" % (self.__class__.__name__,
172 return "<%s('%s:%s')>" % (self.__class__.__name__,
173 self.app_settings_name, self.app_settings_value)
173 self.app_settings_name, self.app_settings_value)
174
174
175
175
176 @classmethod
176 @classmethod
177 def get_by_name(cls, ldap_key):
177 def get_by_name(cls, ldap_key):
178 return cls.query()\
178 return cls.query()\
179 .filter(cls.app_settings_name == ldap_key).scalar()
179 .filter(cls.app_settings_name == ldap_key).scalar()
180
180
181 @classmethod
181 @classmethod
182 def get_app_settings(cls, cache=False):
182 def get_app_settings(cls, cache=False):
183
183
184 ret = cls.query()
184 ret = cls.query()
185
185
186 if cache:
186 if cache:
187 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
187 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
188
188
189 if not ret:
189 if not ret:
190 raise Exception('Could not get application settings !')
190 raise Exception('Could not get application settings !')
191 settings = {}
191 settings = {}
192 for each in ret:
192 for each in ret:
193 settings['rhodecode_' + each.app_settings_name] = \
193 settings['rhodecode_' + each.app_settings_name] = \
194 each.app_settings_value
194 each.app_settings_value
195
195
196 return settings
196 return settings
197
197
198 @classmethod
198 @classmethod
199 def get_ldap_settings(cls, cache=False):
199 def get_ldap_settings(cls, cache=False):
200 ret = cls.query()\
200 ret = cls.query()\
201 .filter(cls.app_settings_name.startswith('ldap_')).all()
201 .filter(cls.app_settings_name.startswith('ldap_')).all()
202 fd = {}
202 fd = {}
203 for row in ret:
203 for row in ret:
204 fd.update({row.app_settings_name:row.app_settings_value})
204 fd.update({row.app_settings_name:row.app_settings_value})
205
205
206 return fd
206 return fd
207
207
208
208
209 class RhodeCodeUi(Base, BaseModel):
209 class RhodeCodeUi(Base, BaseModel):
210 __tablename__ = 'rhodecode_ui'
210 __tablename__ = 'rhodecode_ui'
211 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
211 __table_args__ = (UniqueConstraint('ui_key'), {'extend_existing':True})
212
212
213 HOOK_UPDATE = 'changegroup.update'
213 HOOK_UPDATE = 'changegroup.update'
214 HOOK_REPO_SIZE = 'changegroup.repo_size'
214 HOOK_REPO_SIZE = 'changegroup.repo_size'
215 HOOK_PUSH = 'pretxnchangegroup.push_logger'
215 HOOK_PUSH = 'pretxnchangegroup.push_logger'
216 HOOK_PULL = 'preoutgoing.pull_logger'
216 HOOK_PULL = 'preoutgoing.pull_logger'
217
217
218 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
218 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
219 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
219 ui_section = Column("ui_section", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
220 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
220 ui_key = Column("ui_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
221 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
221 ui_value = Column("ui_value", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
222 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
222 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
223
223
224
224
225 @classmethod
225 @classmethod
226 def get_by_key(cls, key):
226 def get_by_key(cls, key):
227 return cls.query().filter(cls.ui_key == key)
227 return cls.query().filter(cls.ui_key == key)
228
228
229
229
230 @classmethod
230 @classmethod
231 def get_builtin_hooks(cls):
231 def get_builtin_hooks(cls):
232 q = cls.query()
232 q = cls.query()
233 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
233 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE,
234 cls.HOOK_REPO_SIZE,
234 cls.HOOK_REPO_SIZE,
235 cls.HOOK_PUSH, cls.HOOK_PULL]))
235 cls.HOOK_PUSH, cls.HOOK_PULL]))
236 return q.all()
236 return q.all()
237
237
238 @classmethod
238 @classmethod
239 def get_custom_hooks(cls):
239 def get_custom_hooks(cls):
240 q = cls.query()
240 q = cls.query()
241 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
241 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE,
242 cls.HOOK_REPO_SIZE,
242 cls.HOOK_REPO_SIZE,
243 cls.HOOK_PUSH, cls.HOOK_PULL]))
243 cls.HOOK_PUSH, cls.HOOK_PULL]))
244 q = q.filter(cls.ui_section == 'hooks')
244 q = q.filter(cls.ui_section == 'hooks')
245 return q.all()
245 return q.all()
246
246
247 @classmethod
247 @classmethod
248 def create_or_update_hook(cls, key, val):
248 def create_or_update_hook(cls, key, val):
249 new_ui = cls.get_by_key(key).scalar() or cls()
249 new_ui = cls.get_by_key(key).scalar() or cls()
250 new_ui.ui_section = 'hooks'
250 new_ui.ui_section = 'hooks'
251 new_ui.ui_active = True
251 new_ui.ui_active = True
252 new_ui.ui_key = key
252 new_ui.ui_key = key
253 new_ui.ui_value = val
253 new_ui.ui_value = val
254
254
255 Session().add(new_ui)
255 Session().add(new_ui)
256 Session().commit()
256 Session().commit()
257
257
258
258
259 class User(Base, BaseModel):
259 class User(Base, BaseModel):
260 __tablename__ = 'users'
260 __tablename__ = 'users'
261 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
261 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'extend_existing':True})
262 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
262 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
263 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
263 username = Column("username", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
264 password = Column("password", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
265 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
265 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
266 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
266 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
267 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
267 name = Column("name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 lastname = Column("lastname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 email = Column("email", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
270 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
271 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 ldap_dn = Column("ldap_dn", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
272 api_key = Column("api_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
273
273
274 user_log = relationship('UserLog', cascade='all')
274 user_log = relationship('UserLog', cascade='all')
275 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
275 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
276
276
277 repositories = relationship('Repository')
277 repositories = relationship('Repository')
278 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
278 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
279 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
279 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
280
280
281 group_member = relationship('UsersGroupMember', cascade='all')
281 group_member = relationship('UsersGroupMember', cascade='all')
282
282
283 notifications = relationship('UserNotification',)
283 notifications = relationship('UserNotification',)
284
284
285 @property
285 @property
286 def full_contact(self):
286 def full_contact(self):
287 return '%s %s <%s>' % (self.name, self.lastname, self.email)
287 return '%s %s <%s>' % (self.name, self.lastname, self.email)
288
288
289 @property
289 @property
290 def short_contact(self):
290 def short_contact(self):
291 return '%s %s' % (self.name, self.lastname)
291 return '%s %s' % (self.name, self.lastname)
292
292
293 @property
293 @property
294 def is_admin(self):
294 def is_admin(self):
295 return self.admin
295 return self.admin
296
296
297 def __repr__(self):
297 def __repr__(self):
298 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
298 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
299 self.user_id, self.username)
299 self.user_id, self.username)
300
300
301
301
302 @classmethod
302 @classmethod
303 def get_by_username(cls, username, case_insensitive=False, cache=False):
303 def get_by_username(cls, username, case_insensitive=False, cache=False):
304 if case_insensitive:
304 if case_insensitive:
305 q = cls.query().filter(cls.username.ilike(username))
305 q = cls.query().filter(cls.username.ilike(username))
306 else:
306 else:
307 q = cls.query().filter(cls.username == username)
307 q = cls.query().filter(cls.username == username)
308
308
309 if cache:
309 if cache:
310 q = q.options(FromCache("sql_cache_short",
310 q = q.options(FromCache("sql_cache_short",
311 "get_user_%s" % username))
311 "get_user_%s" % username))
312 return q.scalar()
312 return q.scalar()
313
313
314 @classmethod
314 @classmethod
315 def get_by_api_key(cls, api_key, cache=False):
315 def get_by_api_key(cls, api_key, cache=False):
316 q = cls.query().filter(cls.api_key == api_key)
316 q = cls.query().filter(cls.api_key == api_key)
317
317
318 if cache:
318 if cache:
319 q = q.options(FromCache("sql_cache_short",
319 q = q.options(FromCache("sql_cache_short",
320 "get_api_key_%s" % api_key))
320 "get_api_key_%s" % api_key))
321 return q.scalar()
321 return q.scalar()
322
322
323 @classmethod
323 @classmethod
324 def get_by_email(cls, email, cache=False):
324 def get_by_email(cls, email, cache=False):
325 q = cls.query().filter(cls.email == email)
325 q = cls.query().filter(cls.email == email)
326
326
327 if cache:
327 if cache:
328 q = q.options(FromCache("sql_cache_short",
328 q = q.options(FromCache("sql_cache_short",
329 "get_api_key_%s" % email))
329 "get_api_key_%s" % email))
330 return q.scalar()
330 return q.scalar()
331
331
332 def update_lastlogin(self):
332 def update_lastlogin(self):
333 """Update user lastlogin"""
333 """Update user lastlogin"""
334
334
335 self.last_login = datetime.datetime.now()
335 self.last_login = datetime.datetime.now()
336 Session().add(self)
336 Session().add(self)
337 Session().commit()
337 Session().commit()
338 log.debug('updated user %s lastlogin', self.username)
338 log.debug('updated user %s lastlogin', self.username)
339
339
340
340
341 class UserLog(Base, BaseModel):
341 class UserLog(Base, BaseModel):
342 __tablename__ = 'user_logs'
342 __tablename__ = 'user_logs'
343 __table_args__ = {'extend_existing':True}
343 __table_args__ = {'extend_existing':True}
344 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
344 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
345 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
345 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
346 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
346 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
347 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
347 repository_name = Column("repository_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
348 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
348 user_ip = Column("user_ip", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
349 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
349 action = Column("action", UnicodeText(length=1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
350 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
350 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
351
351
352 @property
352 @property
353 def action_as_day(self):
353 def action_as_day(self):
354 return datetime.date(*self.action_date.timetuple()[:3])
354 return datetime.date(*self.action_date.timetuple()[:3])
355
355
356 user = relationship('User')
356 user = relationship('User')
357 repository = relationship('Repository')
357 repository = relationship('Repository')
358
358
359
359
360 class UsersGroup(Base, BaseModel):
360 class UsersGroup(Base, BaseModel):
361 __tablename__ = 'users_groups'
361 __tablename__ = 'users_groups'
362 __table_args__ = {'extend_existing':True}
362 __table_args__ = {'extend_existing':True}
363
363
364 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
364 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
365 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
365 users_group_name = Column("users_group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
366 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
366 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
367
367
368 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
368 members = relationship('UsersGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
369
369
370 def __repr__(self):
370 def __repr__(self):
371 return '<userGroup(%s)>' % (self.users_group_name)
371 return '<userGroup(%s)>' % (self.users_group_name)
372
372
373 @classmethod
373 @classmethod
374 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
374 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
375 if case_insensitive:
375 if case_insensitive:
376 gr = cls.query()\
376 gr = cls.query()\
377 .filter(cls.users_group_name.ilike(group_name))
377 .filter(cls.users_group_name.ilike(group_name))
378 else:
378 else:
379 gr = cls.query()\
379 gr = cls.query()\
380 .filter(cls.users_group_name == group_name)
380 .filter(cls.users_group_name == group_name)
381 if cache:
381 if cache:
382 gr = gr.options(FromCache("sql_cache_short",
382 gr = gr.options(FromCache("sql_cache_short",
383 "get_user_%s" % group_name))
383 "get_user_%s" % group_name))
384 return gr.scalar()
384 return gr.scalar()
385
385
386
386
387 @classmethod
387 @classmethod
388 def get(cls, users_group_id, cache=False):
388 def get(cls, users_group_id, cache=False):
389 users_group = cls.query()
389 users_group = cls.query()
390 if cache:
390 if cache:
391 users_group = users_group.options(FromCache("sql_cache_short",
391 users_group = users_group.options(FromCache("sql_cache_short",
392 "get_users_group_%s" % users_group_id))
392 "get_users_group_%s" % users_group_id))
393 return users_group.get(users_group_id)
393 return users_group.get(users_group_id)
394
394
395 @classmethod
395 @classmethod
396 def create(cls, form_data):
396 def create(cls, form_data):
397 try:
397 try:
398 new_users_group = cls()
398 new_users_group = cls()
399 for k, v in form_data.items():
399 for k, v in form_data.items():
400 setattr(new_users_group, k, v)
400 setattr(new_users_group, k, v)
401
401
402 Session().add(new_users_group)
402 Session().add(new_users_group)
403 Session().commit()
403 Session().commit()
404 return new_users_group
404 return new_users_group
405 except:
405 except:
406 log.error(traceback.format_exc())
406 log.error(traceback.format_exc())
407 Session().rollback()
407 Session().rollback()
408 raise
408 raise
409
409
410 @classmethod
410 @classmethod
411 def update(cls, users_group_id, form_data):
411 def update(cls, users_group_id, form_data):
412
412
413 try:
413 try:
414 users_group = cls.get(users_group_id, cache=False)
414 users_group = cls.get(users_group_id, cache=False)
415
415
416 for k, v in form_data.items():
416 for k, v in form_data.items():
417 if k == 'users_group_members':
417 if k == 'users_group_members':
418 users_group.members = []
418 users_group.members = []
419 Session().flush()
419 Session().flush()
420 members_list = []
420 members_list = []
421 if v:
421 if v:
422 v = [v] if isinstance(v, basestring) else v
422 v = [v] if isinstance(v, basestring) else v
423 for u_id in set(v):
423 for u_id in set(v):
424 member = UsersGroupMember(users_group_id, u_id)
424 member = UsersGroupMember(users_group_id, u_id)
425 members_list.append(member)
425 members_list.append(member)
426 setattr(users_group, 'members', members_list)
426 setattr(users_group, 'members', members_list)
427 setattr(users_group, k, v)
427 setattr(users_group, k, v)
428
428
429 Session().add(users_group)
429 Session().add(users_group)
430 Session().commit()
430 Session().commit()
431 except:
431 except:
432 log.error(traceback.format_exc())
432 log.error(traceback.format_exc())
433 Session().rollback()
433 Session().rollback()
434 raise
434 raise
435
435
436 @classmethod
436 @classmethod
437 def delete(cls, users_group_id):
437 def delete(cls, users_group_id):
438 try:
438 try:
439
439
440 # check if this group is not assigned to repo
440 # check if this group is not assigned to repo
441 assigned_groups = UsersGroupRepoToPerm.query()\
441 assigned_groups = UsersGroupRepoToPerm.query()\
442 .filter(UsersGroupRepoToPerm.users_group_id ==
442 .filter(UsersGroupRepoToPerm.users_group_id ==
443 users_group_id).all()
443 users_group_id).all()
444
444
445 if assigned_groups:
445 if assigned_groups:
446 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
446 raise UsersGroupsAssignedException('RepoGroup assigned to %s' %
447 assigned_groups)
447 assigned_groups)
448
448
449 users_group = cls.get(users_group_id, cache=False)
449 users_group = cls.get(users_group_id, cache=False)
450 Session().delete(users_group)
450 Session().delete(users_group)
451 Session().commit()
451 Session().commit()
452 except:
452 except:
453 log.error(traceback.format_exc())
453 log.error(traceback.format_exc())
454 Session().rollback()
454 Session().rollback()
455 raise
455 raise
456
456
457 class UsersGroupMember(Base, BaseModel):
457 class UsersGroupMember(Base, BaseModel):
458 __tablename__ = 'users_groups_members'
458 __tablename__ = 'users_groups_members'
459 __table_args__ = {'extend_existing':True}
459 __table_args__ = {'extend_existing':True}
460
460
461 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
461 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
462 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
462 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
463 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
463 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
464
464
465 user = relationship('User', lazy='joined')
465 user = relationship('User', lazy='joined')
466 users_group = relationship('UsersGroup')
466 users_group = relationship('UsersGroup')
467
467
468 def __init__(self, gr_id='', u_id=''):
468 def __init__(self, gr_id='', u_id=''):
469 self.users_group_id = gr_id
469 self.users_group_id = gr_id
470 self.user_id = u_id
470 self.user_id = u_id
471
471
472 @staticmethod
472 @staticmethod
473 def add_user_to_group(group, user):
473 def add_user_to_group(group, user):
474 ugm = UsersGroupMember()
474 ugm = UsersGroupMember()
475 ugm.users_group = group
475 ugm.users_group = group
476 ugm.user = user
476 ugm.user = user
477 Session().add(ugm)
477 Session().add(ugm)
478 Session().commit()
478 Session().commit()
479 return ugm
479 return ugm
480
480
481 class Repository(Base, BaseModel):
481 class Repository(Base, BaseModel):
482 __tablename__ = 'repositories'
482 __tablename__ = 'repositories'
483 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
483 __table_args__ = (UniqueConstraint('repo_name'), {'extend_existing':True},)
484
484
485 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
485 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
486 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
486 repo_name = Column("repo_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
487 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
487 clone_uri = Column("clone_uri", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
488 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
488 repo_type = Column("repo_type", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
489 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
489 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
490 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
490 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
491 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
491 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
492 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
492 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
493 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
493 description = Column("description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
494 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
494 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
495
495
496 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
496 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
497 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
497 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
498
498
499
499
500 user = relationship('User')
500 user = relationship('User')
501 fork = relationship('Repository', remote_side=repo_id)
501 fork = relationship('Repository', remote_side=repo_id)
502 group = relationship('RepoGroup')
502 group = relationship('RepoGroup')
503 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
503 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
504 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
504 users_group_to_perm = relationship('UsersGroupRepoToPerm', cascade='all')
505 stats = relationship('Statistics', cascade='all', uselist=False)
505 stats = relationship('Statistics', cascade='all', uselist=False)
506
506
507 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
507 followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
508
508
509 logs = relationship('UserLog', cascade='all')
509 logs = relationship('UserLog', cascade='all')
510
510
511 def __repr__(self):
511 def __repr__(self):
512 return "<%s('%s:%s')>" % (self.__class__.__name__,
512 return "<%s('%s:%s')>" % (self.__class__.__name__,
513 self.repo_id, self.repo_name)
513 self.repo_id, self.repo_name)
514
514
515 @classmethod
515 @classmethod
516 def url_sep(cls):
516 def url_sep(cls):
517 return '/'
517 return '/'
518
518
519 @classmethod
519 @classmethod
520 def get_by_repo_name(cls, repo_name):
520 def get_by_repo_name(cls, repo_name):
521 q = Session().query(cls).filter(cls.repo_name == repo_name)
521 q = Session().query(cls).filter(cls.repo_name == repo_name)
522 q = q.options(joinedload(Repository.fork))\
522 q = q.options(joinedload(Repository.fork))\
523 .options(joinedload(Repository.user))\
523 .options(joinedload(Repository.user))\
524 .options(joinedload(Repository.group))
524 .options(joinedload(Repository.group))
525 return q.one()
525 return q.one()
526
526
527 @classmethod
527 @classmethod
528 def get_repo_forks(cls, repo_id):
528 def get_repo_forks(cls, repo_id):
529 return cls.query().filter(Repository.fork_id == repo_id)
529 return cls.query().filter(Repository.fork_id == repo_id)
530
530
531 @classmethod
531 @classmethod
532 def base_path(cls):
532 def base_path(cls):
533 """
533 """
534 Returns base path when all repos are stored
534 Returns base path when all repos are stored
535
535
536 :param cls:
536 :param cls:
537 """
537 """
538 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
538 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
539 cls.url_sep())
539 cls.url_sep())
540 q.options(FromCache("sql_cache_short", "repository_repo_path"))
540 q.options(FromCache("sql_cache_short", "repository_repo_path"))
541 return q.one().ui_value
541 return q.one().ui_value
542
542
543 @property
543 @property
544 def just_name(self):
544 def just_name(self):
545 return self.repo_name.split(Repository.url_sep())[-1]
545 return self.repo_name.split(Repository.url_sep())[-1]
546
546
547 @property
547 @property
548 def groups_with_parents(self):
548 def groups_with_parents(self):
549 groups = []
549 groups = []
550 if self.group is None:
550 if self.group is None:
551 return groups
551 return groups
552
552
553 cur_gr = self.group
553 cur_gr = self.group
554 groups.insert(0, cur_gr)
554 groups.insert(0, cur_gr)
555 while 1:
555 while 1:
556 gr = getattr(cur_gr, 'parent_group', None)
556 gr = getattr(cur_gr, 'parent_group', None)
557 cur_gr = cur_gr.parent_group
557 cur_gr = cur_gr.parent_group
558 if gr is None:
558 if gr is None:
559 break
559 break
560 groups.insert(0, gr)
560 groups.insert(0, gr)
561
561
562 return groups
562 return groups
563
563
564 @property
564 @property
565 def groups_and_repo(self):
565 def groups_and_repo(self):
566 return self.groups_with_parents, self.just_name
566 return self.groups_with_parents, self.just_name
567
567
568 @LazyProperty
568 @LazyProperty
569 def repo_path(self):
569 def repo_path(self):
570 """
570 """
571 Returns base full path for that repository means where it actually
571 Returns base full path for that repository means where it actually
572 exists on a filesystem
572 exists on a filesystem
573 """
573 """
574 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
574 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
575 Repository.url_sep())
575 Repository.url_sep())
576 q.options(FromCache("sql_cache_short", "repository_repo_path"))
576 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
577 return q.one().ui_value
577 return q.one().ui_value
578
578
579 @property
579 @property
580 def repo_full_path(self):
580 def repo_full_path(self):
581 p = [self.repo_path]
581 p = [self.repo_path]
582 # we need to split the name by / since this is how we store the
582 # we need to split the name by / since this is how we store the
583 # names in the database, but that eventually needs to be converted
583 # names in the database, but that eventually needs to be converted
584 # into a valid system path
584 # into a valid system path
585 p += self.repo_name.split(Repository.url_sep())
585 p += self.repo_name.split(Repository.url_sep())
586 return os.path.join(*p)
586 return os.path.join(*p)
587
587
588 def get_new_name(self, repo_name):
588 def get_new_name(self, repo_name):
589 """
589 """
590 returns new full repository name based on assigned group and new new
590 returns new full repository name based on assigned group and new new
591
591
592 :param group_name:
592 :param group_name:
593 """
593 """
594 path_prefix = self.group.full_path_splitted if self.group else []
594 path_prefix = self.group.full_path_splitted if self.group else []
595 return Repository.url_sep().join(path_prefix + [repo_name])
595 return Repository.url_sep().join(path_prefix + [repo_name])
596
596
597 @property
597 @property
598 def _ui(self):
598 def _ui(self):
599 """
599 """
600 Creates an db based ui object for this repository
600 Creates an db based ui object for this repository
601 """
601 """
602 from mercurial import ui
602 from mercurial import ui
603 from mercurial import config
603 from mercurial import config
604 baseui = ui.ui()
604 baseui = ui.ui()
605
605
606 #clean the baseui object
606 #clean the baseui object
607 baseui._ocfg = config.config()
607 baseui._ocfg = config.config()
608 baseui._ucfg = config.config()
608 baseui._ucfg = config.config()
609 baseui._tcfg = config.config()
609 baseui._tcfg = config.config()
610
610
611
611
612 ret = RhodeCodeUi.query()\
612 ret = RhodeCodeUi.query()\
613 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
613 .options(FromCache("sql_cache_short", "repository_repo_ui")).all()
614
614
615 hg_ui = ret
615 hg_ui = ret
616 for ui_ in hg_ui:
616 for ui_ in hg_ui:
617 if ui_.ui_active:
617 if ui_.ui_active:
618 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
618 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
619 ui_.ui_key, ui_.ui_value)
619 ui_.ui_key, ui_.ui_value)
620 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
620 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
621
621
622 return baseui
622 return baseui
623
623
624 @classmethod
624 @classmethod
625 def is_valid(cls, repo_name):
625 def is_valid(cls, repo_name):
626 """
626 """
627 returns True if given repo name is a valid filesystem repository
627 returns True if given repo name is a valid filesystem repository
628
628
629 @param cls:
629 @param cls:
630 @param repo_name:
630 @param repo_name:
631 """
631 """
632 from rhodecode.lib.utils import is_valid_repo
632 from rhodecode.lib.utils import is_valid_repo
633
633
634 return is_valid_repo(repo_name, cls.base_path())
634 return is_valid_repo(repo_name, cls.base_path())
635
635
636
636
637 #==========================================================================
637 #==========================================================================
638 # SCM PROPERTIES
638 # SCM PROPERTIES
639 #==========================================================================
639 #==========================================================================
640
640
641 def get_changeset(self, rev):
641 def get_changeset(self, rev):
642 return get_changeset_safe(self.scm_instance, rev)
642 return get_changeset_safe(self.scm_instance, rev)
643
643
644 @property
644 @property
645 def tip(self):
645 def tip(self):
646 return self.get_changeset('tip')
646 return self.get_changeset('tip')
647
647
648 @property
648 @property
649 def author(self):
649 def author(self):
650 return self.tip.author
650 return self.tip.author
651
651
652 @property
652 @property
653 def last_change(self):
653 def last_change(self):
654 return self.scm_instance.last_change
654 return self.scm_instance.last_change
655
655
656 #==========================================================================
656 #==========================================================================
657 # SCM CACHE INSTANCE
657 # SCM CACHE INSTANCE
658 #==========================================================================
658 #==========================================================================
659
659
660 @property
660 @property
661 def invalidate(self):
661 def invalidate(self):
662 return CacheInvalidation.invalidate(self.repo_name)
662 return CacheInvalidation.invalidate(self.repo_name)
663
663
664 def set_invalidate(self):
664 def set_invalidate(self):
665 """
665 """
666 set a cache for invalidation for this instance
666 set a cache for invalidation for this instance
667 """
667 """
668 CacheInvalidation.set_invalidate(self.repo_name)
668 CacheInvalidation.set_invalidate(self.repo_name)
669
669
670 @LazyProperty
670 @LazyProperty
671 def scm_instance(self):
671 def scm_instance(self):
672 return self.__get_instance()
672 return self.__get_instance()
673
673
674 @property
674 @property
675 def scm_instance_cached(self):
675 def scm_instance_cached(self):
676 @cache_region('long_term')
676 @cache_region('long_term')
677 def _c(repo_name):
677 def _c(repo_name):
678 return self.__get_instance()
678 return self.__get_instance()
679 rn = self.repo_name
679 rn = self.repo_name
680
680
681 inv = self.invalidate
681 inv = self.invalidate
682 if inv is not None:
682 if inv is not None:
683 region_invalidate(_c, None, rn)
683 region_invalidate(_c, None, rn)
684 # update our cache
684 # update our cache
685 CacheInvalidation.set_valid(inv.cache_key)
685 CacheInvalidation.set_valid(inv.cache_key)
686 return _c(rn)
686 return _c(rn)
687
687
688 def __get_instance(self):
688 def __get_instance(self):
689
689
690 repo_full_path = self.repo_full_path
690 repo_full_path = self.repo_full_path
691
691
692 try:
692 try:
693 alias = get_scm(repo_full_path)[0]
693 alias = get_scm(repo_full_path)[0]
694 log.debug('Creating instance of %s repository', alias)
694 log.debug('Creating instance of %s repository', alias)
695 backend = get_backend(alias)
695 backend = get_backend(alias)
696 except VCSError:
696 except VCSError:
697 log.error(traceback.format_exc())
697 log.error(traceback.format_exc())
698 log.error('Perhaps this repository is in db and not in '
698 log.error('Perhaps this repository is in db and not in '
699 'filesystem run rescan repositories with '
699 'filesystem run rescan repositories with '
700 '"destroy old data " option from admin panel')
700 '"destroy old data " option from admin panel')
701 return
701 return
702
702
703 if alias == 'hg':
703 if alias == 'hg':
704
704
705 repo = backend(safe_str(repo_full_path), create=False,
705 repo = backend(safe_str(repo_full_path), create=False,
706 baseui=self._ui)
706 baseui=self._ui)
707 # skip hidden web repository
707 # skip hidden web repository
708 if repo._get_hidden():
708 if repo._get_hidden():
709 return
709 return
710 else:
710 else:
711 repo = backend(repo_full_path, create=False)
711 repo = backend(repo_full_path, create=False)
712
712
713 return repo
713 return repo
714
714
715
715
716 class RepoGroup(Base, BaseModel):
716 class RepoGroup(Base, BaseModel):
717 __tablename__ = 'groups'
717 __tablename__ = 'groups'
718 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
718 __table_args__ = (UniqueConstraint('group_name', 'group_parent_id'),
719 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
719 CheckConstraint('group_id != group_parent_id'), {'extend_existing':True},)
720 __mapper_args__ = {'order_by':'group_name'}
720 __mapper_args__ = {'order_by':'group_name'}
721
721
722 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
722 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
723 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
723 group_name = Column("group_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
724 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
724 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
725 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
725 group_description = Column("group_description", String(length=10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
726
726
727 parent_group = relationship('RepoGroup', remote_side=group_id)
727 parent_group = relationship('RepoGroup', remote_side=group_id)
728
728
729
729
730 def __init__(self, group_name='', parent_group=None):
730 def __init__(self, group_name='', parent_group=None):
731 self.group_name = group_name
731 self.group_name = group_name
732 self.parent_group = parent_group
732 self.parent_group = parent_group
733
733
734 def __repr__(self):
734 def __repr__(self):
735 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
735 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
736 self.group_name)
736 self.group_name)
737
737
738 @classmethod
738 @classmethod
739 def groups_choices(cls):
739 def groups_choices(cls):
740 from webhelpers.html import literal as _literal
740 from webhelpers.html import literal as _literal
741 repo_groups = [('', '')]
741 repo_groups = [('', '')]
742 sep = ' &raquo; '
742 sep = ' &raquo; '
743 _name = lambda k: _literal(sep.join(k))
743 _name = lambda k: _literal(sep.join(k))
744
744
745 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
745 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
746 for x in cls.query().all()])
746 for x in cls.query().all()])
747
747
748 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
748 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
749 return repo_groups
749 return repo_groups
750
750
751 @classmethod
751 @classmethod
752 def url_sep(cls):
752 def url_sep(cls):
753 return '/'
753 return '/'
754
754
755 @classmethod
755 @classmethod
756 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
756 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
757 if case_insensitive:
757 if case_insensitive:
758 gr = cls.query()\
758 gr = cls.query()\
759 .filter(cls.group_name.ilike(group_name))
759 .filter(cls.group_name.ilike(group_name))
760 else:
760 else:
761 gr = cls.query()\
761 gr = cls.query()\
762 .filter(cls.group_name == group_name)
762 .filter(cls.group_name == group_name)
763 if cache:
763 if cache:
764 gr = gr.options(FromCache("sql_cache_short",
764 gr = gr.options(FromCache("sql_cache_short",
765 "get_group_%s" % group_name))
765 "get_group_%s" % group_name))
766 return gr.scalar()
766 return gr.scalar()
767
767
768 @property
768 @property
769 def parents(self):
769 def parents(self):
770 parents_recursion_limit = 5
770 parents_recursion_limit = 5
771 groups = []
771 groups = []
772 if self.parent_group is None:
772 if self.parent_group is None:
773 return groups
773 return groups
774 cur_gr = self.parent_group
774 cur_gr = self.parent_group
775 groups.insert(0, cur_gr)
775 groups.insert(0, cur_gr)
776 cnt = 0
776 cnt = 0
777 while 1:
777 while 1:
778 cnt += 1
778 cnt += 1
779 gr = getattr(cur_gr, 'parent_group', None)
779 gr = getattr(cur_gr, 'parent_group', None)
780 cur_gr = cur_gr.parent_group
780 cur_gr = cur_gr.parent_group
781 if gr is None:
781 if gr is None:
782 break
782 break
783 if cnt == parents_recursion_limit:
783 if cnt == parents_recursion_limit:
784 # this will prevent accidental infinit loops
784 # this will prevent accidental infinit loops
785 log.error('group nested more than %s' %
785 log.error('group nested more than %s' %
786 parents_recursion_limit)
786 parents_recursion_limit)
787 break
787 break
788
788
789 groups.insert(0, gr)
789 groups.insert(0, gr)
790 return groups
790 return groups
791
791
792 @property
792 @property
793 def children(self):
793 def children(self):
794 return RepoGroup.query().filter(RepoGroup.parent_group == self)
794 return RepoGroup.query().filter(RepoGroup.parent_group == self)
795
795
796 @property
796 @property
797 def name(self):
797 def name(self):
798 return self.group_name.split(RepoGroup.url_sep())[-1]
798 return self.group_name.split(RepoGroup.url_sep())[-1]
799
799
800 @property
800 @property
801 def full_path(self):
801 def full_path(self):
802 return self.group_name
802 return self.group_name
803
803
804 @property
804 @property
805 def full_path_splitted(self):
805 def full_path_splitted(self):
806 return self.group_name.split(RepoGroup.url_sep())
806 return self.group_name.split(RepoGroup.url_sep())
807
807
808 @property
808 @property
809 def repositories(self):
809 def repositories(self):
810 return Repository.query().filter(Repository.group == self)
810 return Repository.query().filter(Repository.group == self)
811
811
812 @property
812 @property
813 def repositories_recursive_count(self):
813 def repositories_recursive_count(self):
814 cnt = self.repositories.count()
814 cnt = self.repositories.count()
815
815
816 def children_count(group):
816 def children_count(group):
817 cnt = 0
817 cnt = 0
818 for child in group.children:
818 for child in group.children:
819 cnt += child.repositories.count()
819 cnt += child.repositories.count()
820 cnt += children_count(child)
820 cnt += children_count(child)
821 return cnt
821 return cnt
822
822
823 return cnt + children_count(self)
823 return cnt + children_count(self)
824
824
825
825
826 def get_new_name(self, group_name):
826 def get_new_name(self, group_name):
827 """
827 """
828 returns new full group name based on parent and new name
828 returns new full group name based on parent and new name
829
829
830 :param group_name:
830 :param group_name:
831 """
831 """
832 path_prefix = (self.parent_group.full_path_splitted if
832 path_prefix = (self.parent_group.full_path_splitted if
833 self.parent_group else [])
833 self.parent_group else [])
834 return RepoGroup.url_sep().join(path_prefix + [group_name])
834 return RepoGroup.url_sep().join(path_prefix + [group_name])
835
835
836
836
837 class Permission(Base, BaseModel):
837 class Permission(Base, BaseModel):
838 __tablename__ = 'permissions'
838 __tablename__ = 'permissions'
839 __table_args__ = {'extend_existing':True}
839 __table_args__ = {'extend_existing':True}
840 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
840 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
841 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
841 permission_name = Column("permission_name", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
842 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
842 permission_longname = Column("permission_longname", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
843
843
844 def __repr__(self):
844 def __repr__(self):
845 return "<%s('%s:%s')>" % (self.__class__.__name__,
845 return "<%s('%s:%s')>" % (self.__class__.__name__,
846 self.permission_id, self.permission_name)
846 self.permission_id, self.permission_name)
847
847
848 @classmethod
848 @classmethod
849 def get_by_key(cls, key):
849 def get_by_key(cls, key):
850 return cls.query().filter(cls.permission_name == key).scalar()
850 return cls.query().filter(cls.permission_name == key).scalar()
851
851
852 class UserRepoToPerm(Base, BaseModel):
852 class UserRepoToPerm(Base, BaseModel):
853 __tablename__ = 'repo_to_perm'
853 __tablename__ = 'repo_to_perm'
854 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
854 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'extend_existing':True})
855 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
855 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
856 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
856 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
857 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
857 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
858 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
858 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
859
859
860 user = relationship('User')
860 user = relationship('User')
861 permission = relationship('Permission')
861 permission = relationship('Permission')
862 repository = relationship('Repository')
862 repository = relationship('Repository')
863
863
864 class UserToPerm(Base, BaseModel):
864 class UserToPerm(Base, BaseModel):
865 __tablename__ = 'user_to_perm'
865 __tablename__ = 'user_to_perm'
866 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
866 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'extend_existing':True})
867 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
867 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
868 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
868 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
869 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
869 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
870
870
871 user = relationship('User')
871 user = relationship('User')
872 permission = relationship('Permission')
872 permission = relationship('Permission')
873
873
874 @classmethod
874 @classmethod
875 def has_perm(cls, user_id, perm):
875 def has_perm(cls, user_id, perm):
876 if not isinstance(perm, Permission):
876 if not isinstance(perm, Permission):
877 raise Exception('perm needs to be an instance of Permission class')
877 raise Exception('perm needs to be an instance of Permission class')
878
878
879 return cls.query().filter(cls.user_id == user_id)\
879 return cls.query().filter(cls.user_id == user_id)\
880 .filter(cls.permission == perm).scalar() is not None
880 .filter(cls.permission == perm).scalar() is not None
881
881
882 @classmethod
882 @classmethod
883 def grant_perm(cls, user_id, perm):
883 def grant_perm(cls, user_id, perm):
884 if not isinstance(perm, Permission):
884 if not isinstance(perm, Permission):
885 raise Exception('perm needs to be an instance of Permission class')
885 raise Exception('perm needs to be an instance of Permission class')
886
886
887 new = cls()
887 new = cls()
888 new.user_id = user_id
888 new.user_id = user_id
889 new.permission = perm
889 new.permission = perm
890 try:
890 try:
891 Session().add(new)
891 Session().add(new)
892 Session().commit()
892 Session().commit()
893 except:
893 except:
894 Session().rollback()
894 Session().rollback()
895
895
896
896
897 @classmethod
897 @classmethod
898 def revoke_perm(cls, user_id, perm):
898 def revoke_perm(cls, user_id, perm):
899 if not isinstance(perm, Permission):
899 if not isinstance(perm, Permission):
900 raise Exception('perm needs to be an instance of Permission class')
900 raise Exception('perm needs to be an instance of Permission class')
901
901
902 try:
902 try:
903 obj = cls.query().filter(cls.user_id == user_id)\
903 obj = cls.query().filter(cls.user_id == user_id)\
904 .filter(cls.permission == perm).one()
904 .filter(cls.permission == perm).one()
905 Session().delete(obj)
905 Session().delete(obj)
906 Session().commit()
906 Session().commit()
907 except:
907 except:
908 Session().rollback()
908 Session().rollback()
909
909
910 class UsersGroupRepoToPerm(Base, BaseModel):
910 class UsersGroupRepoToPerm(Base, BaseModel):
911 __tablename__ = 'users_group_repo_to_perm'
911 __tablename__ = 'users_group_repo_to_perm'
912 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
912 __table_args__ = (UniqueConstraint('repository_id', 'users_group_id', 'permission_id'), {'extend_existing':True})
913 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
913 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
914 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
914 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
915 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
915 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
916 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
916 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
917
917
918 users_group = relationship('UsersGroup')
918 users_group = relationship('UsersGroup')
919 permission = relationship('Permission')
919 permission = relationship('Permission')
920 repository = relationship('Repository')
920 repository = relationship('Repository')
921
921
922 def __repr__(self):
922 def __repr__(self):
923 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
923 return '<userGroup:%s => %s >' % (self.users_group, self.repository)
924
924
925 class UsersGroupToPerm(Base, BaseModel):
925 class UsersGroupToPerm(Base, BaseModel):
926 __tablename__ = 'users_group_to_perm'
926 __tablename__ = 'users_group_to_perm'
927 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
927 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
928 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
928 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
929 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
929 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
930
930
931 users_group = relationship('UsersGroup')
931 users_group = relationship('UsersGroup')
932 permission = relationship('Permission')
932 permission = relationship('Permission')
933
933
934
934
935 @classmethod
935 @classmethod
936 def has_perm(cls, users_group_id, perm):
936 def has_perm(cls, users_group_id, perm):
937 if not isinstance(perm, Permission):
937 if not isinstance(perm, Permission):
938 raise Exception('perm needs to be an instance of Permission class')
938 raise Exception('perm needs to be an instance of Permission class')
939
939
940 return cls.query().filter(cls.users_group_id ==
940 return cls.query().filter(cls.users_group_id ==
941 users_group_id)\
941 users_group_id)\
942 .filter(cls.permission == perm)\
942 .filter(cls.permission == perm)\
943 .scalar() is not None
943 .scalar() is not None
944
944
945 @classmethod
945 @classmethod
946 def grant_perm(cls, users_group_id, perm):
946 def grant_perm(cls, users_group_id, perm):
947 if not isinstance(perm, Permission):
947 if not isinstance(perm, Permission):
948 raise Exception('perm needs to be an instance of Permission class')
948 raise Exception('perm needs to be an instance of Permission class')
949
949
950 new = cls()
950 new = cls()
951 new.users_group_id = users_group_id
951 new.users_group_id = users_group_id
952 new.permission = perm
952 new.permission = perm
953 try:
953 try:
954 Session().add(new)
954 Session().add(new)
955 Session().commit()
955 Session().commit()
956 except:
956 except:
957 Session().rollback()
957 Session().rollback()
958
958
959
959
960 @classmethod
960 @classmethod
961 def revoke_perm(cls, users_group_id, perm):
961 def revoke_perm(cls, users_group_id, perm):
962 if not isinstance(perm, Permission):
962 if not isinstance(perm, Permission):
963 raise Exception('perm needs to be an instance of Permission class')
963 raise Exception('perm needs to be an instance of Permission class')
964
964
965 try:
965 try:
966 obj = cls.query().filter(cls.users_group_id == users_group_id)\
966 obj = cls.query().filter(cls.users_group_id == users_group_id)\
967 .filter(cls.permission == perm).one()
967 .filter(cls.permission == perm).one()
968 Session().delete(obj)
968 Session().delete(obj)
969 Session().commit()
969 Session().commit()
970 except:
970 except:
971 Session().rollback()
971 Session().rollback()
972
972
973
973
974 class UserRepoGroupToPerm(Base, BaseModel):
974 class UserRepoGroupToPerm(Base, BaseModel):
975 __tablename__ = 'group_to_perm'
975 __tablename__ = 'group_to_perm'
976 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
976 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
977
977
978 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
978 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
979 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
979 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
980 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
980 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
981 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
981 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
982
982
983 user = relationship('User')
983 user = relationship('User')
984 permission = relationship('Permission')
984 permission = relationship('Permission')
985 group = relationship('RepoGroup')
985 group = relationship('RepoGroup')
986
986
987 class UsersGroupRepoGroupToPerm(Base, BaseModel):
987 class UsersGroupRepoGroupToPerm(Base, BaseModel):
988 __tablename__ = 'users_group_repo_group_to_perm'
988 __tablename__ = 'users_group_repo_group_to_perm'
989 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
989 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'extend_existing':True})
990
990
991 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
991 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
992 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
992 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
993 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
993 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
994 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
994 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
995
995
996 users_group = relationship('UsersGroup')
996 users_group = relationship('UsersGroup')
997 permission = relationship('Permission')
997 permission = relationship('Permission')
998 group = relationship('RepoGroup')
998 group = relationship('RepoGroup')
999
999
1000 class Statistics(Base, BaseModel):
1000 class Statistics(Base, BaseModel):
1001 __tablename__ = 'statistics'
1001 __tablename__ = 'statistics'
1002 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
1002 __table_args__ = (UniqueConstraint('repository_id'), {'extend_existing':True})
1003 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1003 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1004 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1004 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1005 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1005 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1006 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1006 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1007 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1007 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1008 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1008 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1009
1009
1010 repository = relationship('Repository', single_parent=True)
1010 repository = relationship('Repository', single_parent=True)
1011
1011
1012 class UserFollowing(Base, BaseModel):
1012 class UserFollowing(Base, BaseModel):
1013 __tablename__ = 'user_followings'
1013 __tablename__ = 'user_followings'
1014 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1014 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
1015 UniqueConstraint('user_id', 'follows_user_id')
1015 UniqueConstraint('user_id', 'follows_user_id')
1016 , {'extend_existing':True})
1016 , {'extend_existing':True})
1017
1017
1018 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1018 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1019 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1019 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1020 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1020 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1021 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1021 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1022 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1022 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1023
1023
1024 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1024 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1025
1025
1026 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1026 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1027 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1027 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1028
1028
1029
1029
1030 @classmethod
1030 @classmethod
1031 def get_repo_followers(cls, repo_id):
1031 def get_repo_followers(cls, repo_id):
1032 return cls.query().filter(cls.follows_repo_id == repo_id)
1032 return cls.query().filter(cls.follows_repo_id == repo_id)
1033
1033
1034 class CacheInvalidation(Base, BaseModel):
1034 class CacheInvalidation(Base, BaseModel):
1035 __tablename__ = 'cache_invalidation'
1035 __tablename__ = 'cache_invalidation'
1036 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1036 __table_args__ = (UniqueConstraint('cache_key'), {'extend_existing':True})
1037 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1037 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1038 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1038 cache_key = Column("cache_key", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1039 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1039 cache_args = Column("cache_args", String(length=255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1040 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1040 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1041
1041
1042
1042
1043 def __init__(self, cache_key, cache_args=''):
1043 def __init__(self, cache_key, cache_args=''):
1044 self.cache_key = cache_key
1044 self.cache_key = cache_key
1045 self.cache_args = cache_args
1045 self.cache_args = cache_args
1046 self.cache_active = False
1046 self.cache_active = False
1047
1047
1048 def __repr__(self):
1048 def __repr__(self):
1049 return "<%s('%s:%s')>" % (self.__class__.__name__,
1049 return "<%s('%s:%s')>" % (self.__class__.__name__,
1050 self.cache_id, self.cache_key)
1050 self.cache_id, self.cache_key)
1051
1051
1052 @classmethod
1052 @classmethod
1053 def invalidate(cls, key):
1053 def invalidate(cls, key):
1054 """
1054 """
1055 Returns Invalidation object if this given key should be invalidated
1055 Returns Invalidation object if this given key should be invalidated
1056 None otherwise. `cache_active = False` means that this cache
1056 None otherwise. `cache_active = False` means that this cache
1057 state is not valid and needs to be invalidated
1057 state is not valid and needs to be invalidated
1058
1058
1059 :param key:
1059 :param key:
1060 """
1060 """
1061 return cls.query()\
1061 return cls.query()\
1062 .filter(CacheInvalidation.cache_key == key)\
1062 .filter(CacheInvalidation.cache_key == key)\
1063 .filter(CacheInvalidation.cache_active == False)\
1063 .filter(CacheInvalidation.cache_active == False)\
1064 .scalar()
1064 .scalar()
1065
1065
1066 @classmethod
1066 @classmethod
1067 def set_invalidate(cls, key):
1067 def set_invalidate(cls, key):
1068 """
1068 """
1069 Mark this Cache key for invalidation
1069 Mark this Cache key for invalidation
1070
1070
1071 :param key:
1071 :param key:
1072 """
1072 """
1073
1073
1074 log.debug('marking %s for invalidation' % key)
1074 log.debug('marking %s for invalidation' % key)
1075 inv_obj = Session().query(cls)\
1075 inv_obj = Session().query(cls)\
1076 .filter(cls.cache_key == key).scalar()
1076 .filter(cls.cache_key == key).scalar()
1077 if inv_obj:
1077 if inv_obj:
1078 inv_obj.cache_active = False
1078 inv_obj.cache_active = False
1079 else:
1079 else:
1080 log.debug('cache key not found in invalidation db -> creating one')
1080 log.debug('cache key not found in invalidation db -> creating one')
1081 inv_obj = CacheInvalidation(key)
1081 inv_obj = CacheInvalidation(key)
1082
1082
1083 try:
1083 try:
1084 Session().add(inv_obj)
1084 Session().add(inv_obj)
1085 Session().commit()
1085 Session().commit()
1086 except Exception:
1086 except Exception:
1087 log.error(traceback.format_exc())
1087 log.error(traceback.format_exc())
1088 Session().rollback()
1088 Session().rollback()
1089
1089
1090 @classmethod
1090 @classmethod
1091 def set_valid(cls, key):
1091 def set_valid(cls, key):
1092 """
1092 """
1093 Mark this cache key as active and currently cached
1093 Mark this cache key as active and currently cached
1094
1094
1095 :param key:
1095 :param key:
1096 """
1096 """
1097 inv_obj = CacheInvalidation.query()\
1097 inv_obj = CacheInvalidation.query()\
1098 .filter(CacheInvalidation.cache_key == key).scalar()
1098 .filter(CacheInvalidation.cache_key == key).scalar()
1099 inv_obj.cache_active = True
1099 inv_obj.cache_active = True
1100 Session().add(inv_obj)
1100 Session().add(inv_obj)
1101 Session().commit()
1101 Session().commit()
1102
1102
1103
1103
1104 class ChangesetComment(Base, BaseModel):
1104 class ChangesetComment(Base, BaseModel):
1105 __tablename__ = 'changeset_comments'
1105 __tablename__ = 'changeset_comments'
1106 __table_args__ = ({'extend_existing':True},)
1106 __table_args__ = ({'extend_existing':True},)
1107 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1107 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1108 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1108 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1109 revision = Column('revision', String(40), nullable=False)
1109 revision = Column('revision', String(40), nullable=False)
1110 line_no = Column('line_no', Unicode(10), nullable=True)
1110 line_no = Column('line_no', Unicode(10), nullable=True)
1111 f_path = Column('f_path', Unicode(1000), nullable=True)
1111 f_path = Column('f_path', Unicode(1000), nullable=True)
1112 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1112 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1113 text = Column('text', Unicode(25000), nullable=False)
1113 text = Column('text', Unicode(25000), nullable=False)
1114 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1114 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1115
1115
1116 author = relationship('User', lazy='joined')
1116 author = relationship('User', lazy='joined')
1117 repo = relationship('Repository')
1117 repo = relationship('Repository')
1118
1118
1119
1119
1120 @classmethod
1120 @classmethod
1121 def get_users(cls, revision):
1121 def get_users(cls, revision):
1122 """
1122 """
1123 Returns user associated with this changesetComment. ie those
1123 Returns user associated with this changesetComment. ie those
1124 who actually commented
1124 who actually commented
1125
1125
1126 :param cls:
1126 :param cls:
1127 :param revision:
1127 :param revision:
1128 """
1128 """
1129 return Session().query(User)\
1129 return Session().query(User)\
1130 .filter(cls.revision == revision)\
1130 .filter(cls.revision == revision)\
1131 .join(ChangesetComment.author).all()
1131 .join(ChangesetComment.author).all()
1132
1132
1133
1133
1134 class Notification(Base, BaseModel):
1134 class Notification(Base, BaseModel):
1135 __tablename__ = 'notifications'
1135 __tablename__ = 'notifications'
1136 __table_args__ = ({'extend_existing':True})
1136 __table_args__ = ({'extend_existing':True})
1137
1137
1138 TYPE_CHANGESET_COMMENT = u'cs_comment'
1138 TYPE_CHANGESET_COMMENT = u'cs_comment'
1139 TYPE_MESSAGE = u'message'
1139 TYPE_MESSAGE = u'message'
1140 TYPE_MENTION = u'mention'
1140 TYPE_MENTION = u'mention'
1141
1141
1142 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1142 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
1143 subject = Column('subject', Unicode(512), nullable=True)
1143 subject = Column('subject', Unicode(512), nullable=True)
1144 body = Column('body', Unicode(50000), nullable=True)
1144 body = Column('body', Unicode(50000), nullable=True)
1145 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1145 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
1146 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1146 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1147 type_ = Column('type', Unicode(256))
1147 type_ = Column('type', Unicode(256))
1148
1148
1149 created_by_user = relationship('User')
1149 created_by_user = relationship('User')
1150 notifications_to_users = relationship('UserNotification', lazy='joined',
1150 notifications_to_users = relationship('UserNotification', lazy='joined',
1151 cascade="all, delete, delete-orphan")
1151 cascade="all, delete, delete-orphan")
1152
1152
1153 @property
1153 @property
1154 def recipients(self):
1154 def recipients(self):
1155 return [x.user for x in UserNotification.query()\
1155 return [x.user for x in UserNotification.query()\
1156 .filter(UserNotification.notification == self).all()]
1156 .filter(UserNotification.notification == self).all()]
1157
1157
1158 @classmethod
1158 @classmethod
1159 def create(cls, created_by, subject, body, recipients, type_=None):
1159 def create(cls, created_by, subject, body, recipients, type_=None):
1160 if type_ is None:
1160 if type_ is None:
1161 type_ = Notification.TYPE_MESSAGE
1161 type_ = Notification.TYPE_MESSAGE
1162
1162
1163 notification = cls()
1163 notification = cls()
1164 notification.created_by_user = created_by
1164 notification.created_by_user = created_by
1165 notification.subject = subject
1165 notification.subject = subject
1166 notification.body = body
1166 notification.body = body
1167 notification.type_ = type_
1167 notification.type_ = type_
1168 notification.created_on = datetime.datetime.now()
1168 notification.created_on = datetime.datetime.now()
1169
1169
1170 for u in recipients:
1170 for u in recipients:
1171 assoc = UserNotification()
1171 assoc = UserNotification()
1172 assoc.notification = notification
1172 assoc.notification = notification
1173 u.notifications.append(assoc)
1173 u.notifications.append(assoc)
1174 Session().add(notification)
1174 Session().add(notification)
1175 return notification
1175 return notification
1176
1176
1177 @property
1177 @property
1178 def description(self):
1178 def description(self):
1179 from rhodecode.model.notification import NotificationModel
1179 from rhodecode.model.notification import NotificationModel
1180 return NotificationModel().make_description(self)
1180 return NotificationModel().make_description(self)
1181
1181
1182 class UserNotification(Base, BaseModel):
1182 class UserNotification(Base, BaseModel):
1183 __tablename__ = 'user_to_notification'
1183 __tablename__ = 'user_to_notification'
1184 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1184 __table_args__ = (UniqueConstraint('user_id', 'notification_id'),
1185 {'extend_existing':True})
1185 {'extend_existing':True})
1186 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), primary_key=True)
1186 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), primary_key=True)
1187 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1187 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
1188 read = Column('read', Boolean, default=False)
1188 read = Column('read', Boolean, default=False)
1189 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1189 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
1190
1190
1191 user = relationship('User', lazy="joined")
1191 user = relationship('User', lazy="joined")
1192 notification = relationship('Notification', lazy="joined",
1192 notification = relationship('Notification', lazy="joined",
1193 order_by=lambda:Notification.created_on.desc(),
1193 order_by=lambda:Notification.created_on.desc(),
1194 cascade='all')
1194 cascade='all')
1195
1195
1196 def mark_as_read(self):
1196 def mark_as_read(self):
1197 self.read = True
1197 self.read = True
1198 Session().add(self)
1198 Session().add(self)
1199
1199
1200 class DbMigrateVersion(Base, BaseModel):
1200 class DbMigrateVersion(Base, BaseModel):
1201 __tablename__ = 'db_migrate_version'
1201 __tablename__ = 'db_migrate_version'
1202 __table_args__ = {'extend_existing':True}
1202 __table_args__ = {'extend_existing':True}
1203 repository_id = Column('repository_id', String(250), primary_key=True)
1203 repository_id = Column('repository_id', String(250), primary_key=True)
1204 repository_path = Column('repository_path', Text)
1204 repository_path = Column('repository_path', Text)
1205 version = Column('version', Integer)
1205 version = Column('version', Integer)
1206
1206
General Comments 0
You need to be logged in to leave comments. Login now