##// END OF EJS Templates
user-groups: fixed in_ filters for large ammount of data.
marcink -
r3624:1068a814 default
parent child Browse files
Show More
@@ -1,259 +1,271 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import logging
22 22
23 23 import formencode
24 24 import formencode.htmlfill
25 25
26 26 from pyramid.httpexceptions import HTTPFound
27 27 from pyramid.view import view_config
28 28 from pyramid.response import Response
29 29 from pyramid.renderers import render
30 30
31 31 from rhodecode import events
32 32 from rhodecode.apps._base import BaseAppView, DataGridAppView
33 33 from rhodecode.lib.auth import (
34 34 LoginRequired, NotAnonymous, CSRFRequired, HasPermissionAnyDecorator)
35 35 from rhodecode.lib import helpers as h, audit_logger
36 36 from rhodecode.lib.utils2 import safe_unicode
37 37
38 38 from rhodecode.model.forms import UserGroupForm
39 39 from rhodecode.model.permission import PermissionModel
40 40 from rhodecode.model.scm import UserGroupList
41 41 from rhodecode.model.db import (
42 or_, count, User, UserGroup, UserGroupMember)
42 or_, count, User, UserGroup, UserGroupMember, in_filter_generator)
43 43 from rhodecode.model.meta import Session
44 44 from rhodecode.model.user_group import UserGroupModel
45 45 from rhodecode.model.db import true
46 46
47 47 log = logging.getLogger(__name__)
48 48
49 49
50 50 class AdminUserGroupsView(BaseAppView, DataGridAppView):
51 51
52 52 def load_default_context(self):
53 53 c = self._get_local_tmpl_context()
54 54
55 55 PermissionModel().set_global_permission_choices(
56 56 c, gettext_translator=self.request.translate)
57 57
58 58 return c
59 59
60 60 # permission check in data loading of
61 61 # `user_groups_list_data` via UserGroupList
62 62 @LoginRequired()
63 63 @NotAnonymous()
64 64 @view_config(
65 65 route_name='user_groups', request_method='GET',
66 66 renderer='rhodecode:templates/admin/user_groups/user_groups.mako')
67 67 def user_groups_list(self):
68 68 c = self.load_default_context()
69 69 return self._get_template_context(c)
70 70
71 71 # permission check inside
72 72 @LoginRequired()
73 73 @NotAnonymous()
74 74 @view_config(
75 75 route_name='user_groups_data', request_method='GET',
76 76 renderer='json_ext', xhr=True)
77 77 def user_groups_list_data(self):
78 78 self.load_default_context()
79 79 column_map = {
80 80 'active': 'users_group_active',
81 81 'description': 'user_group_description',
82 82 'members': 'members_total',
83 83 'owner': 'user_username',
84 84 'sync': 'group_data'
85 85 }
86 86 draw, start, limit = self._extract_chunk(self.request)
87 87 search_q, order_by, order_dir = self._extract_ordering(
88 88 self.request, column_map=column_map)
89 89
90 90 _render = self.request.get_partial_renderer(
91 91 'rhodecode:templates/data_table/_dt_elements.mako')
92 92
93 93 def user_group_name(user_group_name):
94 94 return _render("user_group_name", user_group_name)
95 95
96 96 def user_group_actions(user_group_id, user_group_name):
97 97 return _render("user_group_actions", user_group_id, user_group_name)
98 98
99 99 def user_profile(username):
100 100 return _render('user_profile', username)
101 101
102 102 auth_user_group_list = UserGroupList(
103 103 UserGroup.query().all(), perm_set=['usergroup.admin'])
104 104
105 105 allowed_ids = [-1]
106 106 for user_group in auth_user_group_list:
107 107 allowed_ids.append(user_group.users_group_id)
108 108
109 109 user_groups_data_total_count = UserGroup.query()\
110 .filter(UserGroup.users_group_id.in_(allowed_ids))\
110 .filter(or_(
111 # generate multiple IN to fix limitation problems
112 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
113 ))\
111 114 .count()
112 115
113 116 user_groups_data_total_inactive_count = UserGroup.query()\
114 .filter(UserGroup.users_group_id.in_(allowed_ids))\
117 .filter(or_(
118 # generate multiple IN to fix limitation problems
119 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
120 ))\
115 121 .filter(UserGroup.users_group_active != true()).count()
116 122
117 123 member_count = count(UserGroupMember.user_id)
118 124 base_q = Session.query(
119 125 UserGroup.users_group_name,
120 126 UserGroup.user_group_description,
121 127 UserGroup.users_group_active,
122 128 UserGroup.users_group_id,
123 129 UserGroup.group_data,
124 130 User,
125 131 member_count.label('member_count')
126 ) \
127 .filter(UserGroup.users_group_id.in_(allowed_ids)) \
128 .outerjoin(UserGroupMember) \
129 .join(User, User.user_id == UserGroup.user_id) \
130 .group_by(UserGroup, User)
132 ) \
133 .filter(or_(
134 # generate multiple IN to fix limitation problems
135 *in_filter_generator(UserGroup.users_group_id, allowed_ids)
136 )) \
137 .outerjoin(UserGroupMember) \
138 .join(User, User.user_id == UserGroup.user_id) \
139 .group_by(UserGroup, User)
131 140
132 141 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
133 142
134 143 if search_q:
135 144 like_expression = u'%{}%'.format(safe_unicode(search_q))
136 145 base_q = base_q.filter(or_(
137 146 UserGroup.users_group_name.ilike(like_expression),
138 147 ))
139 148 base_q_inactive = base_q.filter(UserGroup.users_group_active != true())
140 149
141 150 user_groups_data_total_filtered_count = base_q.count()
142 151 user_groups_data_total_filtered_inactive_count = base_q_inactive.count()
143 152
153 sort_defined = False
144 154 if order_by == 'members_total':
145 155 sort_col = member_count
156 sort_defined = True
146 157 elif order_by == 'user_username':
147 158 sort_col = User.username
148 159 else:
149 160 sort_col = getattr(UserGroup, order_by, None)
150 161
151 if isinstance(sort_col, count) or sort_col:
162 if sort_defined or sort_col:
152 163 if order_dir == 'asc':
153 164 sort_col = sort_col.asc()
154 165 else:
155 166 sort_col = sort_col.desc()
156 167
157 168 base_q = base_q.order_by(sort_col)
158 169 base_q = base_q.offset(start).limit(limit)
159 170
160 171 # authenticated access to user groups
161 172 auth_user_group_list = base_q.all()
162 173
163 174 user_groups_data = []
164 175 for user_gr in auth_user_group_list:
165 user_groups_data.append({
176 row = {
166 177 "users_group_name": user_group_name(user_gr.users_group_name),
167 178 "name_raw": h.escape(user_gr.users_group_name),
168 179 "description": h.escape(user_gr.user_group_description),
169 180 "members": user_gr.member_count,
170 181 # NOTE(marcink): because of advanced query we
171 182 # need to load it like that
172 183 "sync": UserGroup._load_sync(
173 184 UserGroup._load_group_data(user_gr.group_data)),
174 185 "active": h.bool2icon(user_gr.users_group_active),
175 186 "owner": user_profile(user_gr.User.username),
176 187 "action": user_group_actions(
177 188 user_gr.users_group_id, user_gr.users_group_name)
178 })
189 }
190 user_groups_data.append(row)
179 191
180 192 data = ({
181 193 'draw': draw,
182 194 'data': user_groups_data,
183 195 'recordsTotal': user_groups_data_total_count,
184 196 'recordsTotalInactive': user_groups_data_total_inactive_count,
185 197 'recordsFiltered': user_groups_data_total_filtered_count,
186 198 'recordsFilteredInactive': user_groups_data_total_filtered_inactive_count,
187 199 })
188 200
189 201 return data
190 202
191 203 @LoginRequired()
192 204 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
193 205 @view_config(
194 206 route_name='user_groups_new', request_method='GET',
195 207 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
196 208 def user_groups_new(self):
197 209 c = self.load_default_context()
198 210 return self._get_template_context(c)
199 211
200 212 @LoginRequired()
201 213 @HasPermissionAnyDecorator('hg.admin', 'hg.usergroup.create.true')
202 214 @CSRFRequired()
203 215 @view_config(
204 216 route_name='user_groups_create', request_method='POST',
205 217 renderer='rhodecode:templates/admin/user_groups/user_group_add.mako')
206 218 def user_groups_create(self):
207 219 _ = self.request.translate
208 220 c = self.load_default_context()
209 221 users_group_form = UserGroupForm(self.request.translate)()
210 222
211 223 user_group_name = self.request.POST.get('users_group_name')
212 224 try:
213 225 form_result = users_group_form.to_python(dict(self.request.POST))
214 226 user_group = UserGroupModel().create(
215 227 name=form_result['users_group_name'],
216 228 description=form_result['user_group_description'],
217 229 owner=self._rhodecode_user.user_id,
218 230 active=form_result['users_group_active'])
219 231 Session().flush()
220 232 creation_data = user_group.get_api_data()
221 233 user_group_name = form_result['users_group_name']
222 234
223 235 audit_logger.store_web(
224 236 'user_group.create', action_data={'data': creation_data},
225 237 user=self._rhodecode_user)
226 238
227 239 user_group_link = h.link_to(
228 240 h.escape(user_group_name),
229 241 h.route_path(
230 242 'edit_user_group', user_group_id=user_group.users_group_id))
231 243 h.flash(h.literal(_('Created user group %(user_group_link)s')
232 244 % {'user_group_link': user_group_link}),
233 245 category='success')
234 246 Session().commit()
235 247 user_group_id = user_group.users_group_id
236 248 except formencode.Invalid as errors:
237 249
238 250 data = render(
239 251 'rhodecode:templates/admin/user_groups/user_group_add.mako',
240 252 self._get_template_context(c), self.request)
241 253 html = formencode.htmlfill.render(
242 254 data,
243 255 defaults=errors.value,
244 256 errors=errors.error_dict or {},
245 257 prefix_error=False,
246 258 encoding="UTF-8",
247 259 force_defaults=False
248 260 )
249 261 return Response(html)
250 262
251 263 except Exception:
252 264 log.exception("Exception creating user group")
253 265 h.flash(_('Error occurred during creation of user group %s') \
254 266 % user_group_name, category='error')
255 267 raise HTTPFound(h.route_path('user_groups_new'))
256 268
257 269 events.trigger(events.UserPermissionsChange([self._rhodecode_user.user_id]))
258 270 raise HTTPFound(
259 271 h.route_path('edit_user_group', user_group_id=user_group_id))
@@ -1,115 +1,115 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.mako"/>
3 3
4 4 <%def name="title()">
5 5 ${_('User groups administration')}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}
8 8 %endif
9 9 </%def>
10 10
11 11 <%def name="breadcrumbs_links()">
12 12 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
13 13 ${h.link_to(_('Admin'),h.route_path('admin_home'))} &raquo; <span id="user_group_count">0</span>
14 14 </%def>
15 15
16 16 <%def name="menu_bar_nav()">
17 17 ${self.menu_items(active='admin')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box">
22 22
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 <ul class="links">
26 26 %if h.HasPermissionAny('hg.admin', 'hg.usergroup.create.true')():
27 27 <li>
28 28 <a href="${h.route_path('user_groups_new')}" class="btn btn-small btn-success">${_(u'Add User Group')}</a>
29 29 </li>
30 30 %endif
31 31 </ul>
32 32 </div>
33 33
34 34 <div id="repos_list_wrap">
35 35 <table id="user_group_list_table" class="display"></table>
36 36 </div>
37 37
38 38 </div>
39 39 <script>
40 40 $(document).ready(function() {
41 41 var $userGroupsListTable = $('#user_group_list_table');
42 42
43 43 // user list
44 44 $userGroupsListTable.DataTable({
45 45 processing: true,
46 46 serverSide: true,
47 47 ajax: {
48 48 "url": "${h.route_path('user_groups_data')}",
49 49 "dataSrc": function (json) {
50 50 var filteredCount = json.recordsFiltered;
51 51 var filteredInactiveCount = json.recordsFilteredInactive;
52 52 var totalInactive = json.recordsTotalInactive;
53 53 var total = json.recordsTotal;
54 54
55 55 var _text = _gettext(
56 56 "{0} ({1} inactive) of {2} user groups ({3} inactive)").format(
57 57 filteredCount, filteredInactiveCount, total, totalInactive);
58 58
59 59 if (total === filteredCount) {
60 60 _text = _gettext(
61 61 "{0} user groups ({1} inactive)").format(total, totalInactive);
62 62 }
63 63 $('#user_group_count').text(_text);
64 64 return json.data;
65 65 },
66 66 },
67 67
68 68 dom: 'rtp',
69 69 pageLength: ${c.visual.admin_grid_items},
70 70 order: [[ 0, "asc" ]],
71 71 columns: [
72 72 { data: {"_": "users_group_name",
73 73 "sort": "users_group_name"}, title: "${_('Name')}", className: "td-componentname" },
74 74 { data: {"_": "description",
75 75 "sort": "description"}, title: "${_('Description')}", className: "td-description" },
76 76 { data: {"_": "members",
77 77 "sort": "members"}, title: "${_('Members')}", className: "td-number" },
78 78 { data: {"_": "sync",
79 79 "sort": "sync"}, title: "${_('Sync')}", className: "td-sync" },
80 80 { data: {"_": "active",
81 81 "sort": "active"}, title: "${_('Active')}", className: "td-active" },
82 82 { data: {"_": "owner",
83 83 "sort": "owner"}, title: "${_('Owner')}", className: "td-user" },
84 84 { data: {"_": "action",
85 85 "sort": "action"}, title: "${_('Action')}", className: "td-action", orderable: false}
86 86 ],
87 87 language: {
88 88 paginate: DEFAULT_GRID_PAGINATION,
89 89 sProcessing: _gettext('loading...'),
90 90 emptyTable: _gettext("No user groups available yet.")
91 91 }
92 92 });
93 93
94 94 $userGroupsListTable.on('xhr.dt', function(e, settings, json, xhr){
95 95 $userGroupsListTable.css('opacity', 1);
96 96 });
97 97
98 98 $userGroupsListTable.on('preXhr.dt', function(e, settings, data){
99 99 $userGroupsListTable.css('opacity', 0.3);
100 100 });
101 101
102 102 // filter
103 103 $('#q_filter').on('keyup',
104 104 $.debounce(250, function() {
105 $('#user_group_list_table').DataTable().search(
105 $userGroupsListTable.DataTable().search(
106 106 $('#q_filter').val()
107 107 ).draw();
108 108 })
109 109 );
110 110
111 111 });
112 112
113 113 </script>
114 114
115 115 </%def>
General Comments 0
You need to be logged in to leave comments. Login now