| @@ -0,0 +1,69 @@ | ||
| 1 | +# -*- coding: utf-8 -*- | |
| 2 | +from __future__ import unicode_literals | |
| 3 | + | |
| 4 | +from django.db import models, migrations | |
| 5 | + | |
| 6 | + | |
| 7 | +class Migration(migrations.Migration): | |
| 8 | + | |
| 9 | + dependencies = [ | |
| 10 | +        ('account', '0005_auto_20151209_1638'), | |
| 11 | + ] | |
| 12 | + | |
| 13 | + operations = [ | |
| 14 | + migrations.AddField( | |
| 15 | + model_name='userinfo', | |
| 16 | + name='assign_at', | |
| 17 | + field=models.DateTimeField(help_text='\u5206\u914d\u65f6\u95f4', null=True, verbose_name='assign_at', blank=True), | |
| 18 | + ), | |
| 19 | + migrations.AddField( | |
| 20 | + model_name='userinfo', | |
| 21 | + name='assign_ip', | |
| 22 | + field=models.CharField(help_text='\u5206\u914dIP', max_length=255, null=True, verbose_name='assign_ip', blank=True), | |
| 23 | + ), | |
| 24 | + migrations.AddField( | |
| 25 | + model_name='userinfo', | |
| 26 | + name='avatar', | |
| 27 | + field=models.CharField(help_text='\u7528\u6237\u5934\u50cf', max_length=255, null=True, verbose_name='avatar', blank=True), | |
| 28 | + ), | |
| 29 | + migrations.AddField( | |
| 30 | + model_name='userinfo', | |
| 31 | + name='city', | |
| 32 | + field=models.CharField(help_text='\u7528\u6237\u57ce\u5e02', max_length=255, null=True, verbose_name='city', blank=True), | |
| 33 | + ), | |
| 34 | + migrations.AddField( | |
| 35 | + model_name='userinfo', | |
| 36 | + name='country', | |
| 37 | + field=models.CharField(help_text='\u7528\u6237\u56fd\u5bb6', max_length=255, null=True, verbose_name='country', blank=True), | |
| 38 | + ), | |
| 39 | + migrations.AddField( | |
| 40 | + model_name='userinfo', | |
| 41 | + name='nickname', | |
| 42 | + field=models.CharField(help_text='\u7528\u6237\u6635\u79f0', max_length=255, null=True, verbose_name='nickname', blank=True), | |
| 43 | + ), | |
| 44 | + migrations.AddField( | |
| 45 | + model_name='userinfo', | |
| 46 | + name='province', | |
| 47 | + field=models.CharField(help_text='\u7528\u6237\u7701\u4efd', max_length=255, null=True, verbose_name='province', blank=True), | |
| 48 | + ), | |
| 49 | + migrations.AddField( | |
| 50 | + model_name='userinfo', | |
| 51 | + name='signup_at', | |
| 52 | + field=models.DateTimeField(help_text='\u6ce8\u518c\u65f6\u95f4', null=True, verbose_name='signup_at', blank=True), | |
| 53 | + ), | |
| 54 | + migrations.AddField( | |
| 55 | + model_name='userinfo', | |
| 56 | + name='user_from', | |
| 57 | + field=models.IntegerField(default=0, help_text='\u7528\u6237\u6765\u6e90', verbose_name='user_from', choices=[(0, 'APP \u521b\u5efa\u7528\u6237'), (1, '\u5fae\u4fe1\u6388\u6743\u7528\u6237')]), | |
| 58 | + ), | |
| 59 | + migrations.AddField( | |
| 60 | + model_name='userinfo', | |
| 61 | + name='wx_uid', | |
| 62 | + field=models.CharField(null=True, max_length=255, blank=True, help_text='\u5fae\u4fe1\u552f\u4e00\u6807\u8bc6', unique=True, verbose_name='wx_uid', db_index=True), | |
| 63 | + ), | |
| 64 | + migrations.AlterField( | |
| 65 | + model_name='userinfo', | |
| 66 | + name='user_status', | |
| 67 | + field=models.IntegerField(default=0, verbose_name='user_status', choices=[(0, '\u672a\u9a8c\u8bc1'), (1, '\u5df2\u6fc0\u6d3b'), (2, '\u5df2\u7981\u7528'), (3, '\u5df2\u5220\u9664'), (10, '\u5df2\u5206\u914d')]), | |
| 68 | + ), | |
| 69 | + ] | 
| @@ -64,6 +64,14 @@ class LensmanLoginLogInfo(CreateUpdateMixin): | ||
| 64 | 64 |  | 
| 65 | 65 |  | 
| 66 | 66 | class UserInfo(CreateUpdateMixin): | 
| 67 | + APP_USER = 0 | |
| 68 | + WX_USER = 1 | |
| 69 | + | |
| 70 | + USER_FROM = ( | |
| 71 | + (APP_USER, u'APP 创建用户'), | |
| 72 | + (WX_USER, u'微信授权用户'), | |
| 73 | + ) | |
| 74 | + | |
| 67 | 75 | UNVERIFIED = 0 | 
| 68 | 76 | ACTIVATED = 1 | 
| 69 | 77 | DISABLED = 2 | 
| @@ -88,17 +96,31 @@ class UserInfo(CreateUpdateMixin): | ||
| 88 | 96 |  | 
| 89 | 97 | user_id = models.CharField(_(u'user_id'), max_length=255, blank=True, null=True, help_text=u'用户唯一标识', db_index=True, unique=True) | 
| 90 | 98 |  | 
| 99 | + user_from = models.IntegerField(_(u'user_from'), choices=USER_FROM, default=APP_USER, help_text=u'用户来源') | |
| 100 | + # APP 创建用户 | |
| 91 | 101 | username = models.CharField(_(u'username'), max_length=255, blank=True, null=True, help_text=u'用户用户名', db_index=True, unique=True) | 
| 92 | 102 | password = models.CharField(_(u'password'), max_length=255, blank=True, null=True, help_text=u'用户密码') | 
| 93 | - | |
| 103 | + # 微信授权用户 | |
| 104 | + wx_uid = models.CharField(_(u'wx_uid'), max_length=255, blank=True, null=True, help_text=u'微信唯一标识', db_index=True, unique=True) | |
| 105 | + # 用户基本信息 | |
| 94 | 106 | name = models.CharField(_(u'name'), max_length=255, blank=True, null=True, help_text=u'用户姓名') | 
| 95 | 107 | sex = models.IntegerField(_(u'sex'), choices=SEX_TYPE, default=MALE, help_text=u'用户性别') | 
| 108 | + nickname = models.CharField(_(u'nickname'), max_length=255, blank=True, null=True, help_text=u'用户昵称') | |
| 109 | + avatar = models.CharField(_(u'avatar'), max_length=255, blank=True, null=True, help_text=u'用户头像') | |
| 96 | 110 | phone = models.CharField(_(u'phone'), max_length=255, blank=True, null=True, help_text=u'用户电话', db_index=True, unique=True) | 
| 111 | + country = models.CharField(_(u'country'), max_length=255, blank=True, null=True, help_text=u'用户国家') | |
| 112 | + province = models.CharField(_(u'province'), max_length=255, blank=True, null=True, help_text=u'用户省份') | |
| 113 | + city = models.CharField(_(u'city'), max_length=255, blank=True, null=True, help_text=u'用户城市') | |
| 97 | 114 | location = models.CharField(_(u'location'), max_length=255, blank=True, null=True, help_text=u'用户地址') | 
| 98 | 115 |  | 
| 99 | 116 | user_status = models.IntegerField(_(u'user_status'), choices=USER_STATUS, default=UNVERIFIED) | 
| 100 | 117 |  | 
| 118 | + assign_ip = models.CharField(_(u'assign_ip'), max_length=255, blank=True, null=True, help_text=_(u'分配IP')) | |
| 119 | + assign_at = models.DateTimeField(_(u'assign_at'), blank=True, null=True, help_text=_(u'分配时间')) | |
| 120 | + | |
| 101 | 121 | signup_ip = models.CharField(_(u'signup_ip'), max_length=255, blank=True, null=True, help_text=_(u'注册IP')) | 
| 122 | + signup_at = models.DateTimeField(_(u'signup_at'), blank=True, null=True, help_text=_(u'注册时间')) | |
| 123 | + | |
| 102 | 124 | login_ip = models.CharField(_(u'login_ip'), max_length=255, blank=True, null=True, help_text=_(u'登录IP')) | 
| 103 | 125 | login_at = models.DateTimeField(_(u'login_at'), blank=True, null=True, help_text=_(u'登录时间')) | 
| 104 | 126 |  | 
| @@ -109,6 +131,13 @@ class UserInfo(CreateUpdateMixin): | ||
| 109 | 131 | def __unicode__(self): | 
| 110 | 132 | return unicode(self.pk) | 
| 111 | 133 |  | 
| 134 | + @property | |
| 135 | + def final_nickname(self): | |
| 136 | + if self.user_from == self.APP_USER: | |
| 137 | + return self.username | |
| 138 | + elif self.user_from == self.WX_USER: | |
| 139 | + return self.nickname | |
| 140 | + | |
| 112 | 141 | def _data(self): | 
| 113 | 142 |          return { | 
| 114 | 143 | 'user_id': self.user_id, | 
| @@ -55,6 +55,7 @@ def user_is_registered_api(request): | ||
| 55 | 55 |  | 
| 56 | 56 |  | 
| 57 | 57 | def user_signup_api(request): | 
| 58 | +    user_id = request.POST.get('user_id', '') | |
| 58 | 59 |      username = request.POST.get('username', '') | 
| 59 | 60 |      password = request.POST.get('password', '') | 
| 60 | 61 |  | 
| @@ -64,13 +65,32 @@ def user_signup_api(request): | ||
| 64 | 65 | 'message': u'该用户名已注册', | 
| 65 | 66 | }) | 
| 66 | 67 |  | 
| 67 | - user = UserInfo.objects.create( | |
| 68 | - user_id=CurtailUUID.uuid(UserInfo, 'user_id'), | |
| 69 | - username=username, | |
| 70 | - password=make_password(password, None, 'pbkdf2_sha256'), | |
| 71 | - user_status=UserInfo.ACTIVATED, | |
| 72 | - signup_ip=ip_addr(request), | |
| 73 | - ) | |
| 68 | + # 判断 user_id 是否存在并且为分配用户,如果存在并且为分配用户,则直接在该帐户上更新,否则则直接创建帐户 | |
| 69 | + signup_ip, signup_at = ip_addr(request), tc.utc_datetime() | |
| 70 | + | |
| 71 | + try: | |
| 72 | + user = UserInfo.objects.get(user_id=user_id) | |
| 73 | + except UserInfo.DoesNotExist: | |
| 74 | + user = None | |
| 75 | + | |
| 76 | + if user and user.user_status == UserInfo.ASSIGN: | |
| 77 | + user.user_from = UserInfo.APP_USER, | |
| 78 | + user.username = username | |
| 79 | + user.password = make_password(password, None, 'pbkdf2_sha256') | |
| 80 | + user.user_status = UserInfo.ACTIVATED | |
| 81 | + user.signup_ip = signup_ip | |
| 82 | + user.signup_at = signup_at | |
| 83 | + user.save() | |
| 84 | + else: | |
| 85 | + user = UserInfo.objects.create( | |
| 86 | + user_id=CurtailUUID.uuid(UserInfo, 'user_id'), | |
| 87 | + user_from=UserInfo.APP_USER, | |
| 88 | + username=username, | |
| 89 | + password=make_password(password, None, 'pbkdf2_sha256'), | |
| 90 | + user_status=UserInfo.ACTIVATED, | |
| 91 | + signup_ip=signup_ip, | |
| 92 | + signup_at=signup_at, | |
| 93 | + ) | |
| 74 | 94 |  | 
| 75 | 95 |      return JsonResponse({ | 
| 76 | 96 | 'status': 200, | 
| @@ -121,6 +141,73 @@ def user_login_api(request): | ||
| 121 | 141 | }) | 
| 122 | 142 |  | 
| 123 | 143 |  | 
| 144 | +def wx_authorize_api(request): | |
| 145 | +    user_id = request.POST.get('user_id', '') | |
| 146 | +    wx_uid = request.POST.get('wx_uid', '') | |
| 147 | + | |
| 148 | + # 判断 wx_uid 是否已经存在,如果已经存在,则直接返回改帐户信息 | |
| 149 | + try: | |
| 150 | + user = UserInfo.objects.get(wx_uid=wx_uid) | |
| 151 | + except UserInfo.DoesNotExist: | |
| 152 | + user = None | |
| 153 | + | |
| 154 | + if user: | |
| 155 | +        return JsonResponse({ | |
| 156 | + 'status': 200, | |
| 157 | + 'message': u'登录成功', | |
| 158 | + 'data': user.data, | |
| 159 | + }) | |
| 160 | + | |
| 161 | + # wx_uid 不存在 | |
| 162 | + # 判断 user_id 是否存在并且为分配用户,如果存在并且为分配用户,则直接在该帐户上更新,否则则直接创建帐户 | |
| 163 | +    sex = request.POST.get('sex', 0) | |
| 164 | +    nickname = request.POST.get('nickname', '') or request.POST.get('screen_name', '') | |
| 165 | +    avatar = request.POST.get('headimgurl', '') or request.POST.get('profile_image_url', '') | |
| 166 | +    country = request.POST.get('country', '') | |
| 167 | +    province = request.POST.get('province', '') | |
| 168 | +    city = request.POST.get('city', '') | |
| 169 | + | |
| 170 | + signup_ip, signup_at = ip_addr(request), tc.utc_datetime() | |
| 171 | + | |
| 172 | + try: | |
| 173 | + user = UserInfo.objects.get(user_id=user_id) | |
| 174 | + except UserInfo.DoesNotExist: | |
| 175 | + user = None | |
| 176 | + | |
| 177 | + if user and user.user_status == UserInfo.ASSIGN: | |
| 178 | + user.user_from = UserInfo.WX_USER | |
| 179 | + user.wx_uid = wx_uid | |
| 180 | + user.sex = sex | |
| 181 | + user.nickname = nickname | |
| 182 | + user.avatar = avatar | |
| 183 | + user.country = country | |
| 184 | + user.province = province | |
| 185 | + user.city = city | |
| 186 | + user.signup_ip = signup_ip | |
| 187 | + user.signup_at = signup_at | |
| 188 | + user.save() | |
| 189 | + else: | |
| 190 | + user = UserInfo.objects.create( | |
| 191 | + user_id=CurtailUUID.uuid(UserInfo, 'user_id'), | |
| 192 | + user_from=UserInfo.WX_USER, | |
| 193 | + sex=sex, | |
| 194 | + nickname=nickname, | |
| 195 | + avatar=avatar, | |
| 196 | + country=country, | |
| 197 | + province=province, | |
| 198 | + city=city, | |
| 199 | + user_status=UserInfo.ACTIVATED, | |
| 200 | + signup_ip=signup_ip, | |
| 201 | + signup_at=signup_at, | |
| 202 | + ) | |
| 203 | + | |
| 204 | +    return JsonResponse({ | |
| 205 | + 'status': 200, | |
| 206 | + 'message': u'登录成功', | |
| 207 | + 'data': user.data, | |
| 208 | + }) | |
| 209 | + | |
| 210 | + | |
| 124 | 211 | class UserViewSet(viewsets.ModelViewSet): | 
| 125 | 212 | """ | 
| 126 | 213 | API endpoint that allows users to be viewed or edited. | 
| @@ -8,10 +8,12 @@ from photo import views as photo_views | ||
| 8 | 8 |  | 
| 9 | 9 |  | 
| 10 | 10 | urlpatterns = [ | 
| 11 | - url(r'^login$', account_views.lesman_login_api, name='lesman_login_api'), | |
| 11 | + url(r'^login$', account_views.lesman_login_api, name='lesman_login_api'), # 摄影师登录 | |
| 12 | 12 | url(r'^u/is_registered$', account_views.user_is_registered_api, name='user_is_registered_api'), # 用户是否已经注册 | 
| 13 | 13 | url(r'^u/signup$', account_views.user_signup_api, name='user_signup_api'), # 用户注册 | 
| 14 | 14 | url(r'^u/login$', account_views.user_login_api, name='user_login_api'), # 用户登录 | 
| 15 | + | |
| 16 | + url(r'^u/wx/authorize$', account_views.wx_authorize_api, name='wx_authorize_api'), # 用户登录 | |
| 15 | 17 | ] | 
| 16 | 18 |  | 
| 17 | 19 | urlpatterns += [ | 
| @@ -43,7 +43,7 @@ def group_create_api(request): | ||
| 43 | 43 | GroupUserInfo.objects.create( | 
| 44 | 44 | group_id=group_id, | 
| 45 | 45 | user_id=user_id, | 
| 46 | - nickname=user.username, | |
| 46 | + nickname=user.final_nickname, | |
| 47 | 47 | admin=True, | 
| 48 | 48 | user_status=GroupUserInfo.PASSED, | 
| 49 | 49 | passed_at=tc.utc_datetime(), | 
| @@ -172,7 +172,7 @@ def group_join_api(request): | ||
| 172 | 172 | group_id=group_id, | 
| 173 | 173 | user_id=user_id, | 
| 174 | 174 | current_id=group_photo and group_photo.id or -1, | 
| 175 | - nickname=nickname or user.username, | |
| 175 | + nickname=nickname or user.final_nickname, | |
| 176 | 176 | admin=False, | 
| 177 | 177 | user_status=GroupUserInfo.PASSED, | 
| 178 | 178 | passed_at=tc.utc_datetime(), | 
| @@ -123,50 +123,94 @@ def upload_photo(request): | ||
| 123 | 123 | def session_detail_api(request, session): | 
| 124 | 124 |      user_id = request.POST.get('user_id', '') | 
| 125 | 125 |  | 
| 126 | - try: | |
| 127 | - user = UserInfo.objects.get(user_id=user_id) | |
| 128 | - user_id = user.user_id | |
| 129 | - except UserInfo.DoesNotExist: | |
| 130 | - user_id = CurtailUUID.uuid(UserInfo, 'user_id') | |
| 131 | - user = UserInfo.objects.create( | |
| 132 | - user_id=user_id, | |
| 133 | - user_status=UserInfo.ASSIGN, | |
| 134 | - signup_ip=ip_addr(request), | |
| 135 | - ) | |
| 126 | + # 判断 user_id 是否存在,如果不存在,则直接分配帐户 | |
| 127 | +    user, created = UserInfo.objects.get_or_create(user_id=user_id, defaults={ | |
| 128 | + 'user_id': CurtailUUID.uuid(UserInfo, 'user_id'), | |
| 129 | + 'user_status': UserInfo.ASSIGN, | |
| 130 | + 'assign_ip': ip_addr(request), | |
| 131 | + 'assign_at': tc.utc_datetime(), | |
| 132 | + }) | |
| 133 | + # try: | |
| 134 | + # user = UserInfo.objects.get(user_id=user_id) | |
| 135 | + # except UserInfo.DoesNotExist: | |
| 136 | + # user = None | |
| 137 | + # | |
| 138 | + # if not user: | |
| 139 | + # assign_ip, assign_at = ip_addr(request), tc.utc_datetime() | |
| 140 | + # user = UserInfo.objects.create( | |
| 141 | + # user_id=CurtailUUID.uuid(UserInfo, 'user_id'), | |
| 142 | + # user_status=UserInfo.ASSIGN, | |
| 143 | + # assign_ip=assign_ip, | |
| 144 | + # assign_at=assign_at, | |
| 145 | + # ) | |
| 146 | + | |
| 147 | + user_id = user.user_id | |
| 148 | + | |
| 149 | + # 判断通过 session_id 创建的群组是否存在,如果不存在,则直接创建 | |
| 150 | +    group, created = GroupInfo.objects.get_or_create(session_id=session, defaults={ | |
| 151 | + 'group_id': CurtailUUID.uuid(GroupInfo, 'group_id'), | |
| 152 | + 'admin_id': user_id, | |
| 153 | + 'group_from': GroupInfo.SESSION_GROUP, | |
| 154 | + 'session_id': session, | |
| 155 | + }) | |
| 156 | + # try: | |
| 157 | + # group = GroupInfo.objects.get(session_id=session) | |
| 158 | + # except GroupInfo.DoesNotExist: | |
| 159 | + # group = None | |
| 160 | + # | |
| 161 | + # if not group: | |
| 162 | + # group = GroupInfo.objects.create( | |
| 163 | + # group_id=CurtailUUID.uuid(GroupInfo, 'group_id'), | |
| 164 | + # admin_id=user_id, | |
| 165 | + # group_from=GroupInfo.SESSION_GROUP, | |
| 166 | + # session_id=session, | |
| 167 | + # ) | |
| 168 | + | |
| 169 | + group_id = group.group_id | |
| 170 | + | |
| 171 | + # 判断 group_id/user_id 的群组用户是否存在,如果不存在,则直接创建 | |
| 172 | + group_photo = GroupPhotoInfo.objects.filter(group_id=group_id).last() | |
| 173 | +    group_user = GroupUserInfo.objects.get_or_create(group_id=group_id, user_id=user_id, defaults={ | |
| 174 | + 'current_id': group_photo and group_photo.id or -1, | |
| 175 | + 'nickname': user.final_nickname, | |
| 176 | + 'admin': False, | |
| 177 | + 'user_status': GroupUserInfo.PASSED, | |
| 178 | + 'passed_at': tc.utc_datetime(), | |
| 179 | + }) | |
| 136 | 180 |  | 
| 137 | - try: | |
| 138 | - group = GroupInfo.objects.get(session_id=session) | |
| 139 | - group_id = group.group_id | |
| 140 | - group_photo = GroupPhotoInfo.objects.filter(group_id=group_id).last() | |
| 141 | - if not GroupUserInfo.objects.filter( | |
| 142 | - group_id=group_id, | |
| 143 | - user_id=user_id | |
| 144 | - ).exists(): | |
| 145 | - GroupUserInfo.objects.create( | |
| 146 | - group_id=group_id, | |
| 147 | - user_id=user_id, | |
| 148 | - current_id=group_photo and group_photo.id or -1, | |
| 149 | - nickname=user.username, | |
| 150 | - admin=False, | |
| 151 | - user_status=GroupUserInfo.PASSED, | |
| 152 | - passed_at=tc.utc_datetime(), | |
| 153 | - ) | |
| 154 | - except GroupInfo.DoesNotExist: | |
| 155 | - group_id = CurtailUUID.uuid(GroupInfo, 'group_id') | |
| 156 | - group = GroupInfo.objects.create( | |
| 157 | - group_id=group_id, | |
| 158 | - admin_id=user_id, | |
| 159 | - group_from=GroupInfo.SESSION_GROUP, | |
| 160 | - session_id=session, | |
| 161 | - ) | |
| 162 | - GroupUserInfo.objects.create( | |
| 163 | - group_id=group_id, | |
| 164 | - user_id=user_id, | |
| 165 | - nickname=user.username, | |
| 166 | - admin=True, | |
| 167 | - user_status=GroupUserInfo.PASSED, | |
| 168 | - passed_at=tc.utc_datetime(), | |
| 169 | - ) | |
| 181 | + # try: | |
| 182 | + # group = GroupInfo.objects.get(session_id=session) | |
| 183 | + # group_id = group.group_id | |
| 184 | + # group_photo = GroupPhotoInfo.objects.filter(group_id=group_id).last() | |
| 185 | + # if not GroupUserInfo.objects.filter( | |
| 186 | + # group_id=group_id, | |
| 187 | + # user_id=user_id | |
| 188 | + # ).exists(): | |
| 189 | + # GroupUserInfo.objects.create( | |
| 190 | + # group_id=group_id, | |
| 191 | + # user_id=user_id, | |
| 192 | + # current_id=group_photo and group_photo.id or -1, | |
| 193 | + # nickname=user.username, | |
| 194 | + # admin=False, | |
| 195 | + # user_status=GroupUserInfo.PASSED, | |
| 196 | + # passed_at=tc.utc_datetime(), | |
| 197 | + # ) | |
| 198 | + # except GroupInfo.DoesNotExist: | |
| 199 | + # group_id = CurtailUUID.uuid(GroupInfo, 'group_id') | |
| 200 | + # group = GroupInfo.objects.create( | |
| 201 | + # group_id=group_id, | |
| 202 | + # admin_id=user_id, | |
| 203 | + # group_from=GroupInfo.SESSION_GROUP, | |
| 204 | + # session_id=session, | |
| 205 | + # ) | |
| 206 | + # GroupUserInfo.objects.create( | |
| 207 | + # group_id=group_id, | |
| 208 | + # user_id=user_id, | |
| 209 | + # nickname=user.username, | |
| 210 | + # admin=True, | |
| 211 | + # user_status=GroupUserInfo.PASSED, | |
| 212 | + # passed_at=tc.utc_datetime(), | |
| 213 | + # ) | |
| 170 | 214 |  | 
| 171 | 215 | photos = PhotosInfo.objects.filter(session_id=session) | 
| 172 | 216 |      return JsonResponse({ |