##// END OF EJS Templates
fixes #98 protection against float division of percentage stats
marcink -
r935:5e8c7d78 beta
parent child Browse files
Show More
@@ -1,313 +1,313 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Admin controller for RhodeCode
6 Admin controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 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
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import formencode
30 import formencode
31 from operator import itemgetter
31 from operator import itemgetter
32 from formencode import htmlfill
32 from formencode import htmlfill
33
33
34 from paste.httpexceptions import HTTPInternalServerError
34 from paste.httpexceptions import HTTPInternalServerError
35 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
36 from pylons.controllers.util import abort, redirect
36 from pylons.controllers.util import abort, redirect
37 from pylons.i18n.translation import _
37 from pylons.i18n.translation import _
38
38
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 HasPermissionAnyDecorator
41 HasPermissionAnyDecorator
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43 from rhodecode.lib.utils import invalidate_cache, action_logger
43 from rhodecode.lib.utils import invalidate_cache, action_logger
44 from rhodecode.model.db import User
44 from rhodecode.model.db import User
45 from rhodecode.model.forms import RepoForm
45 from rhodecode.model.forms import RepoForm
46 from rhodecode.model.scm import ScmModel
46 from rhodecode.model.scm import ScmModel
47 from rhodecode.model.repo import RepoModel
47 from rhodecode.model.repo import RepoModel
48
48
49
49
50 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
51
51
52 class ReposController(BaseController):
52 class ReposController(BaseController):
53 """REST Controller styled on the Atom Publishing Protocol"""
53 """REST Controller styled on the Atom Publishing Protocol"""
54 # To properly map this controller, ensure your config/routing.py
54 # To properly map this controller, ensure your config/routing.py
55 # file has a resource setup:
55 # file has a resource setup:
56 # map.resource('repo', 'repos')
56 # map.resource('repo', 'repos')
57
57
58 @LoginRequired()
58 @LoginRequired()
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
59 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
60 def __before__(self):
60 def __before__(self):
61 c.admin_user = session.get('admin_user')
61 c.admin_user = session.get('admin_user')
62 c.admin_username = session.get('admin_username')
62 c.admin_username = session.get('admin_username')
63 super(ReposController, self).__before__()
63 super(ReposController, self).__before__()
64
64
65 @HasPermissionAllDecorator('hg.admin')
65 @HasPermissionAllDecorator('hg.admin')
66 def index(self, format='html'):
66 def index(self, format='html'):
67 """GET /repos: All items in the collection"""
67 """GET /repos: All items in the collection"""
68 # url('repos')
68 # url('repos')
69 cached_repo_list = ScmModel().get_repos()
69 cached_repo_list = ScmModel().get_repos()
70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
70 c.repos_list = sorted(cached_repo_list, key=itemgetter('name_sort'))
71 return render('admin/repos/repos.html')
71 return render('admin/repos/repos.html')
72
72
73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
73 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
74 def create(self):
74 def create(self):
75 """POST /repos: Create a new item"""
75 """POST /repos: Create a new item"""
76 # url('repos')
76 # url('repos')
77 repo_model = RepoModel()
77 repo_model = RepoModel()
78 _form = RepoForm()()
78 _form = RepoForm()()
79 form_result = {}
79 form_result = {}
80 try:
80 try:
81 form_result = _form.to_python(dict(request.POST))
81 form_result = _form.to_python(dict(request.POST))
82 repo_model.create(form_result, c.rhodecode_user)
82 repo_model.create(form_result, c.rhodecode_user)
83 h.flash(_('created repository %s') % form_result['repo_name'],
83 h.flash(_('created repository %s') % form_result['repo_name'],
84 category='success')
84 category='success')
85
85
86 if request.POST.get('user_created'):
86 if request.POST.get('user_created'):
87 action_logger(self.rhodecode_user, 'user_created_repo',
87 action_logger(self.rhodecode_user, 'user_created_repo',
88 form_result['repo_name'], '', self.sa)
88 form_result['repo_name'], '', self.sa)
89 else:
89 else:
90 action_logger(self.rhodecode_user, 'admin_created_repo',
90 action_logger(self.rhodecode_user, 'admin_created_repo',
91 form_result['repo_name'], '', self.sa)
91 form_result['repo_name'], '', self.sa)
92
92
93 except formencode.Invalid, errors:
93 except formencode.Invalid, errors:
94 c.new_repo = errors.value['repo_name']
94 c.new_repo = errors.value['repo_name']
95
95
96 if request.POST.get('user_created'):
96 if request.POST.get('user_created'):
97 r = render('admin/repos/repo_add_create_repository.html')
97 r = render('admin/repos/repo_add_create_repository.html')
98 else:
98 else:
99 r = render('admin/repos/repo_add.html')
99 r = render('admin/repos/repo_add.html')
100
100
101 return htmlfill.render(
101 return htmlfill.render(
102 r,
102 r,
103 defaults=errors.value,
103 defaults=errors.value,
104 errors=errors.error_dict or {},
104 errors=errors.error_dict or {},
105 prefix_error=False,
105 prefix_error=False,
106 encoding="UTF-8")
106 encoding="UTF-8")
107
107
108 except Exception:
108 except Exception:
109 log.error(traceback.format_exc())
109 log.error(traceback.format_exc())
110 msg = _('error occurred during creation of repository %s') \
110 msg = _('error occurred during creation of repository %s') \
111 % form_result.get('repo_name')
111 % form_result.get('repo_name')
112 h.flash(msg, category='error')
112 h.flash(msg, category='error')
113 if request.POST.get('user_created'):
113 if request.POST.get('user_created'):
114 return redirect(url('home'))
114 return redirect(url('home'))
115 return redirect(url('repos'))
115 return redirect(url('repos'))
116
116
117 @HasPermissionAllDecorator('hg.admin')
117 @HasPermissionAllDecorator('hg.admin')
118 def new(self, format='html'):
118 def new(self, format='html'):
119 """GET /repos/new: Form to create a new item"""
119 """GET /repos/new: Form to create a new item"""
120 new_repo = request.GET.get('repo', '')
120 new_repo = request.GET.get('repo', '')
121 c.new_repo = h.repo_name_slug(new_repo)
121 c.new_repo = h.repo_name_slug(new_repo)
122
122
123 return render('admin/repos/repo_add.html')
123 return render('admin/repos/repo_add.html')
124
124
125 @HasPermissionAllDecorator('hg.admin')
125 @HasPermissionAllDecorator('hg.admin')
126 def update(self, repo_name):
126 def update(self, repo_name):
127 """PUT /repos/repo_name: Update an existing item"""
127 """PUT /repos/repo_name: Update an existing item"""
128 # Forms posted to this method should contain a hidden field:
128 # Forms posted to this method should contain a hidden field:
129 # <input type="hidden" name="_method" value="PUT" />
129 # <input type="hidden" name="_method" value="PUT" />
130 # Or using helpers:
130 # Or using helpers:
131 # h.form(url('repo', repo_name=ID),
131 # h.form(url('repo', repo_name=ID),
132 # method='put')
132 # method='put')
133 # url('repo', repo_name=ID)
133 # url('repo', repo_name=ID)
134 repo_model = RepoModel()
134 repo_model = RepoModel()
135 changed_name = repo_name
135 changed_name = repo_name
136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
136 _form = RepoForm(edit=True, old_data={'repo_name':repo_name})()
137
137
138 try:
138 try:
139 form_result = _form.to_python(dict(request.POST))
139 form_result = _form.to_python(dict(request.POST))
140 repo_model.update(repo_name, form_result)
140 repo_model.update(repo_name, form_result)
141 invalidate_cache('get_repo_cached_%s' % repo_name)
141 invalidate_cache('get_repo_cached_%s' % repo_name)
142 h.flash(_('Repository %s updated successfully' % repo_name),
142 h.flash(_('Repository %s updated successfully' % repo_name),
143 category='success')
143 category='success')
144 changed_name = form_result['repo_name']
144 changed_name = form_result['repo_name']
145 action_logger(self.rhodecode_user, 'admin_updated_repo',
145 action_logger(self.rhodecode_user, 'admin_updated_repo',
146 changed_name, '', self.sa)
146 changed_name, '', self.sa)
147
147
148 except formencode.Invalid, errors:
148 except formencode.Invalid, errors:
149 c.repo_info = repo_model.get_by_repo_name(repo_name)
149 c.repo_info = repo_model.get_by_repo_name(repo_name)
150 if c.repo_info.stats:
150 if c.repo_info.stats:
151 last_rev = c.repo_info.stats.stat_on_revision
151 last_rev = c.repo_info.stats.stat_on_revision
152 else:
152 else:
153 last_rev = 0
153 last_rev = 0
154 c.stats_revision = last_rev
154 c.stats_revision = last_rev
155 r = ScmModel().get(repo_name)
155 r = ScmModel().get(repo_name)
156 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
156 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
157
157
158 if last_rev == 0:
158 if last_rev == 0:
159 c.stats_percentage = 0
159 c.stats_percentage = 0
160 else:
160 else:
161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
161 c.stats_percentage = '%.2f' % ((float((last_rev)) /
162 c.repo_last_rev) * 100)
162 c.repo_last_rev) * 100)
163
163
164 c.users_array = repo_model.get_users_js()
164 c.users_array = repo_model.get_users_js()
165 errors.value.update({'user':c.repo_info.user.username})
165 errors.value.update({'user':c.repo_info.user.username})
166 return htmlfill.render(
166 return htmlfill.render(
167 render('admin/repos/repo_edit.html'),
167 render('admin/repos/repo_edit.html'),
168 defaults=errors.value,
168 defaults=errors.value,
169 errors=errors.error_dict or {},
169 errors=errors.error_dict or {},
170 prefix_error=False,
170 prefix_error=False,
171 encoding="UTF-8")
171 encoding="UTF-8")
172
172
173 except Exception:
173 except Exception:
174 log.error(traceback.format_exc())
174 log.error(traceback.format_exc())
175 h.flash(_('error occurred during update of repository %s') \
175 h.flash(_('error occurred during update of repository %s') \
176 % repo_name, category='error')
176 % repo_name, category='error')
177
177
178 return redirect(url('edit_repo', repo_name=changed_name))
178 return redirect(url('edit_repo', repo_name=changed_name))
179
179
180 @HasPermissionAllDecorator('hg.admin')
180 @HasPermissionAllDecorator('hg.admin')
181 def delete(self, repo_name):
181 def delete(self, repo_name):
182 """DELETE /repos/repo_name: Delete an existing item"""
182 """DELETE /repos/repo_name: Delete an existing item"""
183 # Forms posted to this method should contain a hidden field:
183 # Forms posted to this method should contain a hidden field:
184 # <input type="hidden" name="_method" value="DELETE" />
184 # <input type="hidden" name="_method" value="DELETE" />
185 # Or using helpers:
185 # Or using helpers:
186 # h.form(url('repo', repo_name=ID),
186 # h.form(url('repo', repo_name=ID),
187 # method='delete')
187 # method='delete')
188 # url('repo', repo_name=ID)
188 # url('repo', repo_name=ID)
189
189
190 repo_model = RepoModel()
190 repo_model = RepoModel()
191 repo = repo_model.get_by_repo_name(repo_name)
191 repo = repo_model.get_by_repo_name(repo_name)
192 if not repo:
192 if not repo:
193 h.flash(_('%s repository is not mapped to db perhaps'
193 h.flash(_('%s repository is not mapped to db perhaps'
194 ' it was moved or renamed from the filesystem'
194 ' it was moved or renamed from the filesystem'
195 ' please run the application again'
195 ' please run the application again'
196 ' in order to rescan repositories') % repo_name,
196 ' in order to rescan repositories') % repo_name,
197 category='error')
197 category='error')
198
198
199 return redirect(url('repos'))
199 return redirect(url('repos'))
200 try:
200 try:
201 action_logger(self.rhodecode_user, 'admin_deleted_repo',
201 action_logger(self.rhodecode_user, 'admin_deleted_repo',
202 repo_name, '', self.sa)
202 repo_name, '', self.sa)
203 repo_model.delete(repo)
203 repo_model.delete(repo)
204 invalidate_cache('get_repo_cached_%s' % repo_name)
204 invalidate_cache('get_repo_cached_%s' % repo_name)
205 h.flash(_('deleted repository %s') % repo_name, category='success')
205 h.flash(_('deleted repository %s') % repo_name, category='success')
206
206
207 except Exception, e:
207 except Exception, e:
208 log.error(traceback.format_exc())
208 log.error(traceback.format_exc())
209 h.flash(_('An error occurred during deletion of %s') % repo_name,
209 h.flash(_('An error occurred during deletion of %s') % repo_name,
210 category='error')
210 category='error')
211
211
212 return redirect(url('repos'))
212 return redirect(url('repos'))
213
213
214 @HasPermissionAllDecorator('hg.admin')
214 @HasPermissionAllDecorator('hg.admin')
215 def delete_perm_user(self, repo_name):
215 def delete_perm_user(self, repo_name):
216 """
216 """
217 DELETE an existing repository permission user
217 DELETE an existing repository permission user
218 :param repo_name:
218 :param repo_name:
219 """
219 """
220
220
221 try:
221 try:
222 repo_model = RepoModel()
222 repo_model = RepoModel()
223 repo_model.delete_perm_user(request.POST, repo_name)
223 repo_model.delete_perm_user(request.POST, repo_name)
224 except Exception, e:
224 except Exception, e:
225 h.flash(_('An error occurred during deletion of repository user'),
225 h.flash(_('An error occurred during deletion of repository user'),
226 category='error')
226 category='error')
227 raise HTTPInternalServerError()
227 raise HTTPInternalServerError()
228
228
229 @HasPermissionAllDecorator('hg.admin')
229 @HasPermissionAllDecorator('hg.admin')
230 def repo_stats(self, repo_name):
230 def repo_stats(self, repo_name):
231 """
231 """
232 DELETE an existing repository statistics
232 DELETE an existing repository statistics
233 :param repo_name:
233 :param repo_name:
234 """
234 """
235
235
236 try:
236 try:
237 repo_model = RepoModel()
237 repo_model = RepoModel()
238 repo_model.delete_stats(repo_name)
238 repo_model.delete_stats(repo_name)
239 except Exception, e:
239 except Exception, e:
240 h.flash(_('An error occurred during deletion of repository stats'),
240 h.flash(_('An error occurred during deletion of repository stats'),
241 category='error')
241 category='error')
242 return redirect(url('edit_repo', repo_name=repo_name))
242 return redirect(url('edit_repo', repo_name=repo_name))
243
243
244 @HasPermissionAllDecorator('hg.admin')
244 @HasPermissionAllDecorator('hg.admin')
245 def repo_cache(self, repo_name):
245 def repo_cache(self, repo_name):
246 """
246 """
247 INVALIDATE existing repository cache
247 INVALIDATE existing repository cache
248 :param repo_name:
248 :param repo_name:
249 """
249 """
250
250
251 try:
251 try:
252 ScmModel().mark_for_invalidation(repo_name)
252 ScmModel().mark_for_invalidation(repo_name)
253 except Exception, e:
253 except Exception, e:
254 h.flash(_('An error occurred during cache invalidation'),
254 h.flash(_('An error occurred during cache invalidation'),
255 category='error')
255 category='error')
256 return redirect(url('edit_repo', repo_name=repo_name))
256 return redirect(url('edit_repo', repo_name=repo_name))
257
257
258 @HasPermissionAllDecorator('hg.admin')
258 @HasPermissionAllDecorator('hg.admin')
259 def show(self, repo_name, format='html'):
259 def show(self, repo_name, format='html'):
260 """GET /repos/repo_name: Show a specific item"""
260 """GET /repos/repo_name: Show a specific item"""
261 # url('repo', repo_name=ID)
261 # url('repo', repo_name=ID)
262
262
263 @HasPermissionAllDecorator('hg.admin')
263 @HasPermissionAllDecorator('hg.admin')
264 def edit(self, repo_name, format='html'):
264 def edit(self, repo_name, format='html'):
265 """GET /repos/repo_name/edit: Form to edit an existing item"""
265 """GET /repos/repo_name/edit: Form to edit an existing item"""
266 # url('edit_repo', repo_name=ID)
266 # url('edit_repo', repo_name=ID)
267 repo_model = RepoModel()
267 repo_model = RepoModel()
268 r = ScmModel().get(repo_name)
268 r = ScmModel().get(repo_name)
269 c.repo_info = repo_model.get_by_repo_name(repo_name)
269 c.repo_info = repo_model.get_by_repo_name(repo_name)
270
270
271 if c.repo_info is None:
271 if c.repo_info is None:
272 h.flash(_('%s repository is not mapped to db perhaps'
272 h.flash(_('%s repository is not mapped to db perhaps'
273 ' it was created or renamed from the filesystem'
273 ' it was created or renamed from the filesystem'
274 ' please run the application again'
274 ' please run the application again'
275 ' in order to rescan repositories') % repo_name,
275 ' in order to rescan repositories') % repo_name,
276 category='error')
276 category='error')
277
277
278 return redirect(url('repos'))
278 return redirect(url('repos'))
279
279
280 if c.repo_info.stats:
280 if c.repo_info.stats:
281 last_rev = c.repo_info.stats.stat_on_revision
281 last_rev = c.repo_info.stats.stat_on_revision
282 else:
282 else:
283 last_rev = 0
283 last_rev = 0
284 c.stats_revision = last_rev
284 c.stats_revision = last_rev
285
285
286 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
286 c.repo_last_rev = r.revisions[-1] if r.revisions else 0
287
287
288 if last_rev == 0:
288 if last_rev == 0 or c.repo_last_rev == 0:
289 c.stats_percentage = 0
289 c.stats_percentage = 0
290 else:
290 else:
291 c.stats_percentage = '%.2f' % ((float((last_rev)) /
291 c.stats_percentage = '%.2f' % ((float((last_rev)) /
292 c.repo_last_rev) * 100)
292 c.repo_last_rev) * 100)
293
293
294 defaults = c.repo_info.get_dict()
294 defaults = c.repo_info.get_dict()
295 if c.repo_info.user:
295 if c.repo_info.user:
296 defaults.update({'user':c.repo_info.user.username})
296 defaults.update({'user':c.repo_info.user.username})
297 else:
297 else:
298 replacement_user = self.sa.query(User)\
298 replacement_user = self.sa.query(User)\
299 .filter(User.admin == True).first().username
299 .filter(User.admin == True).first().username
300 defaults.update({'user':replacement_user})
300 defaults.update({'user':replacement_user})
301
301
302 c.users_array = repo_model.get_users_js()
302 c.users_array = repo_model.get_users_js()
303
303
304 for p in c.repo_info.repo_to_perm:
304 for p in c.repo_info.repo_to_perm:
305 defaults.update({'perm_%s' % p.user.username:
305 defaults.update({'perm_%s' % p.user.username:
306 p.permission.permission_name})
306 p.permission.permission_name})
307
307
308 return htmlfill.render(
308 return htmlfill.render(
309 render('admin/repos/repo_edit.html'),
309 render('admin/repos/repo_edit.html'),
310 defaults=defaults,
310 defaults=defaults,
311 encoding="UTF-8",
311 encoding="UTF-8",
312 force_defaults=False
312 force_defaults=False
313 )
313 )
General Comments 0
You need to be logged in to leave comments. Login now