Coverage for .tox / coverage / lib / python3.11 / site-packages / wuttaweb / views / people.py: 100%

84 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-03-20 21:14 -0500

1# -*- coding: utf-8; -*- 

2################################################################################ 

3# 

4# wuttaweb -- Web App for Wutta Framework 

5# Copyright © 2024-2026 Lance Edgar 

6# 

7# This file is part of Wutta Framework. 

8# 

9# Wutta Framework is free software: you can redistribute it and/or modify it 

10# under the terms of the GNU General Public License as published by the Free 

11# Software Foundation, either version 3 of the License, or (at your option) any 

12# later version. 

13# 

14# Wutta Framework is distributed in the hope that it will be useful, but 

15# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 

16# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 

17# more details. 

18# 

19# You should have received a copy of the GNU General Public License along with 

20# Wutta Framework. If not, see <http://www.gnu.org/licenses/>. 

21# 

22################################################################################ 

23""" 

24Views for people 

25""" 

26 

27import sqlalchemy as sa 

28 

29from wuttjamaican.db.model import Person 

30from wuttaweb.views import MasterView 

31from wuttaweb.util import make_users_grid 

32 

33 

34class PersonView(MasterView): # pylint: disable=abstract-method 

35 """ 

36 Master view for people. 

37 

38 Default route prefix is ``people``. 

39 

40 Notable URLs provided by this class: 

41 

42 * ``/people/`` 

43 * ``/people/new`` 

44 * ``/people/XXX`` 

45 * ``/people/XXX/edit`` 

46 * ``/people/XXX/delete`` 

47 """ 

48 

49 model_class = Person 

50 model_title_plural = "People" 

51 route_prefix = "people" 

52 sort_defaults = "full_name" 

53 has_autocomplete = True 

54 

55 grid_columns = [ 

56 "full_name", 

57 "first_name", 

58 "middle_name", 

59 "last_name", 

60 ] 

61 

62 filter_defaults = { 

63 "full_name": {"active": True}, 

64 } 

65 

66 form_fields = [ 

67 "full_name", 

68 "first_name", 

69 "middle_name", 

70 "last_name", 

71 "users", 

72 ] 

73 

74 mergeable = True 

75 merge_additive_fields = ["usernames"] 

76 

77 def configure_grid(self, grid): # pylint: disable=empty-docstring 

78 """ """ 

79 g = grid 

80 super().configure_grid(g) 

81 

82 # full_name 

83 g.set_link("full_name") 

84 

85 # first_name 

86 g.set_link("first_name") 

87 

88 # last_name 

89 g.set_link("last_name") 

90 

91 def configure_form(self, form): # pylint: disable=empty-docstring 

92 """ """ 

93 f = form 

94 super().configure_form(f) 

95 person = f.model_instance 

96 

97 # full_name 

98 if self.creating or self.editing: 

99 f.remove("full_name") 

100 

101 # users 

102 if self.creating or self.editing: 

103 f.remove("users") 

104 else: 

105 f.set_grid("users", self.make_users_grid(person)) 

106 

107 def make_users_grid(self, person): 

108 """ 

109 Make and return the grid for the Users field. 

110 

111 This grid is shown for the Users field when viewing a Person. 

112 

113 :returns: Fully configured :class:`~wuttaweb.grids.base.Grid` 

114 instance. 

115 """ 

116 return make_users_grid( 

117 self.request, 

118 route_prefix=self.get_route_prefix(), 

119 data=person.users, 

120 columns=[ 

121 "username", 

122 "active", 

123 ], 

124 ) 

125 

126 def objectify(self, form): # pylint: disable=empty-docstring 

127 """ """ 

128 person = super().objectify(form) 

129 

130 # full_name 

131 person.full_name = self.app.make_full_name(person.first_name, person.last_name) 

132 

133 return person 

134 

135 def merge_get_data(self, obj): # pylint: disable=empty-docstring 

136 """ """ 

137 person = obj 

138 data = super().merge_get_data(person) 

139 

140 data["usernames"] = sorted([u.username for u in person.users]) 

141 

142 return data 

143 

144 def merge_execute(self, removing, keeping): 

145 """ 

146 We override default merge logic to re-assign users if needed. 

147 

148 See also parent method: 

149 :meth:`~wuttaweb.views.master.MasterView.merge_execute()` 

150 """ 

151 session = self.Session() 

152 

153 # reassign users 

154 for user in list(removing.users): 

155 user.person = keeping 

156 session.flush() 

157 

158 # continue default merge 

159 super().merge_execute(removing, keeping) 

160 

161 def autocomplete_query(self, term): # pylint: disable=empty-docstring 

162 """ """ 

163 model = self.app.model 

164 session = self.Session() 

165 query = session.query(model.Person) 

166 criteria = [model.Person.full_name.ilike(f"%{word}%") for word in term.split()] 

167 query = query.filter(sa.and_(*criteria)).order_by(model.Person.full_name) 

168 return query 

169 

170 def view_profile(self, session=None): # pylint: disable=empty-docstring 

171 """ """ 

172 person = self.get_instance(session=session) 

173 context = { 

174 "person": person, 

175 "instance": person, 

176 } 

177 return self.render_to_response("view_profile", context) 

178 

179 def make_user(self): # pylint: disable=empty-docstring 

180 """ """ 

181 self.request.session.flash("TODO: this feature is not yet supported", "error") 

182 return self.redirect(self.request.get_referrer()) 

183 

184 @classmethod 

185 def defaults(cls, config): # pylint: disable=empty-docstring 

186 """ """ 

187 

188 # nb. Person may come from custom model 

189 wutta_config = config.registry.settings["wutta_config"] 

190 app = wutta_config.get_app() 

191 cls.model_class = app.model.Person 

192 

193 cls._defaults(config) 

194 cls._people_defaults(config) 

195 

196 @classmethod 

197 def _people_defaults(cls, config): 

198 route_prefix = cls.get_route_prefix() 

199 url_prefix = cls.get_url_prefix() 

200 instance_url_prefix = cls.get_instance_url_prefix() 

201 permission_prefix = cls.get_permission_prefix() 

202 

203 # view profile 

204 config.add_route( 

205 f"{route_prefix}.view_profile", 

206 f"{instance_url_prefix}/profile", 

207 request_method="GET", 

208 ) 

209 config.add_view( 

210 cls, 

211 attr="view_profile", 

212 route_name=f"{route_prefix}.view_profile", 

213 permission=f"{permission_prefix}.view_profile", 

214 ) 

215 

216 # make user for person 

217 config.add_route( 

218 f"{route_prefix}.make_user", 

219 f"{url_prefix}/make-user", 

220 request_method="POST", 

221 ) 

222 config.add_view( 

223 cls, 

224 attr="make_user", 

225 route_name=f"{route_prefix}.make_user", 

226 permission="users.create", 

227 ) 

228 

229 

230def defaults(config, **kwargs): # pylint: disable=missing-function-docstring 

231 base = globals() 

232 

233 PersonView = kwargs.get( # pylint: disable=invalid-name,redefined-outer-name 

234 "PersonView", base["PersonView"] 

235 ) 

236 PersonView.defaults(config) 

237 

238 

239def includeme(config): # pylint: disable=missing-function-docstring 

240 defaults(config)