Compare commits

...
Author SHA1 Message Date
abutalib-kiwi 2216411fd2 Merge branch 'dev' into ZBKADM-72-minor-changes 2023-08-21 14:33:36 +05:30
abutalib-kiwi 606c1fa6e6 minor changes in excel import api method 2023-08-21 14:28:06 +05:30
Ankitajain-kiwi 092cbfad3e Merge branch 'qa' into dev 2023-08-21 12:57:57 +05:30
dilipshrivastwa-kiwi 249a469d89 Merge pull request #230 from KiwiTechLLC/sprint5
resolve bugs
2023-08-21 12:54:46 +05:30
jain d202774df1 resolve bugs 2023-08-21 12:39:05 +05:30
jain 1adf0c2f70 resolve bugs 2023-08-21 12:24:01 +05:30
Ankitajain-kiwi b193166914 Merge pull request #229 from KiwiTechLLC/sprint5
login issue
2023-08-20 14:14:45 +05:30
jain 48b455e38a login issue 2023-08-20 14:12:17 +05:30
dilipshrivastwa-kiwi 6d54a718b9 Merge pull request #226 from KiwiTechLLC/ZBKBCK-346
[ZBKBCK-346] change password and forgot password api has been optimised
2023-08-18 20:26:02 +05:30
Ruman Siddiqui 454f66b3a0 conflict has been resolved 2023-08-18 19:50:14 +05:30
dilipshrivastwa-kiwi 5045f5acda Merge pull request #228 from KiwiTechLLC/ZBKADM-72-minor-changes
added description for api, changes in upload image and file to alibab…
2023-08-18 19:01:34 +05:30
abutalib-kiwi e84180a6c2 Merge branch 'dev' into ZBKADM-72-minor-changes 2023-08-18 18:46:47 +05:30
abutalib-kiwi 21e006ae2a added description for api, changes in upload image and file to alibaba method 2023-08-18 18:37:58 +05:30
jain 92e5104e3f expiry date of otp 2023-08-18 18:37:01 +05:30
Ankitajain-kiwi dfb73a7d36 Merge pull request #227 from KiwiTechLLC/sprint5
Sprint5
2023-08-18 18:32:21 +05:30
jain 3921f76f22 remove print statement 2023-08-18 18:31:27 +05:30
jain cdf1a7b74e add comma 2023-08-18 18:29:46 +05:30
jain b4026a4f11 middleware 2023-08-18 18:12:14 +05:30
Ruman Siddiqui 71a3e36bf3 [ZBKBCK-346] change password and forgot password api has been optimised 2023-08-18 16:57:42 +05:30
dilipshrivastwa-kiwi 4bc91abebf Merge pull request #225 from KiwiTechLLC/sprint5
remove threading from login api
2023-08-18 16:40:35 +05:30
jain 5b236dfc81 remove threading from login api 2023-08-18 16:37:42 +05:30
dilipshrivastwa-kiwi 150bb9250d Merge pull request #222 from KiwiTechLLC/sprint5
Sprint5
2023-08-18 16:31:35 +05:30
jain 51d3b77ff7 password validation, deactivated user's middleware 2023-08-18 15:45:57 +05:30
dilipshrivastwa-kiwi 92c67dd455 Merge pull request #224 from KiwiTechLLC/ZBKADM-72-minor-changes
article bug fixed, changed image name in get image url
2023-08-18 15:07:46 +05:30
abutalib-kiwi 9a5447bca2 article bug fixed, changed image name in get image url 2023-08-18 14:52:38 +05:30
dilipshrivastwa-kiwi 392086760f Merge pull request #223 from KiwiTechLLC/ZBKADM-72
csv/excel method changed, uploading to alibaba cloud and providing li…
2023-08-18 13:47:36 +05:30
abutalib-kiwi ceaf584332 csv/excel method changed, uploading to alibaba cloud and providing link to frontend 2023-08-18 11:46:40 +05:30
jain ef4c459229 handle deleted user scenario in complete task api 2023-08-18 11:45:21 +05:30
jain dd2890bca6 handle delete scenerio in approve task and junior 2023-08-18 11:04:05 +05:30
jain b46109e487 FAQ list and creation 2023-08-17 16:06:58 +05:30
jain 3f6c9a2d99 FAQ list and creation 2023-08-17 15:48:26 +05:30
jain f74302df04 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint5 2023-08-17 14:22:33 +05:30
dilipshrivastwa-kiwi 9bdd324c4e Merge pull request #221 from KiwiTechLLC/email-verification-issue
remove print statement for expiry date
2023-08-17 13:28:55 +05:30
jain 04ed9c668c remove print statement for expiry date 2023-08-17 13:24:20 +05:30
dilipshrivastwa-kiwi e1d092d663 Merge pull request #220 from KiwiTechLLC/ZBKADM-72
added required packages in reuirements fil
2023-08-17 13:10:11 +05:30
abutalib-kiwi 4e8243c17e added required packages in reuirements fil 2023-08-17 13:07:59 +05:30
dilipshrivastwa-kiwi 13ba311822 Merge pull request #218 from KiwiTechLLC/ZBKADM-72
csv/excel export api
2023-08-17 12:45:38 +05:30
abutalib-kiwi 043c8c2f63 csv excel export api 2023-08-17 12:30:39 +05:30
dilipshrivastwa-kiwi a5f239b9d9 Merge pull request #219 from KiwiTechLLC/email-verification-issue
print statement for expiry date
2023-08-17 12:29:31 +05:30
jain 36427b50c1 print statement for expiry date 2023-08-17 12:24:33 +05:30
jain 728d19da99 FAQ model 2023-08-17 12:21:13 +05:30
dilipshrivastwa-kiwi 5da07002e0 Merge pull request #217 from KiwiTechLLC/ZBKBCK-47
notification modification added more payload
2023-08-16 16:48:53 +05:30
abutalib-kiwi 3017b0ece0 changes in notification method 2023-08-16 16:40:34 +05:30
abutalib-kiwi e73113fcca Merge branch 'dev' into ZBKBCK-47 2023-08-16 16:39:36 +05:30
Ankitajain-kiwi ed1b5dd453 Merge pull request #215 from KiwiTechLLC/dev
Dev
2023-08-16 16:34:54 +05:30
dilipshrivastwa-kiwi c278670c39 Merge pull request #214 from KiwiTechLLC/sprint5
email verification and user notification bug
2023-08-16 16:28:58 +05:30
jain 60af098e1e email verfication and user notification bug 2023-08-16 16:16:05 +05:30
Ankitajain-kiwi 74328f37b2 Merge pull request #212 from KiwiTechLLC/dev
Dev
2023-08-16 11:38:39 +05:30
Ankitajain-kiwi d29a60558f Merge pull request #211 from KiwiTechLLC/sprint5
user type
2023-08-16 11:38:12 +05:30
jain 451c3bdae7 user type 2023-08-16 11:37:21 +05:30
Ankitajain-kiwi d8f4467d98 Merge pull request #209 from KiwiTechLLC/dev
guardian code status
2023-08-14 18:23:41 +05:30
jain 7c809776b6 guardian code status 2023-08-14 18:22:50 +05:30
Ankitajain-kiwi a9cc05c675 Merge pull request #207 from KiwiTechLLC/dev
Dev
2023-08-14 17:33:56 +05:30
abutalib-kiwi e46d649fdc notification modification added more payload 2023-08-14 16:03:36 +05:30
dilipshrivastwa-kiwi adb827f5a0 Merge pull request #206 from KiwiTechLLC/sprint4
Sprint4
2023-08-14 15:26:27 +05:30
jain a02dfd4e31 login api with user type 2023-08-14 15:23:41 +05:30
jain d3b0be953e login api with user type 2023-08-14 15:16:16 +05:30
jain 11b9f00285 Merge branch 'login_api_reduced_queries' of github.com:KiwiTechLLC/ZODBank-Backend into login_api_reduced_queries 2023-08-14 15:01:43 +05:30
abutalib-kiwi 4bc16c56bd csv/xls report 2023-08-14 13:57:29 +05:30
dilipshrivastwa-kiwi 08dc9f8538 Merge pull request #205 from KiwiTechLLC/sprint4
sonar fixes
2023-08-14 12:20:48 +05:30
jain 6373d1e02c sonar fixes 2023-08-14 12:13:14 +05:30
Ankitajain-kiwi 83d7d119be Merge pull request #203 from KiwiTechLLC/dev
Dev
2023-08-14 11:46:04 +05:30
Ankitajain-kiwi a1d959299a Merge pull request #202 from KiwiTechLLC/sprint4
is complete key
2023-08-14 11:45:37 +05:30
jain 070637bf1d is complete key 2023-08-14 11:43:59 +05:30
Ankitajain-kiwi 19a6475097 Merge pull request #200 from KiwiTechLLC/dev
Dev
2023-08-11 16:55:51 +05:30
Ankitajain-kiwi fd9a4902ae Merge pull request #199 from KiwiTechLLC/sprint4
guardian code chnages
2023-08-11 16:55:21 +05:30
jain 082f93ff9d guardian code chnages 2023-08-11 16:54:27 +05:30
dilipshrivastwa-kiwi 25eaee31a4 Merge pull request #198 from KiwiTechLLC/ZBKADM-71
added pagination in leaderboard admin
2023-08-11 16:02:13 +05:30
abutalib-kiwi d5c30f2029 added pagination in leaderboard admin 2023-08-11 15:56:22 +05:30
Ankitajain-kiwi cae28e0b54 Merge pull request #197 from KiwiTechLLC/sprint4
is complete article key
2023-08-11 15:41:35 +05:30
jain 54200dba52 is complete article key 2023-08-11 15:39:25 +05:30
dilipshrivastwa-kiwi 846a9d42d4 Merge pull request #196 from KiwiTechLLC/ZBKADM-72-sonar-issues
sonar issues
2023-08-11 14:44:25 +05:30
abutalib-kiwi 69c19cf097 sonar issues 2023-08-11 14:40:49 +05:30
dilipshrivastwa-kiwi 18cb885410 Merge pull request #193 from KiwiTechLLC/ZBKADM-71
leaderboard ranking
2023-08-11 14:13:05 +05:30
Ankitajain-kiwi 4ca60af5da Merge pull request #195 from KiwiTechLLC/dev
Dev
2023-08-11 13:50:53 +05:30
Ankitajain-kiwi 4d7209df19 Merge pull request #194 from KiwiTechLLC/sprint4
remove deleted user from leaderboard
2023-08-11 13:40:48 +05:30
jain 94c76b7f2e remove deleted user from leaderboard 2023-08-11 13:39:25 +05:30
abutalib-kiwi af8ea39200 leaderboard ranking 2023-08-11 13:37:44 +05:30
Ankitajain-kiwi 6e9304b2cc Merge pull request #192 from KiwiTechLLC/sprint4
is complete article key
2023-08-11 13:04:54 +05:30
jain 5eab8f4907 is complete article key 2023-08-11 13:04:05 +05:30
Ankitajain-kiwi 807526acfa Merge pull request #191 from KiwiTechLLC/dev
Dev
2023-08-11 12:24:28 +05:30
dilipshrivastwa-kiwi f750487b88 Merge pull request #190 from KiwiTechLLC/sprint4
add correct and attempt answer
2023-08-11 12:21:47 +05:30
jain 8b0032f6d2 junior point table 2023-08-11 12:18:35 +05:30
jain bb65f81200 add correct and attempt answer 2023-08-11 11:48:02 +05:30
dilipshrivastwa-kiwi 04db40e100 Merge pull request #189 from KiwiTechLLC/sprint4
create article
2023-08-10 21:24:15 +05:30
jain 1e162255d7 create article 2023-08-10 20:17:24 +05:30
Ankitajain-kiwi f4149379c2 Merge pull request #188 from KiwiTechLLC/dev
Dev
2023-08-10 19:21:12 +05:30
dilipshrivastwa-kiwi a479f3cb62 Merge pull request #187 from KiwiTechLLC/sprint4
task points
2023-08-10 19:06:32 +05:30
jain fbd6f9ece5 task points 2023-08-10 19:05:33 +05:30
jain 11c84208b7 task points 2023-08-10 19:00:37 +05:30
Ruman Siddiqui 4f02cef0f9 Optimised login api 2023-08-10 18:43:03 +05:30
Abu Talib b8f1acaed8 Merge pull request #185 from KiwiTechLLC/dev
Dev
2023-08-10 18:37:06 +05:30
dilipshrivastwa-kiwi 80dffb7942 Merge pull request #184 from KiwiTechLLC/ZBKADM-71
image name fix, remove '|' character
2023-08-10 18:21:10 +05:30
abutalib-kiwi 0a8c2cf56d image name fix, remove '|' character 2023-08-10 18:17:49 +05:30
Ankitajain-kiwi 9e5dd5e3d5 Merge pull request #183 from KiwiTechLLC/dev
Dev
2023-08-10 17:04:06 +05:30
dilipshrivastwa-kiwi 03fb4f4176 Merge pull request #182 from KiwiTechLLC/sprint4
current page update in article api
2023-08-10 15:51:09 +05:30
jain 22afe7e555 current page update in article api 2023-08-10 15:43:31 +05:30
jain 69723b362f current page update in article api 2023-08-10 15:30:46 +05:30
Ankitajain-kiwi 9e612c7171 Merge pull request #181 from KiwiTechLLC/sprint4
junior points
2023-08-10 12:20:34 +05:30
jain 7a9be0326a junior points 2023-08-10 12:19:08 +05:30
Ankitajain-kiwi aaa1c55227 Merge pull request #180 from KiwiTechLLC/sprint4
guardian code
2023-08-10 12:07:49 +05:30
jain 656f0da89a guardian code 2023-08-10 12:06:52 +05:30
Ankitajain-kiwi b961044e4d Merge pull request #179 from KiwiTechLLC/dev
Dev
2023-08-10 11:57:29 +05:30
Ankitajain-kiwi 28e71e132c Merge pull request #178 from KiwiTechLLC/sprint4
Sprint4
2023-08-10 11:56:53 +05:30
jain b30643299f junior points 2023-08-10 11:56:19 +05:30
jain b6b70af13f remove guardian code request 2023-08-10 11:02:32 +05:30
jain 22dd7fc10b remove guardian code request 2023-08-10 10:55:33 +05:30
Ankitajain-kiwi 2498394127 Merge pull request #177 from KiwiTechLLC/dev
Dev
2023-08-09 19:22:42 +05:30
Ankitajain-kiwi 924bc20193 Merge pull request #176 from KiwiTechLLC/sprint4
points
2023-08-09 19:21:50 +05:30
jain af7582b9e2 points 2023-08-09 19:20:56 +05:30
Ankitajain-kiwi 856d27d803 Merge pull request #175 from KiwiTechLLC/sprint4
points remove from serializer
2023-08-09 18:54:12 +05:30
jain 19f56280e4 points remove from serializer 2023-08-09 18:53:18 +05:30
Ankitajain-kiwi 2fc65d462d Merge pull request #174 from KiwiTechLLC/dev
Dev
2023-08-09 18:11:40 +05:30
Ankitajain-kiwi d3800dbc85 Merge pull request #173 from KiwiTechLLC/sprint4
guardian code update
2023-08-09 18:10:18 +05:30
dilipshrivastwa-kiwi 9373dc4697 Merge pull request #172 from KiwiTechLLC/ZBKADM-71
analytics section api, users counts, new signup count, task report
2023-08-09 17:20:53 +05:30
jain 32eaa6c3f2 guardian code update 2023-08-09 17:18:07 +05:30
abutalib-kiwi 11ff8fc700 conflict resolved 2023-08-09 17:03:42 +05:30
abutalib-kiwi 9c75cb1615 analytics section api, users counts, new signup count, task report 2023-08-09 16:57:19 +05:30
dilipshrivastwa-kiwi 3f8b0e2eb7 Merge pull request #171 from KiwiTechLLC/sprint4
financial learning section api
2023-08-09 16:44:27 +05:30
jain 69ce6857e8 financial learning section api 2023-08-09 16:36:33 +05:30
Abu Talib cdf656fdad Merge pull request #170 from KiwiTechLLC/dev
Dev
2023-08-09 14:51:13 +05:30
Ankitajain-kiwi 3efa31efe4 Merge pull request #169 from KiwiTechLLC/sprint4
Sprint4
2023-08-09 14:39:01 +05:30
jain 275b09d2ea Merge branch 'sprint4' of github.com:KiwiTechLLC/ZODBank-Backend into sprint4 2023-08-09 14:38:09 +05:30
jain 3cb0e3ed8b guardian code add in junior list api 2023-08-09 14:37:38 +05:30
dilipshrivastwa-kiwi 823c5ea4c4 Merge pull request #168 from KiwiTechLLC/ZBKADM-69
otp, verify otp and login bug resolved
2023-08-09 14:21:58 +05:30
abutalib-kiwi c9ee482512 otp, verify otp and login bug resolved 2023-08-09 14:16:20 +05:30
dilipshrivastwa-kiwi 08750813ce Merge pull request #167 from KiwiTechLLC/sprint4
Sprint4
2023-08-09 13:54:08 +05:30
Ankitajain-kiwi 75f4f66285 Merge branch 'dev' into sprint4 2023-08-09 13:49:39 +05:30
jain 881bda739b article card api, check answer api 2023-08-09 13:48:02 +05:30
jain d86f082e58 article card api, check answer api 2023-08-09 13:37:06 +05:30
Ankitajain-kiwi 8eaf8751c2 Merge pull request #165 from KiwiTechLLC/dev
Dev
2023-08-08 17:14:36 +05:30
Ankitajain-kiwi b12c5071fe Merge pull request #164 from KiwiTechLLC/sprint4
changes in add junior api
2023-08-08 16:37:08 +05:30
jain f75201b3dd changes in add junior api 2023-08-08 16:36:03 +05:30
Ankitajain-kiwi 6867920754 Merge pull request #163 from KiwiTechLLC/dev
Dev
2023-08-08 16:07:14 +05:30
Ankitajain-kiwi 45ea7013f8 Merge pull request #162 from KiwiTechLLC/sprint4
junior list api
2023-08-08 16:06:52 +05:30
jain 7d428f5eb5 junior list api 2023-08-08 16:05:56 +05:30
Ankitajain-kiwi 22ba4288d9 Merge pull request #161 from KiwiTechLLC/dev
Dev
2023-08-08 15:04:50 +05:30
Ankitajain-kiwi 9ff64e64ef Merge pull request #160 from KiwiTechLLC/sprint4
top junior api
2023-08-08 15:04:03 +05:30
jain 4b23394569 top junior api 2023-08-08 15:03:24 +05:30
dilipshrivastwa-kiwi 81893c8841 Merge pull request #159 from KiwiTechLLC/ZBKADM-69
change in admin login
2023-08-08 14:38:43 +05:30
abutalib-kiwi 10a1ea9b76 change in admin login 2023-08-08 14:29:57 +05:30
Abu Talib 7ab16cb3de Merge pull request #158 from KiwiTechLLC/dev
Dev
2023-08-08 14:24:44 +05:30
dilipshrivastwa-kiwi 7fca493d0d Merge pull request #157 from KiwiTechLLC/sprint4
junior position changes and decrease api response time
2023-08-08 14:02:27 +05:30
jain 85e4ae8761 junior position changes and decrease api response time 2023-08-08 13:54:04 +05:30
dilipshrivastwa-kiwi 6ebd5ae410 Merge pull request #156 from KiwiTechLLC/sprint4
guardian list api changes
2023-08-08 12:35:43 +05:30
jain f57b111555 guardian list api changes 2023-08-08 11:48:53 +05:30
jain 6596414675 guardian list api changes 2023-08-08 11:46:27 +05:30
dilipshrivastwa-kiwi ed7fd3c15d Merge pull request #155 from KiwiTechLLC/ZBKADM-69
notification list modified and mark as read method changed
2023-08-07 20:48:37 +05:30
dilipshrivastwa-kiwi 8cf7148114 Merge pull request #154 from KiwiTechLLC/ZBKADM-69
change method to get real time
2023-08-07 19:19:00 +05:30
Ankitajain-kiwi b98443479f Merge pull request #153 from KiwiTechLLC/sprint4
junior article points table
2023-08-07 19:12:55 +05:30
jain f917244265 junior article points table 2023-08-07 19:12:14 +05:30
dilipshrivastwa-kiwi ceb5bc13c3 Merge pull request #152 from KiwiTechLLC/ZBKADM-69
task list api modified
2023-08-07 18:22:20 +05:30
jain ae0fc4fe8d article list api 2023-08-07 17:06:31 +05:30
dilipshrivastwa-kiwi 0a1b9c7e70 Merge pull request #151 from KiwiTechLLC/ZBKADM-69
changes in edit api, included first name, last name and username
2023-08-07 15:14:46 +05:30
abutalib-kiwi f2cf1488e9 changes in edit api, included first name, last name and username 2023-08-07 15:12:54 +05:30
dilipshrivastwa-kiwi 0426974539 Merge pull request #150 from KiwiTechLLC/sprint4
guardian list api
2023-08-07 13:44:20 +05:30
Ankitajain-kiwi b0e26f41b9 Merge branch 'dev' into sprint4 2023-08-07 13:43:49 +05:30
jain c20249730a guardian list api 2023-08-07 13:42:47 +05:30
jain b3b499e661 guardian list api 2023-08-07 13:41:33 +05:30
jain f377e283fd guardian list api 2023-08-07 13:25:01 +05:30
dilipshrivastwa-kiwi 648a2ec4d5 Merge pull request #149 from KiwiTechLLC/ZBKADM-69
admin login api modified
2023-08-07 12:13:51 +05:30
abutalib-kiwi 83ec922584 admin login api modified 2023-08-07 12:10:36 +05:30
jain 88221ec77a notifications API 2023-08-07 11:28:34 +05:30
jain e853346910 notifications API 2023-08-07 11:26:23 +05:30
dilipshrivastwa-kiwi a088764b7b Merge pull request #148 from KiwiTechLLC/ZBKADM-69
edit user validations, active-inactive api
2023-08-07 11:04:20 +05:30
abutalib-kiwi 4a2f36cde8 edit user validations, active-inactive api 2023-08-07 11:01:37 +05:30
dilipshrivastwa-kiwi 401ee1ddf8 Merge pull request #147 from KiwiTechLLC/ZBKADM-69
Zbkadm 69
2023-08-04 16:46:05 +05:30
abutalib-kiwi baacb1a18f user edit api 2023-08-04 16:37:46 +05:30
abutalib-kiwi 5f1c645e3a user list api 2023-08-04 16:27:46 +05:30
Abu Talib 07d150309b Merge pull request #146 from KiwiTechLLC/dev
Dev
2023-08-04 15:04:55 +05:30
dilipshrivastwa-kiwi 79648637aa Merge pull request #145 from KiwiTechLLC/ZBKADM-69
user detail view api, searching user list, count
2023-08-04 13:21:47 +05:30
abutalib-kiwi 756bea0471 user detail view api, searching user list, count 2023-08-04 13:15:32 +05:30
dilipshrivastwa-kiwi 465632f519 Merge pull request #144 from KiwiTechLLC/ZBKADM-69
user detail api, changed image upload method
2023-08-03 18:38:53 +05:30
abutalib-kiwi b9e2d9bc8a user detail api, changed image upload method 2023-08-03 18:35:19 +05:30
dilipshrivastwa-kiwi d82c8cd4ae Merge pull request #143 from KiwiTechLLC/ZBKADM-69
user detail api, changed image upload method
2023-08-03 18:24:56 +05:30
abutalib-kiwi dd0f2b027a task list api modified 2023-08-03 18:17:01 +05:30
abutalib-kiwi 3806d1f3a6 user detail api, changed image upload method 2023-08-03 18:14:54 +05:30
Abu Talib 3801e6e027 Merge pull request #142 from KiwiTechLLC/dev
Dev
2023-08-03 14:59:49 +05:30
abutalib-kiwi f3e2ab9a34 notification list modified and mark as read method changed 2023-08-02 20:45:46 +05:30
abutalib-kiwi 685f627707 change method to get real time 2023-08-02 19:16:29 +05:30
Ankitajain-kiwi b2d172eae5 Merge pull request #141 from KiwiTechLLC/sprint4
migration file
2023-08-02 16:58:24 +05:30
jain 404825dc85 migration file 2023-08-02 16:57:40 +05:30
Ankitajain-kiwi 6e6f0a55d0 Merge pull request #140 from KiwiTechLLC/sprint4
changes in remove guardian code api
2023-08-02 16:26:56 +05:30
jain 75d0b12008 changes in remove guardian code api 2023-08-02 16:26:20 +05:30
Ankitajain-kiwi 2a4011ca5d Merge pull request #139 from KiwiTechLLC/sprint4
sprint4 reassign task after expired
2023-08-02 15:21:38 +05:30
jain ebb468166e sprint4 reassign task after expired 2023-08-02 15:20:43 +05:30
Ankitajain-kiwi e89fc513cb Merge pull request #138 from KiwiTechLLC/dev
Dev
2023-08-02 14:25:06 +05:30
Ankitajain-kiwi 06176912ee Merge pull request #137 from KiwiTechLLC/sprint4
change junior task list api response
2023-08-02 14:24:38 +05:30
jain ed8fc156ac change junior task list api response 2023-08-02 14:23:36 +05:30
Ankitajain-kiwi e1af1c200d Merge pull request #136 from KiwiTechLLC/dev
Dev
2023-08-02 11:33:29 +05:30
dilipshrivastwa-kiwi bff97f59b2 Merge pull request #135 from KiwiTechLLC/ZBKADM-67
changes in article update api and get image url method
2023-08-02 11:19:21 +05:30
abutalib-kiwi af121f5a53 changes in article update api and get image url method 2023-08-02 11:16:12 +05:30
Ankitajain-kiwi 4c3aa03e13 Merge pull request #134 from KiwiTechLLC/sprint4
api response time
2023-08-02 11:00:42 +05:30
jain f3a8b52617 api response time 2023-08-02 11:00:02 +05:30
dilipshrivastwa-kiwi 0c3a77cd11 Merge pull request #133 from KiwiTechLLC/ZBKADM-67
change in update api, added method to upload and  get image url
2023-08-01 20:19:09 +05:30
abutalib-kiwi d3564efbb9 change in update api, added method to upload and get image url 2023-08-01 20:15:01 +05:30
dilipshrivastwa-kiwi 329df77790 Merge pull request #132 from KiwiTechLLC/ZBKADM-67
article list api changes, changed related name for survey_options to …
2023-08-01 13:16:37 +05:30
abutalib-kiwi ee92c98f34 article list api changes, changed related name for survey_options to options 2023-08-01 13:14:22 +05:30
Abu Talib b238379a22 Merge pull request #130 from KiwiTechLLC/dev
Dev
2023-07-28 19:49:56 +05:30
dilipshrivastwa-kiwi fbce04b97e Merge pull request #129 from KiwiTechLLC/ZBKADM-67
fixed add junior api
2023-07-28 19:30:39 +05:30
abutalib-kiwi afaed69eec fixed add junior api 2023-07-28 19:19:59 +05:30
dilipshrivastwa-kiwi 3c0eaf676e Merge pull request #128 from KiwiTechLLC/ZBKADM-67
fixed add junior api
2023-07-28 19:00:41 +05:30
abutalib-kiwi 6ffaa66a4d fixed add junior api 2023-07-28 18:57:11 +05:30
dilipshrivastwa-kiwi 4b8e7189c1 Merge pull request #127 from KiwiTechLLC/ZBKADM-67
changes in approve junior and add juinior api, added search in junior…
2023-07-28 18:11:54 +05:30
abutalib-kiwi d68f24543d changes in approve junior and add juinior api, added search in junior list api 2023-07-28 17:32:02 +05:30
dilipshrivastwa-kiwi 3534561697 Merge pull request #126 from KiwiTechLLC/ZBKADM-67
some changes in artcle api
2023-07-28 16:50:26 +05:30
abutalib-kiwi de774111c0 some changes in artcle api 2023-07-28 16:08:40 +05:30
Abu Talib c081bc621d Merge pull request #124 from KiwiTechLLC/dev
merging dev into qa
2023-07-27 22:04:51 +05:30
dilipshrivastwa-kiwi bab50e4492 Merge pull request #123 from KiwiTechLLC/ZBKADM-67
some changes in admin password reset related api
2023-07-27 19:36:01 +05:30
abutalib-kiwi 151a177e76 some changes in admin password reset related api 2023-07-27 19:29:12 +05:30
dilipshrivastwa-kiwi 73a62186a7 Merge pull request #122 from KiwiTechLLC/ZBKADM-67
added request status in expired task cronjob
2023-07-27 15:32:17 +05:30
abutalib-kiwi 83c66ab3b6 added request status in expired task cronjob 2023-07-27 15:31:25 +05:30
dilipshrivastwa-kiwi 94e53e1dcd Merge pull request #121 from KiwiTechLLC/ZBKADM-67
added task to send otp on email
2023-07-27 13:12:36 +05:30
abutalib-kiwi 68b77ef766 added task to send otp on email 2023-07-27 12:59:41 +05:30
Abu Talib d2c8b18e3f Merge pull request #120 from KiwiTechLLC/dev
merging dev itno qa
2023-07-27 11:37:25 +05:30
dilipshrivastwa-kiwi a2000459d3 Merge pull request #119 from KiwiTechLLC/ZBKADM-67
some changes in forgot password api
2023-07-27 11:06:09 +05:30
abutalib-kiwi c079f3ceca some changes in forgot password api 2023-07-27 10:59:10 +05:30
dilipshrivastwa-kiwi 671cdfc27b Merge pull request #118 from KiwiTechLLC/ZBKADM-67
change password api
2023-07-26 17:51:32 +05:30
abutalib-kiwi a83e27b12a Merge branch 'dev' into ZBKADM-67 2023-07-26 17:45:22 +05:30
abutalib-kiwi 87aa1af02b change password api 2023-07-26 17:16:36 +05:30
Ankitajain-kiwi bdc0ba4fd5 Merge pull request #117 from KiwiTechLLC/dev
Dev
2023-07-26 16:58:38 +05:30
dilipshrivastwa-kiwi 5f3026f08f Merge pull request #116 from KiwiTechLLC/sprint3
jira-28 notification
2023-07-26 16:51:46 +05:30
Ankitajain-kiwi 3af2584d6a Merge pull request #115 from KiwiTechLLC/dev
Dev
2023-07-26 11:09:40 +05:30
Abu Talib 62cf2b14bf Merge pull request #113 from KiwiTechLLC/dev
merging dev into qa
2023-07-26 11:01:45 +05:30
Ankitajain-kiwi cbd3d139a5 Merge pull request #110 from KiwiTechLLC/dev
Dev
2023-07-25 17:36:49 +05:30
Ankitajain-kiwi 7cb792c6cf Merge pull request #104 from KiwiTechLLC/dev
Dev
2023-07-25 11:52:36 +05:30
Ankitajain-kiwi 5ea28bbd75 Merge pull request #102 from KiwiTechLLC/dev
Dev
2023-07-24 15:56:54 +05:30
Ankitajain-kiwi 180104ece8 Merge pull request #100 from KiwiTechLLC/dev
Dev
2023-07-24 11:04:08 +05:30
56 changed files with 3037 additions and 545 deletions
+1 -1
View File
@@ -21,4 +21,4 @@ static/*
__pycache__/ __pycache__/
*.env *.env
ve/* ve/*
celerybeat-schedule
+28 -9
View File
@@ -7,7 +7,9 @@ from rest_framework.renderers import JSONRenderer
from account.utils import custom_error_response from account.utils import custom_error_response
from account.models import UserDeviceDetails from account.models import UserDeviceDetails
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER
from junior.models import Junior
from guardian.models import Guardian
# Custom middleware # Custom middleware
# when user login with # when user login with
# multiple device simultaneously # multiple device simultaneously
@@ -15,6 +17,16 @@ from base.messages import ERROR_CODE, SUCCESS_CODE
# multiple devices only # multiple devices only
# user can login in single # user can login in single
# device at a time""" # device at a time"""
def custom_response(custom_error):
"""custom response"""
response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND)
# Set content type header to "application/json"
response['Content-Type'] = 'application/json'
# Render the response as JSON
renderer = JSONRenderer()
response.content = renderer.render(response.data)
return response
class CustomMiddleware(object): class CustomMiddleware(object):
"""Custom middleware""" """Custom middleware"""
def __init__(self, get_response): def __init__(self, get_response):
@@ -26,15 +38,22 @@ class CustomMiddleware(object):
response = self.get_response(request) response = self.get_response(request)
# Code to be executed after the view is called # Code to be executed after the view is called
device_id = request.META.get('HTTP_DEVICE_ID') device_id = request.META.get('HTTP_DEVICE_ID')
user_type = request.META.get('HTTP_USER_TYPE')
api_endpoint = request.path
if request.user.is_authenticated: if request.user.is_authenticated:
"""device details""" # device details
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last() device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
if device_id and not device_details: if user_type and str(user_type) == str(NUMBER['one']):
junior = Junior.objects.filter(auth=request.user, is_active=False).last()
if junior:
custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND)
response = custom_response(custom_error)
elif user_type and str(user_type) == str(NUMBER['two']):
guardian = Guardian.objects.filter(user=request.user, is_active=False).last()
if guardian:
custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND)
response = custom_response(custom_error)
if device_id and not device_details and api_endpoint != '/api/v1/user/login/':
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND) custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND) response = custom_response(custom_error)
# Set content type header to "application/json"
response['Content-Type'] = 'application/json'
# Render the response as JSON
renderer = JSONRenderer()
response.content = renderer.render(response.data)
return response return response
+67 -15
View File
@@ -104,10 +104,12 @@ class ResetPasswordSerializer(serializers.Serializer):
return user_opt_details return user_opt_details
return '' return ''
class ChangePasswordSerializer(serializers.Serializer): class ChangePasswordSerializer(serializers.Serializer):
"""Update Password after verification""" """Update Password after verification"""
current_password = serializers.CharField(max_length=100) current_password = serializers.CharField(max_length=100, required=True)
new_password = serializers.CharField(required=True) new_password = serializers.CharField(required=True)
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = User model = User
@@ -118,24 +120,66 @@ class ChangePasswordSerializer(serializers.Serializer):
if self.context.password not in ('', None) and user.check_password(value): if self.context.password not in ('', None) and user.check_password(value):
return value return value
raise serializers.ValidationError(ERROR_CODE['2015']) raise serializers.ValidationError(ERROR_CODE['2015'])
def create(self, validated_data): def create(self, validated_data):
"""
"""
new_password = validated_data.pop('new_password') new_password = validated_data.pop('new_password')
current_password = validated_data.pop('current_password') current_password = validated_data.pop('current_password')
"""Check new password is different from current password""" # Check new password is different from current password
if new_password == current_password: if new_password == current_password:
raise serializers.ValidationError({"details": ERROR_CODE['2026']}) raise serializers.ValidationError({"details": ERROR_CODE['2026']})
user_details = User.objects.filter(email=self.context).last()
if user_details:
user_details.set_password(new_password)
user_details.save()
return {'password':new_password}
return ''
user_details = self.context
user_details.set_password(new_password)
user_details.save()
return {'password':new_password}
class ForgotPasswordSerializer(serializers.Serializer): class ForgotPasswordSerializer(serializers.Serializer):
"""Forget password serializer""" """Forget password serializer"""
email = serializers.EmailField() email = serializers.EmailField(required=True)
def validate_email(self, value):
"""
validate email exist ot not
value: string
return none
"""
if not User.objects.get(email=value):
raise serializers.ValidationError({'details': ERROR_CODE['2004']})
return value
class AdminLoginSerializer(serializers.ModelSerializer):
"""admin login serializer"""
email = serializers.EmailField(required=True)
password = serializers.CharField(required=True)
class Meta:
"""
meta class
"""
model = User
fields = ('email', 'password')
def validate(self, attrs):
user = User.objects.filter(email__iexact=attrs['email'], is_superuser=True
).only('id', 'first_name', 'last_name', 'email',
'username', 'is_active', 'is_superuser').first()
if not user or not user.check_password(attrs['password']):
raise serializers.ValidationError({'details': ERROR_CODE['2002']})
self.context.update({'user': user})
return attrs
def create(self, validated_data):
"""
used to return the user object after validation
"""
return self.context['user']
class SuperUserSerializer(serializers.ModelSerializer): class SuperUserSerializer(serializers.ModelSerializer):
"""Super admin serializer""" """Super admin serializer"""
@@ -185,10 +229,17 @@ class GuardianSerializer(serializers.ModelSerializer):
def get_user_type(self, obj): def get_user_type(self, obj):
"""user type""" """user type"""
email_verified = UserEmailOtp.objects.filter(email=obj.user.username).last() if self.context.get('user_type', ''):
if email_verified and email_verified.user_type is not None: return self.context.get('user_type')
return email_verified.user_type # remove the below code once user_type can be passed
return str(NUMBER['two']) # from everywhere from where this serializer is being called
else:
email_verified = UserEmailOtp.objects.filter(
email=obj.user.username
).last()
if email_verified and email_verified.user_type is not None:
return email_verified.user_type
return str(NUMBER['two'])
def get_auth(self, obj): def get_auth(self, obj):
"""user email address""" """user email address"""
@@ -206,7 +257,7 @@ class GuardianSerializer(serializers.ModelSerializer):
"""Meta info""" """Meta info"""
model = Guardian model = Guardian
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code', fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active', 'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active', 'is_deleted',
'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type', 'country_name'] 'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type', 'country_name']
@@ -249,7 +300,8 @@ class JuniorSerializer(serializers.ModelSerializer):
model = Junior model = Junior
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code', fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_password_set', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_password_set',
'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited'] 'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited',
'is_deleted']
class EmailVerificationSerializer(serializers.ModelSerializer): class EmailVerificationSerializer(serializers.ModelSerializer):
"""Email verification serializer""" """Email verification serializer"""
@@ -8,14 +8,14 @@
<tr> <tr>
<td style="padding: 0 27px 15px;"> <td style="padding: 0 27px 15px;">
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;"> <p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
Hi {{name}}, Hi Support Team,
</p> </p>
</td> </td>
</tr> </tr>
<tr> <tr>
<td style="padding: 0 27px 22px;"> <td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;"> <p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
<b>{{name}}</b> have some queries and need some support. Please support them by using their email address <b> {{sender}}</b>. <br> <br> <b>Queries are:- </b> <br> {{ message }} <b>{{name}}</b> have some queries and need some support. Please support them by using their email address <b> {{sender}}</b>. <br> <br> <b>Queries are:- </b> <br><li> {{ message }}</li>
</p> </p>
</td> </td>
</tr> </tr>
+3 -2
View File
@@ -28,14 +28,15 @@ from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVer
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage, ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet, GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet, DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView) UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView,
AdminLoginViewSet)
"""Router""" """Router"""
router = routers.SimpleRouter() router = routers.SimpleRouter()
"""API End points with router""" """API End points with router"""
router.register('user', UserLogin, basename='user') router.register('user', UserLogin, basename='user')
"""super admin login""" """super admin login"""
router.register('admin', UserLogin, basename='admin') router.register('admin', AdminLoginViewSet, basename='admin')
"""google login end point""" """google login end point"""
router.register('google-login', GoogleLoginViewSet, basename='admin') router.register('google-login', GoogleLoginViewSet, basename='admin')
router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp') router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp')
+59 -5
View File
@@ -1,4 +1,6 @@
"""Account utils""" """Account utils"""
from celery import shared_task
"""Import django""" """Import django"""
from django.conf import settings from django.conf import settings
from rest_framework import viewsets, status from rest_framework import viewsets, status
@@ -20,10 +22,11 @@ from rest_framework import serializers
# Import messages from base package""" # Import messages from base package"""
from junior.models import Junior from junior.models import Junior
from guardian.models import Guardian from guardian.models import Guardian
from account.models import UserDelete from account.models import UserDelete, UserDeviceDetails
from base.messages import ERROR_CODE from base.messages import ERROR_CODE
from django.utils import timezone from django.utils import timezone
from base.constants import NUMBER
from junior.models import JuniorPoints
# Define delete # Define delete
# user account condition, # user account condition,
# Define delete # Define delete
@@ -43,7 +46,7 @@ from django.utils import timezone
# referral code, # referral code,
# Define function for generating # Define function for generating
# alphanumeric code # alphanumeric code
# otp expiry
def delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num): def delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num):
"""delete user account""" """delete user account"""
if user_type == '1' and user_type_data == '1': if user_type == '1' and user_type_data == '1':
@@ -91,7 +94,10 @@ def junior_account_update(user_tb):
junior_data.is_active = False junior_data.is_active = False
junior_data.is_verified = False junior_data.is_verified = False
junior_data.guardian_code = '{}' junior_data.guardian_code = '{}'
junior_data.guardian_code_status = str(NUMBER['one'])
junior_data.is_deleted = True
junior_data.save() junior_data.save()
JuniorPoints.objects.filter(junior=junior_data).delete()
def guardian_account_update(user_tb): def guardian_account_update(user_tb):
"""update guardian account after delete the user account""" """update guardian account after delete the user account"""
@@ -100,12 +106,14 @@ def guardian_account_update(user_tb):
# Update guardian account # Update guardian account
guardian_data.is_active = False guardian_data.is_active = False
guardian_data.is_verified = False guardian_data.is_verified = False
guardian_data.is_deleted = True
guardian_data.save() guardian_data.save()
jun_data = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code)) jun_data = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code))
"""Disassociate relation between guardian and junior""" """Disassociate relation between guardian and junior"""
for data in jun_data: for data in jun_data:
data.guardian_code.remove(guardian_data.guardian_code) data.guardian_code.remove(guardian_data.guardian_code)
data.save() data.save()
@shared_task()
def send_otp_email(recipient_email, otp): def send_otp_email(recipient_email, otp):
"""Send otp on email with template""" """Send otp on email with template"""
from_email = settings.EMAIL_FROM_ADDRESS from_email = settings.EMAIL_FROM_ADDRESS
@@ -121,6 +129,44 @@ def send_otp_email(recipient_email, otp):
) )
return otp return otp
@shared_task()
def send_all_email(template_name, email, otp):
"""
Send all type of email by passing template name
template_name: string
email: string
otp: string
"""
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [email]
send_templated_mail(
template_name=template_name,
from_email=from_email,
recipient_list=recipient_list,
context={
'verification_code': otp
}
)
return otp
@shared_task
def user_device_details(user, device_id):
"""
Used to store the device id of the user
user: user object
device_id: string
return
"""
device_details, created = UserDeviceDetails.objects.get_or_create(user__id=user)
if device_details:
device_details.device_id = device_id
device_details.save()
return True
return False
def send_support_email(name, sender, subject, message): def send_support_email(name, sender, subject, message):
"""Send otp on email with template""" """Send otp on email with template"""
to_email = [settings.EMAIL_FROM_ADDRESS] to_email = [settings.EMAIL_FROM_ADDRESS]
@@ -138,12 +184,14 @@ def send_support_email(name, sender, subject, message):
} }
) )
return name return name
def custom_response(detail, data=None, response_status=status.HTTP_200_OK):
def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count=None):
"""Custom response code""" """Custom response code"""
if not data: if not data:
"""when data is none""" """when data is none"""
data = None data = None
return Response({"data": data, "message": detail, "status": "success", "code": response_status}) return Response({"data": data, "message": detail, "status": "success", "code": response_status, "count": count})
def custom_error_response(detail, response_status): def custom_error_response(detail, response_status):
@@ -229,3 +277,9 @@ def generate_code(value, user_id):
OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1) OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1)
def get_user_full_name(user_obj):
"""
to get user's full name
"""
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.last_name else user_obj.first_name
+159 -92
View File
@@ -1,11 +1,12 @@
"""Account view """ """Account view """
import threading
from notifications.utils import remove_fcm_token from notifications.utils import remove_fcm_token
"""Django import""" # django imports
from datetime import datetime, timedelta from datetime import datetime, timedelta
from rest_framework import viewsets, status, views from rest_framework import viewsets, status, views
from rest_framework.decorators import action from rest_framework.decorators import action
import random
import logging import logging
from django.utils import timezone from django.utils import timezone
import jwt import jwt
@@ -18,25 +19,27 @@ import google.auth.transport.requests
from rest_framework import status from rest_framework import status
import requests import requests
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import mixins
from django.conf import settings from django.conf import settings
"""App Import"""
# local imports
from guardian.models import Guardian from guardian.models import Guardian
from junior.models import Junior from junior.models import Junior, JuniorPoints
from guardian.utils import upload_image_to_alibaba from guardian.utils import upload_image_to_alibaba
from account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification from account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification
from django.contrib.auth.models import User from django.contrib.auth.models import User
"""Account serializer"""
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer, from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer, ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer, GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer,
DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer, DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer,
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer) UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer,
AdminLoginSerializer)
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, ZOD, JUN, GRD from base.constants import NUMBER, ZOD, JUN, GRD, USER_TYPE_FLAG
from guardian.tasks import generate_otp from guardian.tasks import generate_otp
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response, from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
generate_code, OTP_EXPIRY) generate_code, OTP_EXPIRY, user_device_details, send_all_email)
from junior.serializers import JuniorProfileSerializer from junior.serializers import JuniorProfileSerializer
from guardian.serializers import GuardianProfileSerializer from guardian.serializers import GuardianProfileSerializer
@@ -95,6 +98,8 @@ class GoogleLoginMixin(object):
referral_code=generate_code(ZOD, user_obj.id) referral_code=generate_code(ZOD, user_obj.id)
) )
serializer = JuniorSerializer(junior_query) serializer = JuniorSerializer(junior_query)
position = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_query, position=position)
if str(user_type) == '2': if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True, guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
image=profile_picture,signup_method='2', image=profile_picture,signup_method='2',
@@ -145,6 +150,8 @@ class SigninWithApple(views.APIView):
junior_code=generate_code(JUN, user.id), junior_code=generate_code(JUN, user.id),
referral_code=generate_code(ZOD, user.id)) referral_code=generate_code(ZOD, user.id))
serializer = JuniorSerializer(junior_query) serializer = JuniorSerializer(junior_query)
position = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_query, position=position)
if str(user_type) == '2': if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True, guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
signup_method='3', signup_method='3',
@@ -186,15 +193,30 @@ class UpdateProfileImage(views.APIView):
return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST)
class ChangePasswordAPIView(views.APIView): class ChangePasswordAPIView(views.APIView):
"""change password""" """
change password"
"""
serializer_class = ChangePasswordSerializer serializer_class = ChangePasswordSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def post(self, request): def post(self, request):
serializer = ChangePasswordSerializer(context=request.user, data=request.data) """
POST request to change current login user password
"""
serializer = ChangePasswordSerializer(
context=request.user,
data=request.data
)
if serializer.is_valid(): if serializer.is_valid():
serializer.save() serializer.save()
return custom_response(SUCCESS_CODE['3007'], response_status=status.HTTP_200_OK) return custom_response(
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) SUCCESS_CODE['3007'],
response_status=status.HTTP_200_OK
)
return custom_error_response(
serializer.errors,
response_status=status.HTTP_400_BAD_REQUEST
)
class ResetPasswordAPIView(views.APIView): class ResetPasswordAPIView(views.APIView):
"""Reset password""" """Reset password"""
@@ -206,44 +228,43 @@ class ResetPasswordAPIView(views.APIView):
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class ForgotPasswordAPIView(views.APIView): class ForgotPasswordAPIView(views.APIView):
"""Forgot password""" """
Forgot password
"""
serializer_class = ForgotPasswordSerializer
def post(self, request): def post(self, request):
serializer = ForgotPasswordSerializer(data=request.data)
if serializer.is_valid(): """
email = serializer.validated_data['email'] Post method to validate serializer
try: """
User.objects.get(email=email) serializer = self.serializer_class(data=request.data)
except User.DoesNotExist: serializer.is_valid(raise_exception=True)
return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND) email = serializer.validated_data['email']
verification_code = generate_otp() # generate otp
# Send the verification code to the user's email verification_code = generate_otp()
from_email = settings.EMAIL_FROM_ADDRESS # Send the verification code to the user's email
recipient_list = [email] send_all_email.delay(
send_templated_mail( 'email_reset_verification.email', email, verification_code
template_name='email_reset_verification.email', )
from_email=from_email, expiry = OTP_EXPIRY
recipient_list=recipient_list, user_data, created = UserEmailOtp.objects.get_or_create(
context={ email=email
'verification_code': verification_code )
} if created:
) user_data.expired_at = expiry
expiry = OTP_EXPIRY user_data.save()
user_data, created = UserEmailOtp.objects.get_or_create(email=email) if user_data:
if created: user_data.otp = verification_code
user_data.expired_at = expiry user_data.expired_at = expiry
user_data.save() user_data.save()
if user_data: return custom_response(
user_data.otp = verification_code SUCCESS_CODE['3015'],
user_data.expired_at = expiry response_status=status.HTTP_200_OK
user_data.save() )
return custom_response(SUCCESS_CODE['3015'],
response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class SendPhoneOtp(viewsets.ModelViewSet): class SendPhoneOtp(viewsets.ModelViewSet):
"""Send otp on phone""" """Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer serializer_class = UserPhoneOtpSerializer
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
otp = generate_otp() otp = generate_otp()
@@ -260,7 +281,6 @@ class SendPhoneOtp(viewsets.ModelViewSet):
class UserPhoneVerification(viewsets.ModelViewSet): class UserPhoneVerification(viewsets.ModelViewSet):
"""Send otp on phone""" """Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer serializer_class = UserPhoneOtpSerializer
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
try: try:
@@ -276,25 +296,50 @@ class UserPhoneVerification(viewsets.ModelViewSet):
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
class UserLogin(viewsets.ViewSet): class UserLogin(viewsets.ViewSet):
"""User login""" """User login"""
@action(methods=['post'], detail=False) @action(methods=['post'], detail=False)
def login(self, request): def login(self, request):
username = request.data.get('username') username = request.data.get('username')
password = request.data.get('password') password = request.data.get('password')
user_type = request.META.get('HTTP_USER_TYPE')
device_id = request.META.get('HTTP_DEVICE_ID') device_id = request.META.get('HTTP_DEVICE_ID')
user = authenticate(request, username=username, password=password) user = authenticate(request, username=username, password=password)
try: try:
if user is not None: if user is not None:
login(request, user) login(request, user)
guardian_data = Guardian.objects.filter(user__username=username, is_verified=True).last() if str(user_type) == USER_TYPE_FLAG["TWO"]:
if guardian_data: guardian_data = Guardian.objects.filter(user__username=username).last()
serializer = GuardianSerializer(guardian_data).data if guardian_data:
junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last() if guardian_data.is_verified:
if junior_data: serializer = GuardianSerializer(
serializer = JuniorSerializer(junior_data).data guardian_data, context={'user_type': user_type}
).data
else:
return custom_error_response(
ERROR_CODE["2070"],
response_status=status.HTTP_401_UNAUTHORIZED
)
elif str(user_type) == USER_TYPE_FLAG["FIRST"]:
junior_data = Junior.objects.filter(auth__username=username).last()
if junior_data:
if junior_data.is_verified:
serializer = JuniorSerializer(
junior_data, context={'user_type': user_type}
).data
else:
return custom_error_response(
ERROR_CODE["2071"],
response_status=status.HTTP_401_UNAUTHORIZED
)
else:
return custom_error_response(
ERROR_CODE["2069"],
response_status=status.HTTP_401_UNAUTHORIZED
)
# storing device id in using celery task so the time would be reduced
# user_device_details.delay(user.id, device_id)
device_details, created = UserDeviceDetails.objects.get_or_create(user=user) device_details, created = UserDeviceDetails.objects.get_or_create(user=user)
if device_details: if device_details:
device_details.device_id = device_id device_details.device_id = device_id
@@ -308,9 +353,12 @@ class UserLogin(viewsets.ViewSet):
refresh = RefreshToken.for_user(user) refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token) access_token = str(refresh.access_token)
refresh_token = str(refresh) refresh_token = str(refresh)
data = {"auth_token":access_token, "refresh_token":refresh_token, "is_profile_complete": False, data = {
"user_type": email_verified.user_type, "auth_token":access_token,
} "refresh_token":refresh_token,
"is_profile_complete": False,
"user_type": user_type,
}
is_verified = False is_verified = False
if email_verified: if email_verified:
is_verified = email_verified.is_verified is_verified = email_verified.is_verified
@@ -319,43 +367,68 @@ class UserLogin(viewsets.ViewSet):
email_verified.otp = otp email_verified.otp = otp
email_verified.save() email_verified.save()
data.update({"email_otp":otp}) data.update({"email_otp":otp})
send_otp_email(username, otp) send_otp_email.delay(username, otp)
return custom_response(ERROR_CODE['2024'], {"email_otp": otp, "is_email_verified": is_verified}, return custom_response(
response_status=status.HTTP_200_OK) ERROR_CODE['2024'],
{"email_otp": otp, "is_email_verified": is_verified},
response_status=status.HTTP_200_OK
)
data.update({"is_email_verified": is_verified}) data.update({"is_email_verified": is_verified})
return custom_response(SUCCESS_CODE['3003'], data, response_status=status.HTTP_200_OK) return custom_response(
SUCCESS_CODE['3003'],
data,
response_status=status.HTTP_200_OK
)
@action(methods=['post'], detail=False) @action(methods=['post'], detail=False)
def admin_login(self, request): def admin_login(self, request):
username = request.data.get('username') email = request.data.get('email')
password = request.data.get('password') password = request.data.get('password')
user = authenticate(request, username=username, password=password) user = User.objects.filter(email__iexact=email, is_superuser=True
try: ).only('id', 'first_name', 'last_name', 'email',
if user is not None: 'username', 'is_active', 'is_superuser').first()
login(request, user)
if user.is_superuser: if not user or not user.check_password(password):
serializer = SuperUserSerializer(user) return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST)
return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
else: serializer = SuperUserSerializer(user)
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED) return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
logging.error(e)
refresh = RefreshToken.for_user(user) class AdminLoginViewSet(viewsets.GenericViewSet):
access_token = str(refresh.access_token) """
refresh_token = str(refresh) admin login api
data = {"auth_token": access_token, "refresh_token":refresh_token, "user_type": '3'} """
return custom_response(None, data, response_status=status.HTTP_200_OK) serializer_class = AdminLoginSerializer
@action(methods=['post'], url_name='login', url_path='login', detail=False)
def admin_login(self, request, *args, **kwargs):
"""
:param request:
:return:
"""
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
user = serializer.save()
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
data = {"auth_token": access_token, "refresh_token": refresh_token, "username": user.username,
"email": user.email, "first_name": user.first_name, "last_name": user.last_name,
"is_active": user.is_active, "user_type": '3', "is_superuser": user.is_superuser}
return custom_response(None, data)
class UserEmailVerification(viewsets.ModelViewSet): class UserEmailVerification(viewsets.ModelViewSet):
"""User Email verification""" """User Email verification"""
serializer_class = EmailVerificationSerializer serializer_class = EmailVerificationSerializer
queryset = UserEmailOtp.objects.all() http_method_names = ('post',)
def list(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
try: try:
user_obj = User.objects.filter(username=self.request.GET.get('email')).last() user_obj = User.objects.filter(username=self.request.data.get('email')).last()
email_data = UserEmailOtp.objects.filter(email=self.request.GET.get('email'), email_data = UserEmailOtp.objects.filter(email=self.request.data.get('email'),
otp=self.request.GET.get('otp')).last() otp=self.request.data.get('otp')).last()
if email_data: if email_data:
input_datetime_str = str(email_data.expired_at) input_datetime_str = str(email_data.expired_at)
input_format = "%Y-%m-%d %H:%M:%S.%f%z" input_format = "%Y-%m-%d %H:%M:%S.%f%z"
@@ -369,12 +442,12 @@ class UserEmailVerification(viewsets.ModelViewSet):
email_data.is_verified = True email_data.is_verified = True
email_data.save() email_data.save()
if email_data.user_type == '1': if email_data.user_type == '1':
junior_data = Junior.objects.filter(auth__email=self.request.GET.get('email')).last() junior_data = Junior.objects.filter(auth__email=self.request.data.get('email')).last()
if junior_data: if junior_data:
junior_data.is_verified = True junior_data.is_verified = True
junior_data.save() junior_data.save()
else: else:
guardian_data = Guardian.objects.filter(user__email=self.request.GET.get('email')).last() guardian_data = Guardian.objects.filter(user__email=self.request.data.get('email')).last()
if guardian_data: if guardian_data:
guardian_data.is_verified = True guardian_data.is_verified = True
guardian_data.save() guardian_data.save()
@@ -392,14 +465,13 @@ class UserEmailVerification(viewsets.ModelViewSet):
class ReSendEmailOtp(viewsets.ModelViewSet): class ReSendEmailOtp(viewsets.ModelViewSet):
"""Send otp on phone""" """Send otp on phone"""
queryset = UserEmailOtp.objects.all()
serializer_class = EmailVerificationSerializer serializer_class = EmailVerificationSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
otp = generate_otp() otp = generate_otp()
if User.objects.filter(email=request.data['email']): if User.objects.filter(email=request.data['email']):
expiry = OTP_EXPIRY expiry = timezone.now() + timezone.timedelta(days=1)
email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email']) email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email'])
if created: if created:
email_data.expired_at = expiry email_data.expired_at = expiry
@@ -415,7 +487,6 @@ class ReSendEmailOtp(viewsets.ModelViewSet):
class ProfileAPIViewSet(viewsets.ModelViewSet): class ProfileAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset""" """Profile viewset"""
queryset = User.objects.all()
serializer_class = JuniorProfileSerializer serializer_class = JuniorProfileSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
@@ -434,7 +505,6 @@ class ProfileAPIViewSet(viewsets.ModelViewSet):
class UploadImageAPIViewSet(viewsets.ModelViewSet): class UploadImageAPIViewSet(viewsets.ModelViewSet):
"""upload task image""" """upload task image"""
queryset = DefaultTaskImages.objects.all()
serializer_class = DefaultTaskImagesSerializer serializer_class = DefaultTaskImagesSerializer
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""upload images""" """upload images"""
@@ -453,7 +523,6 @@ class UploadImageAPIViewSet(viewsets.ModelViewSet):
class DefaultImageAPIViewSet(viewsets.ModelViewSet): class DefaultImageAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset""" """Profile viewset"""
queryset = DefaultTaskImages.objects.all()
serializer_class = DefaultTaskImagesDetailsSerializer serializer_class = DefaultTaskImagesDetailsSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
@@ -484,19 +553,17 @@ class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
class UserNotificationAPIViewSet(viewsets.ModelViewSet): class UserNotificationAPIViewSet(viewsets.ModelViewSet):
"""notification viewset""" """notification viewset"""
queryset = UserNotification.objects.all()
serializer_class = UserNotificationSerializer serializer_class = UserNotificationSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
"""profile view""" """profile view"""
queryset = self.queryset.filter(user=request.user) queryset = UserNotification.objects.filter(user=request.user)
serializer = UserNotificationSerializer(queryset, many=True) serializer = UserNotificationSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet): class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
"""Update notification viewset""" """Update notification viewset"""
queryset = UserNotification.objects.all()
serializer_class = UpdateUserNotificationSerializer serializer_class = UpdateUserNotificationSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
+25 -3
View File
@@ -27,7 +27,7 @@ NUMBER = {
'ninety_nine': 99, 'hundred': 100, 'thirty_six_hundred': 3600 'ninety_nine': 99, 'hundred': 100, 'thirty_six_hundred': 3600
} }
none = "none"
# Super Admin string constant for 'role' # Super Admin string constant for 'role'
SUPER_ADMIN = "Super Admin" SUPER_ADMIN = "Super Admin"
@@ -50,6 +50,13 @@ USER_TYPE = (
('2', 'guardian'), ('2', 'guardian'),
('3', 'superuser') ('3', 'superuser')
) )
USER_TYPE_FLAG = {
"FIRST" : "1",
"TWO" : "2",
"THREE": "3"
}
"""gender""" """gender"""
GENDERS = ( GENDERS = (
('1', 'Male'), ('1', 'Male'),
@@ -70,6 +77,18 @@ SIGNUP_METHODS = (
('2', 'google'), ('2', 'google'),
('3', 'apple') ('3', 'apple')
) )
# guardian code status
GUARDIAN_CODE_STATUS = (
('1', 'no guardian code'),
('2', 'exist guardian code'),
('3', 'request for guardian code')
)
# article status
ARTICLE_STATUS = (
('1', 'read'),
('2', 'in_progress'),
('3', 'completed')
)
# relationship # relationship
RELATIONSHIP = ( RELATIONSHIP = (
('1', 'parent'), ('1', 'parent'),
@@ -83,6 +102,7 @@ IN_PROGRESS = 2
REJECTED = 3 REJECTED = 3
REQUESTED = 4 REQUESTED = 4
COMPLETED = 5 COMPLETED = 5
EXPIRED = 6
TASK_POINTS = 5 TASK_POINTS = 5
# duplicate name used defined in constant PROJECT_NAME # duplicate name used defined in constant PROJECT_NAME
PROJECT_NAME = 'Zod Bank' PROJECT_NAME = 'Zod Bank'
@@ -105,7 +125,9 @@ MAX_ARTICLE_CARD = 6
MIN_ARTICLE_SURVEY = 5 MIN_ARTICLE_SURVEY = 5
MAX_ARTICLE_SURVEY = 10 MAX_ARTICLE_SURVEY = 10
# real time url # already register
time_url = "http://worldtimeapi.org/api/timezone/Asia/Riyadh" Already_register_user = "duplicate key value violates unique constraint"
ARTICLE_CARD_IMAGE_FOLDER = 'article-card-images' ARTICLE_CARD_IMAGE_FOLDER = 'article-card-images'
DATE_FORMAT = '%Y-%m-%d'
+44 -9
View File
@@ -23,15 +23,15 @@ ERROR_CODE_REQUIRED = {
# Error code # Error code
ERROR_CODE = { ERROR_CODE = {
"2000": "Email not found.", "2000": "Invalid email address. Please enter a registered email.",
"2001": "This is your existing password. Please choose other one", "2001": "This is your existing password. Please choose other one",
"2002": "Invalid login credentials.", "2002": "Invalid username or password.",
"2003": "An account already exists with this email address.", "2003": "An account already exists with this email address.",
"2004": "User not found.", "2004": "User not found.",
"2005": "Your account has been activated.", "2005": "Your account has been activated.",
"2006": "Your account is not activated.", "2006": "Your account is not activated.",
"2007": "Your account already activated.", "2007": "Your account already activated.",
"2008": "Invalid OTP.", "2008": "The OTP entered is not correct.",
"2009": "The user provided cannot be found or the reset password token has become invalid/timed out.", "2009": "The user provided cannot be found or the reset password token has become invalid/timed out.",
"2010": "Invalid Link.", "2010": "Invalid Link.",
"2011": "Your profile has not been completed yet.", "2011": "Your profile has not been completed yet.",
@@ -54,7 +54,7 @@ ERROR_CODE = {
"2026": "New password should not same as old password", "2026": "New password should not same as old password",
"2027": "data should contain `identityToken`", "2027": "data should contain `identityToken`",
"2028": "You are not authorized person to sign up on this platform", "2028": "You are not authorized person to sign up on this platform",
"2029": "Validity of otp verification is expired", "2029": "Validity of otp verification has expired. Please request a new one.",
"2030": "Use correct user type and token", "2030": "Use correct user type and token",
# invalid password # invalid password
"2031": "Invalid password", "2031": "Invalid password",
@@ -88,7 +88,24 @@ ERROR_CODE = {
"2060": "Task does not exist or not in pending state", "2060": "Task does not exist or not in pending state",
"2061": "Please insert image or check the image is valid or not.", "2061": "Please insert image or check the image is valid or not.",
# email not null # email not null
"2062": "Please enter email address" "2062": "Please enter email address",
"2063": "Unauthorized access.",
"2064": "To change your password first request an OTP and get it verify then change your password.",
"2065": "Passwords do not match. Please try again.",
"2066": "Task does not exist or not in expired state",
"2067": "Action not allowed. User type missing.",
"2068": "No guardian associated with this junior",
"2069": "Invalid user type",
"2070": "You do not find as a guardian",
"2071": "You do not find as a junior",
"2072": "You can not approve or reject this task because junior does not exist in the system",
"2073": "You can not approve or reject this junior because junior does not exist in the system",
"2074": "You can not complete this task because you does not exist in the system",
"2075": "Your account is deactivated. Please contact with admin",
"2076": "This junior already associate with you",
"2077": "You can not add guardian",
"2078": "This junior is not associate with you",
} }
"""Success message code""" """Success message code"""
SUCCESS_CODE = { SUCCESS_CODE = {
@@ -104,7 +121,7 @@ SUCCESS_CODE = {
# Success code for link verified # Success code for link verified
"3005": "Your account is deleted successfully.", "3005": "Your account is deleted successfully.",
# Success code for password reset # Success code for password reset
"3006": "Your password has been reset successfully.", "3006": "Password reset successful. You can now log in with your new password.",
# Success code for password update # Success code for password update
"3007": "Your password has been changed successfully.", "3007": "Your password has been changed successfully.",
# Success code for valid link # Success code for valid link
@@ -117,8 +134,8 @@ SUCCESS_CODE = {
"3012": "Phone OTP Verified successfully", "3012": "Phone OTP Verified successfully",
"3013": "Valid Guardian code", "3013": "Valid Guardian code",
"3014": "Password has been updated successfully.", "3014": "Password has been updated successfully.",
"3015": "Verification code sent on your email.", "3015": "Verification code has been sent on your email.",
"3016": "Send otp on your Email successfully", "3016": "An OTP has been sent on your email.",
"3017": "Profile image update successfully", "3017": "Profile image update successfully",
"3018": "Task created successfully", "3018": "Task created successfully",
"3019": "Support Email sent successfully", "3019": "Support Email sent successfully",
@@ -137,7 +154,25 @@ SUCCESS_CODE = {
"3032": "Task request sent successfully", "3032": "Task request sent successfully",
"3033": "Valid Referral code", "3033": "Valid Referral code",
"3034": "Invite guardian successfully", "3034": "Invite guardian successfully",
"3035": "Task started successfully" "3035": "Task started successfully",
"3036": "Task reassign successfully",
"3037": "Profile has been updated successfully.",
"3038": "Status has been changed successfully.",
# notification read
"3039": "Notification read successfully",
# start article
"3040": "Start article successfully",
# complete article
"3041": "Article completed successfully",
# submit assessment successfully
"3042": "Assessment completed successfully",
# read article
"3043": "Read article card successfully",
# remove guardian code request
"3044": "Remove guardian code request successfully",
# create faq
"3045": "Create FAQ data"
} }
"""status code error""" """status code error"""
STATUS_CODE_ERROR = { STATUS_CODE_ERROR = {
+29
View File
@@ -0,0 +1,29 @@
"""
web_admin tasks file
"""
# third party imports
from celery import shared_task
from templated_email import send_templated_mail
# django imports
from django.conf import settings
@shared_task
def send_email_otp(email, verification_code):
"""
used to send otp on email
:param email: e-mail
:param verification_code: otp
"""
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [email]
send_templated_mail(
template_name='email_reset_verification.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'verification_code': verification_code
}
)
return True
Binary file not shown.
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-08-17 12:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0020_alter_juniortask_task_status'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='is_deleted',
field=models.BooleanField(default=False),
),
]
+2
View File
@@ -57,6 +57,8 @@ class Guardian(models.Model):
is_invited = models.BooleanField(default=False) is_invited = models.BooleanField(default=False)
# Profile activity""" # Profile activity"""
is_password_set = models.BooleanField(default=True) is_password_set = models.BooleanField(default=True)
# guardian profile deleted or not"""
is_deleted = models.BooleanField(default=False)
"""Profile activity""" """Profile activity"""
is_active = models.BooleanField(default=True) is_active = models.BooleanField(default=True)
"""guardian is verified or not""" """guardian is verified or not"""
+162 -19
View File
@@ -1,13 +1,15 @@
"""Serializer of Guardian""" """Serializer of Guardian"""
"""Third party Django app""" # third party imports
import logging import logging
from django.contrib.auth import password_validation
from rest_framework import serializers from rest_framework import serializers
# Import Refresh token of jwt # Import Refresh token of jwt
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
from django.db import transaction from django.db import transaction
from django.contrib.auth.models import User from django.contrib.auth.models import User
from datetime import datetime, time from datetime import datetime, time
"""Import Django app""" import pytz
from django.utils import timezone
# Import guardian's model, # Import guardian's model,
# Import junior's model, # Import junior's model,
# Import account's model, # Import account's model,
@@ -16,20 +18,21 @@ from datetime import datetime, time
# Import messages from # Import messages from
# base package, # base package,
# Import some functions # Import some functions
# from utils file""" # local imports
from .models import Guardian, JuniorTask from .models import Guardian, JuniorTask
from account.models import UserProfile, UserEmailOtp, UserNotification from account.models import UserProfile, UserEmailOtp, UserNotification
from account.utils import generate_code from account.utils import generate_code
from junior.serializers import JuniorDetailSerializer from junior.serializers import JuniorDetailSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, JUN, ZOD, GRD from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user
from junior.models import Junior, JuniorPoints from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
# notification's constant # notification's constant
from notifications.constants import TASK_POINTS, TASK_REJECTED from notifications.constants import TASK_POINTS, TASK_REJECTED
# send notification function # send notification function
from notifications.utils import send_notification from notifications.utils import send_notification, send_notification_to_junior
from django.core.exceptions import ValidationError
from django.utils.translation import gettext as _
# In this serializer file # In this serializer file
# define user serializer, # define user serializer,
@@ -41,10 +44,45 @@ from notifications.utils import send_notification
# guardian profile serializer, # guardian profile serializer,
# approve junior serializer, # approve junior serializer,
# approve task serializer, # approve task serializer,
from rest_framework import serializers
class PasswordValidator:
def __init__(self, min_length=8, max_length=None, require_uppercase=True, require_numbers=True):
self.min_length = min_length
self.max_length = max_length
self.require_uppercase = require_uppercase
self.require_numbers = require_numbers
def __call__(self, value):
self.enforce_password_policy(value)
def enforce_password_policy(self, password):
special_characters = "!@#$%^&*()_-+=<>?/[]{}|"
if len(password) < self.min_length:
raise serializers.ValidationError(
_("Password must be at least %(min_length)d characters long.") % {'min_length': self.min_length}
)
if self.max_length is not None and len(password) > self.max_length:
raise serializers.ValidationError(
_("Password must be at most %(max_length)d characters long.") % {'max_length': self.max_length}
)
if self.require_uppercase and not any(char.isupper() for char in password):
raise serializers.ValidationError(_("Password must contain at least one uppercase letter."))
if self.require_numbers and not any(char.isdigit() for char in password):
raise serializers.ValidationError(_("Password must contain at least one digit."))
if self.require_numbers and not any(char in special_characters for char in password):
raise serializers.ValidationError(_("Password must contain at least one special character."))
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):
"""User serializer""" """User serializer"""
auth_token = serializers.SerializerMethodField('get_auth_token') auth_token = serializers.SerializerMethodField('get_auth_token')
password = serializers.CharField(write_only=True, validators=[PasswordValidator()])
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
@@ -67,8 +105,10 @@ class UserSerializer(serializers.ModelSerializer):
UserNotification.objects.get_or_create(user=user) UserNotification.objects.get_or_create(user=user)
if user_type == str(NUMBER['one']): if user_type == str(NUMBER['one']):
# create junior profile # create junior profile
Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id), junior = Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
referral_code=generate_code(ZOD, user.id)) referral_code=generate_code(ZOD, user.id))
position = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior, position=position)
if user_type == str(NUMBER['two']): if user_type == str(NUMBER['two']):
# create guardian profile # create guardian profile
Guardian.objects.create(user=user, guardian_code=generate_code(GRD, user.id), Guardian.objects.create(user=user, guardian_code=generate_code(GRD, user.id),
@@ -81,7 +121,7 @@ class UserSerializer(serializers.ModelSerializer):
otp_verified = False otp_verified = False
if otp and otp.is_verified: if otp and otp.is_verified:
otp_verified = True otp_verified = True
raise serializers.ValidationError({"details":ERROR_CODE['2021'], "otp_verified":bool(otp_verified), raise serializers.ValidationError({"details": ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
"code": 400, "status":"failed", "code": 400, "status":"failed",
}) })
@@ -211,7 +251,7 @@ class GuardianDetailSerializer(serializers.ModelSerializer):
"""Meta info""" """Meta info"""
model = Guardian model = Guardian
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image', 'is_deleted',
'updated_at'] 'updated_at']
class TaskDetailsSerializer(serializers.ModelSerializer): class TaskDetailsSerializer(serializers.ModelSerializer):
"""Task detail serializer""" """Task detail serializer"""
@@ -224,7 +264,10 @@ class TaskDetailsSerializer(serializers.ModelSerializer):
""" remaining time to complete task""" """ remaining time to complete task"""
due_date_datetime = datetime.combine(obj.due_date, datetime.max.time()) due_date_datetime = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time # fetch real time
current_datetime = real_time() # current_datetime = real_time()
# new code
due_date_datetime = due_date_datetime.astimezone(pytz.utc)
current_datetime = timezone.now().astimezone(pytz.utc)
# Perform the subtraction # Perform the subtraction
if due_date_datetime > current_datetime: if due_date_datetime > current_datetime:
time_difference = due_date_datetime - current_datetime time_difference = due_date_datetime - current_datetime
@@ -244,6 +287,34 @@ class TaskDetailsSerializer(serializers.ModelSerializer):
'requested_on', 'rejected_on', 'completed_on', 'is_expired', 'requested_on', 'rejected_on', 'completed_on', 'is_expired',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at'] 'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TaskDetailsjuniorSerializer(serializers.ModelSerializer):
"""Task detail serializer"""
guardian = GuardianDetailSerializer()
remaining_time = serializers.SerializerMethodField('get_remaining_time')
def get_remaining_time(self, obj):
""" remaining time to complete task"""
due_date_datetime = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time
# current_datetime = real_time()
# new code
due_date_datetime = due_date_datetime.astimezone(pytz.utc)
current_datetime = timezone.now().astimezone(pytz.utc)
# Perform the subtraction
if due_date_datetime > current_datetime:
time_difference = due_date_datetime - current_datetime
time_only = convert_timedelta_into_datetime(time_difference)
return str(time_difference.days) + ' days ' + str(time_only)
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
class Meta(object):
"""Meta info"""
model = JuniorTask
fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image',
'requested_on', 'rejected_on', 'completed_on',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TopJuniorSerializer(serializers.ModelSerializer): class TopJuniorSerializer(serializers.ModelSerializer):
"""Top junior serializer""" """Top junior serializer"""
@@ -306,7 +377,8 @@ class GuardianProfileSerializer(serializers.ModelSerializer):
fields = ['id', 'email', 'first_name', 'last_name', 'country_name','country_code', 'phone', 'gender', 'dob', fields = ['id', 'email', 'first_name', 'last_name', 'country_name','country_code', 'phone', 'gender', 'dob',
'guardian_code', 'notification_count', 'total_count', 'complete_field_count', 'referral_code', 'guardian_code', 'notification_count', 'total_count', 'complete_field_count', 'referral_code',
'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method', 'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method',
'updated_at', 'passcode'] 'updated_at', 'passcode','is_deleted']
class ApproveJuniorSerializer(serializers.ModelSerializer): class ApproveJuniorSerializer(serializers.ModelSerializer):
"""approve junior serializer""" """approve junior serializer"""
@@ -319,9 +391,12 @@ class ApproveJuniorSerializer(serializers.ModelSerializer):
"""update guardian code""" """update guardian code"""
instance = self.context['junior'] instance = self.context['junior']
instance.guardian_code = [self.context['guardian_code']] instance.guardian_code = [self.context['guardian_code']]
instance.guardian_code_approved = True
instance.guardian_code_status = str(NUMBER['two'])
instance.save() instance.save()
return instance return instance
class ApproveTaskSerializer(serializers.ModelSerializer): class ApproveTaskSerializer(serializers.ModelSerializer):
"""approve task serializer""" """approve task serializer"""
class Meta(object): class Meta(object):
@@ -343,18 +418,86 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
# update total task point # update total task point
junior_data.total_points = junior_data.total_points + instance.points junior_data.total_points = junior_data.total_points + instance.points
# update complete time of task # update complete time of task
instance.completed_on = real_time() # instance.completed_on = real_time()
send_notification.delay(TASK_POINTS, None, junior_details.auth.id, {}) instance.completed_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_POINTS, None, junior_details.auth.id,
{'task_id': instance.id})
else: else:
# reject the task # reject the task
instance.task_status = str(NUMBER['three']) instance.task_status = str(NUMBER['three'])
instance.is_approved = False instance.is_approved = False
# update total task point
junior_data.total_points = junior_data.total_points - instance.points
# update reject time of task # update reject time of task
instance.rejected_on = real_time() # instance.rejected_on = real_time()
send_notification.delay(TASK_REJECTED, None, junior_details.auth.id, {}) instance.rejected_on = timezone.now().astimezone(pytz.utc)
send_notification_to_junior.delay(TASK_REJECTED, None, junior_details.auth.id,
{'task_id': instance.id})
instance.save() instance.save()
junior_data.save() junior_data.save()
return junior_details return junior_details
class GuardianDetailListSerializer(serializers.ModelSerializer):
"""Guardian serializer"""
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
email = serializers.SerializerMethodField('get_email')
image = serializers.SerializerMethodField('get_image')
guardian_id = serializers.SerializerMethodField('get_guardian_id')
guardian_code = serializers.SerializerMethodField('get_guardian_code')
gender = serializers.SerializerMethodField('get_gender')
phone = serializers.SerializerMethodField('get_phone')
country_name = serializers.SerializerMethodField('get_country_name')
dob = serializers.SerializerMethodField('get_dob')
guardian_code_status = serializers.SerializerMethodField('get_guardian_code_status')
# code info
class Meta(object):
"""Meta info"""
model = JuniorGuardianRelationship
fields = ['guardian_id', 'first_name', 'last_name', 'email', 'relationship', 'image', 'dob',
'guardian_code', 'gender', 'phone', 'country_name', 'created_at', 'guardian_code_status',
'updated_at']
def get_guardian_id(self,obj):
"""first name of guardian"""
return obj.guardian.id
def get_first_name(self,obj):
"""first name of guardian"""
return obj.guardian.user.first_name
def get_last_name(self,obj):
"""last name of guardian"""
return obj.guardian.user.last_name
def get_email(self,obj):
"""email of guardian"""
return obj.guardian.user.email
def get_image(self,obj):
"""guardian image"""
return obj.guardian.image
def get_guardian_code(self,obj):
""" guardian code"""
return obj.guardian.guardian_code
def get_gender(self,obj):
""" guardian gender"""
return obj.guardian.gender
def get_phone(self,obj):
"""guardian phone"""
return obj.guardian.phone
def get_country_name(self,obj):
""" guardian country name """
return obj.guardian.country_name
def get_dob(self,obj):
"""guardian dob """
return obj.guardian.dob
def get_guardian_code_status(self,obj):
"""guardian code status"""
return obj.junior.guardian_code_status
+7 -2
View File
@@ -1,7 +1,12 @@
"""task files""" """task files"""
"""Django import"""
# Django import
import secrets import secrets
def generate_otp(): def generate_otp():
"""generate random otp""" """
generate random otp
"""
digits = "0123456789" digits = "0123456789"
return "".join(secrets.choice(digits) for _ in range(6)) return "".join(secrets.choice(digits) for _ in range(6))
+4 -1
View File
@@ -2,7 +2,8 @@
"""Django import""" """Django import"""
from django.urls import path, include from django.urls import path, include
from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView, from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView,
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView) SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView,
GuardianListAPIView)
"""Third party import""" """Third party import"""
from rest_framework import routers from rest_framework import routers
@@ -36,6 +37,8 @@ router.register('filter-task', SearchTaskListAPIView, basename='filter-task')
router.register('approve-junior', ApproveJuniorAPIView, basename='approve-junior') router.register('approve-junior', ApproveJuniorAPIView, basename='approve-junior')
# Approve junior API""" # Approve junior API"""
router.register('approve-task', ApproveTaskAPIView, basename='approve-task') router.register('approve-task', ApproveTaskAPIView, basename='approve-task')
# guardian list API"""
router.register('guardian-list', GuardianListAPIView, basename='guardian-list')
# Define Url pattern""" # Define Url pattern"""
urlpatterns = [ urlpatterns = [
path('api/v1/', include(router.urls)), path('api/v1/', include(router.urls)),
+40 -12
View File
@@ -11,7 +11,7 @@ import tempfile
# Import date time module's function # Import date time module's function
from datetime import datetime, time from datetime import datetime, time
# import Number constant # import Number constant
from base.constants import NUMBER, time_url from base.constants import NUMBER
# Import Junior's model # Import Junior's model
from junior.models import Junior, JuniorPoints from junior.models import Junior, JuniorPoints
# Import guardian's model # Import guardian's model
@@ -21,7 +21,9 @@ from zod_bank.celery import app
# notification's constant # notification's constant
from notifications.constants import REFERRAL_POINTS from notifications.constants import REFERRAL_POINTS
# send notification function # send notification function
from notifications.utils import send_notification from notifications.utils import send_notification, send_notification_to_junior
# Define upload image on # Define upload image on
# ali baba cloud # ali baba cloud
# firstly save image # firstly save image
@@ -42,14 +44,40 @@ def upload_image_to_alibaba(image, filename):
with tempfile.NamedTemporaryFile(delete=False) as temp_file: with tempfile.NamedTemporaryFile(delete=False) as temp_file:
"""write image in temporary file""" """write image in temporary file"""
temp_file.write(image.read()) temp_file.write(image.read())
"""auth of bucket""" return upload_file_to_alibaba(temp_file, filename)
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
"""fetch bucket details"""
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME) def upload_base64_image_to_alibaba(image, filename):
# Upload the temporary file to Alibaba OSS """
bucket.put_object_from_file(filename, temp_file.name) upload image on oss alibaba bucket
"""create perfect url for image""" """
new_filename = filename.replace(' ', '%20') # Save the image object to a temporary file
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
# write image in temporary file
temp_file.write(image)
return upload_file_to_alibaba(temp_file, filename)
def upload_excel_file_to_alibaba(response, filename):
"""
upload excel file on oss alibaba bucket
"""
# Save the image object to a temporary file
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
# write image in temporary file
temp_file.write(response.content)
return upload_file_to_alibaba(temp_file, filename)
def upload_file_to_alibaba(temp_file, filename):
"""auth of bucket"""
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
"""fetch bucket details"""
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
# Upload the temporary file to Alibaba OSS
bucket.put_object_from_file(filename, temp_file.name)
"""create perfect url for image"""
new_filename = filename.replace(' ', '%20')
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}" return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}"
@@ -89,7 +117,7 @@ def update_referral_points(referral_code, referral_code_used):
junior_query.total_points = junior_query.total_points + NUMBER['five'] junior_query.total_points = junior_query.total_points + NUMBER['five']
junior_query.referral_points = junior_query.referral_points + NUMBER['five'] junior_query.referral_points = junior_query.referral_points + NUMBER['five']
junior_query.save() junior_query.save()
send_notification.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {}) send_notification_to_junior.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {})
@@ -99,7 +127,7 @@ def update_expired_task_status(data=None):
Update task of the status if due date is in past Update task of the status if due date is in past
""" """
try: try:
task_status = [str(NUMBER['one']), str(NUMBER['two'])] task_status = [str(NUMBER['one']), str(NUMBER['two']), str(NUMBER['four'])]
JuniorTask.objects.filter(due_date__lt=datetime.today().date(), JuniorTask.objects.filter(due_date__lt=datetime.today().date(),
task_status__in=task_status).update(task_status=str(NUMBER['six'])) task_status__in=task_status).update(task_status=str(NUMBER['six']))
except ObjectDoesNotExist as e: except ObjectDoesNotExist as e:
+103 -85
View File
@@ -10,6 +10,8 @@ from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status from rest_framework import viewsets, status
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User from django.contrib.auth.models import User
from rest_framework.filters import SearchFilter
from django.utils import timezone from django.utils import timezone
# Import guardian's model, # Import guardian's model,
@@ -26,17 +28,18 @@ from django.utils import timezone
# Import notification constant # Import notification constant
# Import send_notification function # Import send_notification function
from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializer, TaskDetailsSerializer, from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializer, TaskDetailsSerializer,
TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer) TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer,
GuardianDetailListSerializer)
from .models import Guardian, JuniorTask from .models import Guardian, JuniorTask
from junior.models import Junior, JuniorPoints from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from account.models import UserEmailOtp, UserNotification from account.models import UserEmailOtp, UserNotification
from .tasks import generate_otp from .tasks import generate_otp
from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER from base.constants import NUMBER, GUARDIAN_CODE_STATUS
from .utils import upload_image_to_alibaba from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_CREATED, LEADERBOARD_RANKING from notifications.constants import REGISTRATION, TASK_ASSIGNED, LEADERBOARD_RANKING
from notifications.utils import send_notification from notifications.utils import send_notification_to_junior
""" Define APIs """ """ Define APIs """
# Define Signup API, # Define Signup API,
@@ -56,33 +59,29 @@ class SignupViewset(viewsets.ModelViewSet):
serializer_class = UserSerializer serializer_class = UserSerializer
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""Create user profile""" """Create user profile"""
try: if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]:
if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]: serializer = UserSerializer(context=request.data['user_type'], data=request.data)
serializer = UserSerializer(context=request.data['user_type'], data=request.data) if serializer.is_valid():
if serializer.is_valid(): serializer.save()
user = serializer.save() """Generate otp"""
"""Generate otp""" otp = generate_otp()
otp = generate_otp() # expire otp after 1 day
# expire otp after 1 day expiry = timezone.now() + timezone.timedelta(days=1)
expiry = OTP_EXPIRY # create user email otp object
# create user email otp object UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
UserEmailOtp.objects.create(email=request.data['email'], otp=otp, user_type=str(request.data['user_type']), expired_at=expiry)
user_type=str(request.data['user_type']), expired_at=expiry) """Send email to the register user"""
"""Send email to the register user""" send_otp_email(request.data['email'], otp)
send_otp_email(request.data['email'], otp) return custom_response(SUCCESS_CODE['3001'],
# send push notification for registration response_status=status.HTTP_200_OK)
send_notification.delay(REGISTRATION, None, user.id, {}) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
return custom_response(SUCCESS_CODE['3001'], else:
response_status=status.HTTP_200_OK) return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class UpdateGuardianProfile(viewsets.ViewSet): class UpdateGuardianProfile(viewsets.ViewSet):
"""Update guardian profile""" """
queryset = Guardian.objects.all() Update guardian profile
"""
serializer_class = CreateGuardianSerializer serializer_class = CreateGuardianSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
@@ -116,7 +115,6 @@ class UpdateGuardianProfile(viewsets.ViewSet):
class AllTaskListAPIView(viewsets.ModelViewSet): class AllTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile""" """Update guardian profile"""
serializer_class = TaskDetailsSerializer serializer_class = TaskDetailsSerializer
queryset = JuniorTask.objects.all()
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
@@ -126,38 +124,37 @@ class AllTaskListAPIView(viewsets.ModelViewSet):
serializer = TaskDetailsSerializer(queryset, many=True) serializer = TaskDetailsSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class TaskListAPIView(viewsets.ModelViewSet): class TaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile""" """Update guardian profile"""
serializer_class = TaskDetailsSerializer serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
search_fields = ['task_name', ]
pagination_class = PageNumberPagination pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
http_method_names = ('get',) http_method_names = ('get',)
def get_queryset(self):
queryset = JuniorTask.objects.filter(guardian__user=self.request.user
).prefetch_related('junior', 'junior__auth'
).order_by('due_date', 'created_at')
queryset = self.filter_queryset(queryset)
return queryset
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
"""Create guardian profile""" """Create guardian profile"""
try: status_value = self.request.GET.get('status')
status_value = self.request.GET.get('status') queryset = self.get_queryset()
search = self.request.GET.get('search') if status_value and status_value != '0':
if search and str(status_value) == '0': queryset = queryset.filter(task_status=status_value)
queryset = JuniorTask.objects.filter(guardian__user=request.user, paginator = self.pagination_class()
task_name__icontains=search).order_by('due_date', 'created_at') # use Pagination
elif search and str(status_value) != '0': paginated_queryset = paginator.paginate_queryset(queryset, request)
queryset = JuniorTask.objects.filter(guardian__user=request.user,task_name__icontains=search, # use TaskDetailsSerializer serializer
task_status=status_value).order_by('due_date', 'created_at') serializer = self.serializer_class(paginated_queryset, many=True)
if search is None and str(status_value) == '0': return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
queryset = JuniorTask.objects.filter(guardian__user=request.user).order_by('due_date', 'created_at')
elif search is None and str(status_value) != '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user,
task_status=status_value).order_by('due_date','created_at')
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = TaskDetailsSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class CreateTaskAPIView(viewsets.ModelViewSet): class CreateTaskAPIView(viewsets.ModelViewSet):
"""create task for junior""" """create task for junior"""
@@ -169,6 +166,10 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
try: try:
image = request.data['default_image'] image = request.data['default_image']
junior = request.data['junior'] junior = request.data['junior']
junior_id = Junior.objects.filter(id=junior).last()
guardian_data = Guardian.objects.filter(user=request.user).last()
if guardian_data.guardian_code in junior_id.guardian_code:
return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST)
allowed_extensions = ['.jpg', '.jpeg', '.png'] allowed_extensions = ['.jpg', '.jpeg', '.png']
if not any(extension in str(image) for extension in allowed_extensions): if not any(extension in str(image) for extension in allowed_extensions):
return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST)
@@ -189,9 +190,10 @@ class CreateTaskAPIView(viewsets.ModelViewSet):
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data) serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data)
if serializer.is_valid(): if serializer.is_valid():
# save serializer # save serializer
serializer.save() task = serializer.save()
junior_id = Junior.objects.filter(id=junior).last()
send_notification.delay(TASK_CREATED, None, junior_id.auth.id, {}) send_notification_to_junior.delay(TASK_ASSIGNED, request.auth.payload['user_id'],
junior_id.auth.id, {'task_id': task.id})
return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e: except Exception as e:
@@ -227,9 +229,9 @@ class SearchTaskListAPIView(viewsets.ModelViewSet):
class TopJuniorListAPIView(viewsets.ModelViewSet): class TopJuniorListAPIView(viewsets.ModelViewSet):
"""Top juniors list""" """Top juniors list"""
queryset = JuniorPoints.objects.all()
serializer_class = TopJuniorSerializer serializer_class = TopJuniorSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
queryset = JuniorPoints.objects.all()
def get_serializer_context(self): def get_serializer_context(self):
# context list # context list
@@ -241,14 +243,12 @@ class TopJuniorListAPIView(viewsets.ModelViewSet):
"""Fetch junior list of those who complete their tasks""" """Fetch junior list of those who complete their tasks"""
try: try:
junior_total_points = self.get_queryset().order_by('-total_points') junior_total_points = self.get_queryset().order_by('-total_points')
# Update the position field for each JuniorPoints object # Update the position field for each JuniorPoints object
for index, junior in enumerate(junior_total_points): for index, junior in enumerate(junior_total_points):
junior.position = index + 1 junior.position = index + 1
send_notification.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {}) # send_notification_to_junior.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {})
junior.save() junior.save()
serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], many=True)
serializer = self.get_serializer(junior_total_points, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@@ -259,65 +259,64 @@ class ApproveJuniorAPIView(viewsets.ViewSet):
serializer_class = ApproveJuniorSerializer serializer_class = ApproveJuniorSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def get_queryset(self):
"""Get the queryset for the view"""
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last()
return guardian, junior_queryset
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
""" junior list""" """ junior list"""
try: try:
queryset = self.get_queryset() guardian = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last()
if junior_queryset and (junior_queryset.is_deleted or not junior_queryset.is_active):
return custom_error_response(ERROR_CODE['2073'], response_status=status.HTTP_400_BAD_REQUEST)
# action 1 is use for approve and 2 for reject # action 1 is use for approve and 2 for reject
if request.data['action'] == '1': if request.data['action'] == '1':
# use ApproveJuniorSerializer serializer # use ApproveJuniorSerializer serializer
serializer = ApproveJuniorSerializer(context={"guardian_code": queryset[0].guardian_code, serializer = ApproveJuniorSerializer(context={"guardian_code": guardian.guardian_code,
"junior": queryset[1], "action": request.data['action']}, "junior": junior_queryset, "action": request.data['action']},
data=request.data) data=request.data)
if serializer.is_valid(): if serializer.is_valid():
# save serializer # save serializer
serializer.save() serializer.save()
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
else: else:
junior_queryset.guardian_code = None
junior_queryset.guardian_code_status = str(NUMBER['one'])
junior_queryset.save()
return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ApproveTaskAPIView(viewsets.ViewSet): class ApproveTaskAPIView(viewsets.ViewSet):
"""approve junior by guardian""" """approve junior by guardian"""
serializer_class = ApproveTaskSerializer serializer_class = ApproveTaskSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def get_queryset(self):
"""Get the queryset for the view"""
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# task query
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
guardian=guardian,
junior=self.request.data.get('junior_id')).last()
return guardian, task_queryset
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
""" junior list""" """ junior list"""
# action 1 is use for approve and 2 for reject # action 1 is use for approve and 2 for reject
try: try:
queryset = self.get_queryset() guardian = Guardian.objects.filter(user__email=self.request.user).last()
# task query
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
guardian=guardian,
junior=self.request.data.get('junior_id')).last()
if task_queryset and (task_queryset.junior.is_deleted or not task_queryset.junior.is_active):
return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST)
# use ApproveJuniorSerializer serializer # use ApproveJuniorSerializer serializer
serializer = ApproveTaskSerializer(context={"guardian_code": queryset[0].guardian_code, serializer = ApproveTaskSerializer(context={"guardian_code": guardian.guardian_code,
"task_instance": queryset[1], "task_instance": task_queryset,
"action": str(request.data['action']), "action": str(request.data['action']),
"junior": self.request.data['junior_id']}, "junior": self.request.data['junior_id']},
data=request.data) data=request.data)
unexpected_task_status = [str(NUMBER['five']), str(NUMBER['six'])] unexpected_task_status = [str(NUMBER['five']), str(NUMBER['six'])]
if (str(request.data['action']) == str(NUMBER['one']) and serializer.is_valid() if (str(request.data['action']) == str(NUMBER['one']) and serializer.is_valid()
and queryset[1] and queryset[1].task_status not in unexpected_task_status): and task_queryset and task_queryset.task_status not in unexpected_task_status):
# save serializer # save serializer
serializer.save() serializer.save()
return custom_response(SUCCESS_CODE['3025'], response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3025'], response_status=status.HTTP_200_OK)
elif (str(request.data['action']) == str(NUMBER['two']) and serializer.is_valid() elif (str(request.data['action']) == str(NUMBER['two']) and serializer.is_valid()
and queryset[1] and queryset[1].task_status not in unexpected_task_status): and task_queryset and task_queryset.task_status not in unexpected_task_status):
# save serializer # save serializer
serializer.save() serializer.save()
return custom_response(SUCCESS_CODE['3026'], response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3026'], response_status=status.HTTP_200_OK)
@@ -326,3 +325,22 @@ class ApproveTaskAPIView(viewsets.ViewSet):
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class GuardianListAPIView(viewsets.ModelViewSet):
"""Guardian list of assosicated junior"""
serializer_class = GuardianDetailListSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def list(self, request, *args, **kwargs):
""" junior list"""
try:
guardian_data = JuniorGuardianRelationship.objects.filter(junior__auth__email=self.request.user)
# fetch junior object
if guardian_data:
# use GuardianDetailListSerializer serializer
serializer = GuardianDetailListSerializer(guardian_data, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_response({"status": GUARDIAN_CODE_STATUS[1][0]}, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
+24 -1
View File
@@ -2,8 +2,27 @@
"""Third party Django app""" """Third party Django app"""
from django.contrib import admin from django.contrib import admin
"""Import Django app""" """Import Django app"""
from .models import Junior, JuniorPoints, JuniorGuardianRelationship from .models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
JuniorArticleCard, FAQ)
# Register your models here. # Register your models here.
admin.site.register(FAQ)
@admin.register(JuniorArticle)
class JuniorArticleAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['junior', 'article', 'status', 'is_completed']
def __str__(self):
"""Return email id"""
return self.junior__auth__email
@admin.register(JuniorArticleCard)
class JuniorArticleCardAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['junior', 'article', 'article_card', 'is_read']
def __str__(self):
"""Return email id"""
return self.junior__auth__email
@admin.register(Junior) @admin.register(Junior)
class JuniorAdmin(admin.ModelAdmin): class JuniorAdmin(admin.ModelAdmin):
"""Junior Admin""" """Junior Admin"""
@@ -27,3 +46,7 @@ class JuniorGuardianRelationshipAdmin(admin.ModelAdmin):
"""Junior Admin""" """Junior Admin"""
list_display = ['guardian', 'junior', 'relationship'] list_display = ['guardian', 'junior', 'relationship']
@admin.register(JuniorArticlePoints)
class JuniorArticlePointsAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['junior', 'article', 'question', 'submitted_answer', 'is_answer_correct']
@@ -0,0 +1,22 @@
# Generated by Django 4.2.2 on 2023-08-02 11:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0017_juniorguardianrelationship'),
]
operations = [
migrations.RemoveField(
model_name='junior',
name='relationship',
),
migrations.AddField(
model_name='junior',
name='guardian_code_approved',
field=models.BooleanField(default=False),
),
]
@@ -0,0 +1,30 @@
# Generated by Django 4.2.2 on 2023-08-07 13:29
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0004_alter_surveyoption_survey'),
('junior', '0018_remove_junior_relationship_and_more'),
]
operations = [
migrations.CreateModel(
name='JuniorArticlePoints',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('earn_points', models.IntegerField(blank=True, default=5, null=True)),
('is_attempt', models.BooleanField(default=False)),
('is_answer_correct', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles', to='web_admin.article')),
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_details', to='junior.junior', verbose_name='Junior')),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='web_admin.articlesurvey')),
('submitted_answer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submitted_answer', to='web_admin.surveyoption')),
],
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-08-08 05:43
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0019_juniorarticlepoints'),
]
operations = [
migrations.AddField(
model_name='junior',
name='guardian_code_status',
field=models.CharField(blank=True, choices=[('1', 'no guardian code'), ('2', 'exist guardian code'), ('3', 'request for guardian code')], default='1', max_length=31, null=True),
),
]
@@ -0,0 +1,20 @@
# Generated by Django 4.2.2 on 2023-08-08 09:45
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0004_alter_surveyoption_survey'),
('junior', '0020_junior_guardian_code_status'),
]
operations = [
migrations.AlterField(
model_name='juniorarticlepoints',
name='submitted_answer',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='submitted_answer', to='web_admin.surveyoption'),
),
]
+27
View File
@@ -0,0 +1,27 @@
# Generated by Django 4.2.2 on 2023-08-09 09:34
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0004_alter_surveyoption_survey'),
('junior', '0021_alter_juniorarticlepoints_submitted_answer'),
]
operations = [
migrations.CreateModel(
name='JuniorArticle',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_completed', models.BooleanField(default=False)),
('status', models.CharField(blank=True, choices=[('1', 'read'), ('2', 'in_progress'), ('3', 'completed')], default='1', max_length=10, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles_details', to='web_admin.article')),
('junior', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article', to='junior.junior', verbose_name='Junior')),
],
),
]
@@ -0,0 +1,27 @@
# Generated by Django 4.2.2 on 2023-08-09 10:47
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0004_alter_surveyoption_survey'),
('junior', '0022_juniorarticle'),
]
operations = [
migrations.CreateModel(
name='JuniorArticleCard',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('is_read', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles_detail', to='web_admin.article')),
('article_card', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_article_card', to='web_admin.articlecard')),
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article_card', to='junior.junior', verbose_name='Junior')),
],
),
]
@@ -0,0 +1,23 @@
# Generated by Django 4.2.2 on 2023-08-10 08:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0023_juniorarticlecard'),
]
operations = [
migrations.AddField(
model_name='juniorarticle',
name='current_card_page',
field=models.IntegerField(blank=True, default=0, null=True),
),
migrations.AddField(
model_name='juniorarticle',
name='current_que_page',
field=models.IntegerField(blank=True, default=0, null=True),
),
]
@@ -0,0 +1,19 @@
# Generated by Django 4.2.2 on 2023-08-10 14:46
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('junior', '0024_juniorarticle_current_card_page_and_more'),
]
operations = [
migrations.AlterField(
model_name='juniorarticle',
name='junior',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article', to='junior.junior', verbose_name='Junior'),
),
]
@@ -0,0 +1,39 @@
# Generated by Django 4.2.2 on 2023-08-17 09:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0025_alter_juniorarticle_junior'),
]
operations = [
migrations.CreateModel(
name='FAQ',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question', models.IntegerField(max_length=100)),
('description', models.CharField(max_length=500)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'verbose_name': 'FAQ',
'verbose_name_plural': 'FAQ',
},
),
migrations.AlterModelOptions(
name='juniorarticle',
options={'verbose_name': 'Junior Article', 'verbose_name_plural': 'Junior Article'},
),
migrations.AlterModelOptions(
name='juniorarticlecard',
options={'verbose_name': 'Junior Article Card', 'verbose_name_plural': 'Junior Article Card'},
),
migrations.AlterModelOptions(
name='juniorarticlepoints',
options={'verbose_name': 'Junior Article Points', 'verbose_name_plural': 'Junior Article Points'},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-08-17 09:37
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0026_faq_alter_juniorarticle_options_and_more'),
]
operations = [
migrations.AlterField(
model_name='faq',
name='question',
field=models.CharField(max_length=100),
),
]
+18
View File
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-08-17 10:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0027_alter_faq_question'),
]
operations = [
migrations.AddField(
model_name='faq',
name='status',
field=models.IntegerField(blank=True, default=1, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-08-17 12:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0028_faq_status'),
]
operations = [
migrations.AddField(
model_name='junior',
name='is_deleted',
field=models.BooleanField(default=False),
),
]
+109 -2
View File
@@ -6,10 +6,11 @@ from django.contrib.auth import get_user_model
"""Import ArrayField""" """Import ArrayField"""
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
"""Import django app""" """Import django app"""
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP, GUARDIAN_CODE_STATUS, ARTICLE_STATUS
# Import guardian's model # Import guardian's model
from guardian.models import Guardian from guardian.models import Guardian
# Import web admin's model
from web_admin.models import SurveyOption, ArticleSurvey, Article, ArticleCard
"""Define User model""" """Define User model"""
User = get_user_model() User = get_user_model()
# Create your models here. # Create your models here.
@@ -67,10 +68,17 @@ class Junior(models.Model):
is_password_set = models.BooleanField(default=True) is_password_set = models.BooleanField(default=True)
# junior profile is complete or not""" # junior profile is complete or not"""
is_complete_profile = models.BooleanField(default=False) is_complete_profile = models.BooleanField(default=False)
# junior profile deleted or not"""
is_deleted = models.BooleanField(default=False)
# passcode of the junior profile""" # passcode of the junior profile"""
passcode = models.IntegerField(null=True, blank=True, default=None) passcode = models.IntegerField(null=True, blank=True, default=None)
# junior is verified or not""" # junior is verified or not"""
is_verified = models.BooleanField(default=False) is_verified = models.BooleanField(default=False)
"""guardian code is approved or not"""
guardian_code_approved = models.BooleanField(default=False)
# guardian code status"""
guardian_code_status = models.CharField(max_length=31, choices=GUARDIAN_CODE_STATUS, default='1',
null=True, blank=True)
# Profile created and updated time""" # Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
@@ -135,3 +143,102 @@ class JuniorGuardianRelationship(models.Model):
return f'{self.guardian.user}' return f'{self.guardian.user}'
class JuniorArticlePoints(models.Model):
"""
Survey Options model
"""
# associated junior with the task
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_details', verbose_name='Junior')
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles')
question = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='questions')
submitted_answer = models.ForeignKey(SurveyOption, on_delete=models.SET_NULL, null=True,
related_name='submitted_answer')
# earn points"""
earn_points = models.IntegerField(blank=True, null=True, default=5)
is_attempt = models.BooleanField(default=False)
is_answer_correct = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
verbose_name = 'Junior Article Points'
# another name of the model"""
verbose_name_plural = 'Junior Article Points'
def __str__(self):
"""Return title"""
return f'{self.id} | {self.question}'
class JuniorArticle(models.Model):
"""
Survey Options model
"""
# associated junior with the task
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_article',
verbose_name='Junior')
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles_details')
# article completed"""
is_completed = models.BooleanField(default=False)
status = models.CharField(max_length=10, choices=ARTICLE_STATUS, null=True, blank=True, default='1')
current_card_page = models.IntegerField(blank=True, null=True, default=0)
current_que_page = models.IntegerField(blank=True, null=True, default=0)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
verbose_name = 'Junior Article'
# another name of the model"""
verbose_name_plural = 'Junior Article'
def __str__(self):
"""Return title"""
return f'{self.id} | {self.article}'
class JuniorArticleCard(models.Model):
"""
Survey Options model
"""
# associated junior with the task
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_article_card',
verbose_name='Junior')
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles_detail')
article_card = models.ForeignKey(ArticleCard, on_delete=models.CASCADE, related_name='junior_article_card')
# article card read"""
is_read = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
verbose_name = 'Junior Article Card'
# another name of the model"""
verbose_name_plural = 'Junior Article Card'
def __str__(self):
"""Return title"""
return f'{self.id} | {self.article}'
class FAQ(models.Model):
"""FAQ model"""
# questions"""
question = models.CharField(max_length=100)
# answer"""
description = models.CharField(max_length=500)
# status
status = models.IntegerField(default=1, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
verbose_name = 'FAQ'
# another name of the model"""
verbose_name_plural = 'FAQ'
def __str__(self):
"""Return email id"""
return f'{self.question}'
+92 -28
View File
@@ -1,5 +1,8 @@
"""Serializer file for junior""" """Serializer file for junior"""
"""Import Django 3rd party app""" # third party imports
import pytz
# django imports
from rest_framework import serializers from rest_framework import serializers
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import transaction from django.db import transaction
@@ -7,17 +10,18 @@ from datetime import datetime
from django.utils import timezone from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
"""Import django app""" # local imports
from account.utils import send_otp_email, generate_code from account.utils import send_otp_email, generate_code
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, FAQ
from guardian.tasks import generate_otp from guardian.tasks import generate_otp
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED,
GUARDIAN_CODE_STATUS)
from guardian.models import Guardian, JuniorTask from guardian.models import Guardian, JuniorTask
from account.models import UserEmailOtp, UserNotification from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail from junior.utils import junior_notification_email, junior_approval_mail
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
from notifications.utils import send_notification from notifications.utils import send_notification, send_notification_to_junior, send_notification_to_guardian
from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION, from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION,
TASK_SUBMITTED) TASK_SUBMITTED)
@@ -89,7 +93,13 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
"""condition for guardian code""" """condition for guardian code"""
if guardian_code: if guardian_code:
junior.guardian_code = guardian_code junior.guardian_code = guardian_code
junior.dob = validated_data.get('dob',junior.dob) guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last()
if guardian_data:
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior)
junior.guardian_code_status = str(NUMBER['three'])
junior_approval_mail(user.email, user.first_name)
send_notification_to_guardian.delay(APPROVED_JUNIOR, junior.auth.id, guardian_data.user.id, {})
junior.dob = validated_data.get('dob', junior.dob)
junior.passcode = validated_data.get('passcode', junior.passcode) junior.passcode = validated_data.get('passcode', junior.passcode)
junior.country_name = validated_data.get('country_name', junior.country_name) junior.country_name = validated_data.get('country_name', junior.country_name)
"""Update country code and phone number""" """Update country code and phone number"""
@@ -136,8 +146,8 @@ class JuniorDetailSerializer(serializers.ModelSerializer):
"""Meta info""" """Meta info"""
model = Junior model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'guardian_code', 'image', 'is_invited', 'referral_code','is_active', 'is_complete_profile',
'image', 'updated_at'] 'created_at', 'image', 'is_deleted', 'updated_at']
class JuniorDetailListSerializer(serializers.ModelSerializer): class JuniorDetailListSerializer(serializers.ModelSerializer):
"""junior serializer""" """junior serializer"""
@@ -172,10 +182,11 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
data = JuniorPoints.objects.filter(junior=obj).last() data = JuniorPoints.objects.filter(junior=obj).last()
if data: if data:
return data.position return data.position
return 99999
def get_points(self, obj): def get_points(self, obj):
data = sum(JuniorTask.objects.filter(junior=obj, task_status=COMPLETED).values_list('points', flat=True)) data = JuniorPoints.objects.filter(junior=obj).last()
return data if data:
return data.total_points
return NUMBER['zero']
def get_in_progress_task(self, obj): def get_in_progress_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=IN_PROGRESS).count() data = JuniorTask.objects.filter(junior=obj, task_status=IN_PROGRESS).count()
@@ -200,10 +211,11 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = Junior model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'country_name', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task', 'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task',
'requested_task', 'rejected_task', 'position', 'is_invited'] 'requested_task', 'rejected_task', 'position', 'is_invited', 'guardian_code_status',
'is_deleted']
class JuniorProfileSerializer(serializers.ModelSerializer): class JuniorProfileSerializer(serializers.ModelSerializer):
"""junior serializer""" """junior serializer"""
@@ -246,15 +258,14 @@ class JuniorProfileSerializer(serializers.ModelSerializer):
fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob', fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method', 'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method',
'is_invited', 'passcode'] 'is_invited', 'passcode', 'guardian_code_approved', 'is_deleted']
class AddJuniorSerializer(serializers.ModelSerializer): class AddJuniorSerializer(serializers.ModelSerializer):
"""Add junior serializer""" """Add junior serializer"""
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = Junior model = Junior
fields = ['id', 'gender','dob', 'is_invited'] fields = ['id', 'gender', 'dob', 'is_invited']
def create(self, validated_data): def create(self, validated_data):
@@ -263,6 +274,7 @@ class AddJuniorSerializer(serializers.ModelSerializer):
email = self.context['email'] email = self.context['email']
guardian = self.context['user'] guardian = self.context['user']
relationship = self.context['relationship'] relationship = self.context['relationship']
profile_image = self.context['image']
full_name = self.context['first_name'] + ' ' + self.context['last_name'] full_name = self.context['first_name'] + ' ' + self.context['last_name']
guardian_data = Guardian.objects.filter(user__username=guardian).last() guardian_data = Guardian.objects.filter(user__username=guardian).last()
user_data = User.objects.create(username=email, email=email, user_data = User.objects.create(username=email, email=email,
@@ -272,14 +284,18 @@ class AddJuniorSerializer(serializers.ModelSerializer):
user_data.set_password(password) user_data.set_password(password)
user_data.save() user_data.save()
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'), junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
image=profile_image,
dob=validated_data.get('dob'), is_invited=True, dob=validated_data.get('dob'), is_invited=True,
guardian_code=[guardian_data.guardian_code], guardian_code=[guardian_data.guardian_code],
junior_code=generate_code(JUN, user_data.id), junior_code=generate_code(JUN, user_data.id),
referral_code=generate_code(ZOD, user_data.id), referral_code=generate_code(ZOD, user_data.id),
referral_code_used=guardian_data.referral_code, referral_code_used=guardian_data.referral_code,
is_password_set=False, is_verified=True) is_password_set=False, is_verified=True,
guardian_code_status=GUARDIAN_CODE_STATUS[1][0])
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data, JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship) relationship=relationship)
total_junior = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_data, position=total_junior)
"""Generate otp""" """Generate otp"""
otp_value = generate_otp() otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1) expiry_time = timezone.now() + timezone.timedelta(days=1)
@@ -289,9 +305,8 @@ class AddJuniorSerializer(serializers.ModelSerializer):
UserNotification.objects.get_or_create(user=user_data) UserNotification.objects.get_or_create(user=user_data)
"""Notification email""" """Notification email"""
junior_notification_email(email, full_name, email, password) junior_notification_email(email, full_name, email, password)
junior_approval_mail(guardian, full_name)
# push notification # push notification
send_notification.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {}) send_notification_to_junior.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {})
return junior_data return junior_data
@@ -306,6 +321,7 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
if instance: if instance:
instance.is_invited = False instance.is_invited = False
instance.guardian_code = '{}' instance.guardian_code = '{}'
instance.guardian_code_status = str(NUMBER['one'])
instance.save() instance.save()
return instance return instance
@@ -318,12 +334,14 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
fields = ('id', 'image') fields = ('id', 'image')
def update(self, instance, validated_data): def update(self, instance, validated_data):
instance.image = validated_data.get('image', instance.image) instance.image = validated_data.get('image', instance.image)
instance.requested_on = real_time() instance.requested_on = timezone.now().astimezone(pytz.utc)
instance.task_status = str(NUMBER['four']) instance.task_status = str(NUMBER['four'])
instance.is_approved = False instance.is_approved = False
instance.save() instance.save()
send_notification.delay(TASK_SUBMITTED, None, instance.junior.auth.id, {}) send_notification_to_junior.delay(TASK_SUBMITTED, instance.guardian.user.id,
send_notification.delay(TASK_ACTION, None, instance.guardian.user.id, {}) instance.junior.auth.id, {'task_id': instance.id})
send_notification_to_guardian.delay(TASK_ACTION, instance.junior.auth.id,
instance.guardian.user.id, {'task_id': instance.id})
return instance return instance
class JuniorPointsSerializer(serializers.ModelSerializer): class JuniorPointsSerializer(serializers.ModelSerializer):
@@ -335,6 +353,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
requested_task = serializers.SerializerMethodField('get_requested_task') requested_task = serializers.SerializerMethodField('get_requested_task')
rejected_task = serializers.SerializerMethodField('get_rejected_task') rejected_task = serializers.SerializerMethodField('get_rejected_task')
pending_task = serializers.SerializerMethodField('get_pending_task') pending_task = serializers.SerializerMethodField('get_pending_task')
expired_task = serializers.SerializerMethodField('get_expired_task')
position = serializers.SerializerMethodField('get_position') position = serializers.SerializerMethodField('get_position')
def get_junior_id(self, obj): def get_junior_id(self, obj):
@@ -370,11 +389,14 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
def get_pending_task(self, obj): def get_pending_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=PENDING).count() return JuniorTask.objects.filter(junior=obj.junior, task_status=PENDING).count()
def get_expired_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=EXPIRED).count()
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = Junior model = Junior
fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task', fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task',
'requested_task', 'rejected_task'] 'requested_task', 'rejected_task', 'expired_task', 'is_deleted']
class AddGuardianSerializer(serializers.ModelSerializer): class AddGuardianSerializer(serializers.ModelSerializer):
"""Add guardian serializer""" """Add guardian serializer"""
@@ -393,13 +415,15 @@ class AddGuardianSerializer(serializers.ModelSerializer):
relationship = self.context['relationship'] relationship = self.context['relationship']
full_name = self.context['first_name'] + ' ' + self.context['last_name'] full_name = self.context['first_name'] + ' ' + self.context['last_name']
junior_data = Junior.objects.filter(auth__username=junior).last() junior_data = Junior.objects.filter(auth__username=junior).last()
junior_data.guardian_code_status = GUARDIAN_CODE_STATUS[2][0]
junior_data.save()
instance = User.objects.filter(username=email).last() instance = User.objects.filter(username=email).last()
if instance: if instance:
guardian_data = Guardian.objects.filter(user=instance).update(is_invited=True, guardian_data = Guardian.objects.filter(user=instance).update(is_invited=True,
referral_code=generate_code(ZOD, referral_code=generate_code(ZOD,
instance.id), instance.id),
referral_code_used=junior_data.referral_code, referral_code_used=junior_data.referral_code,
is_verified=True) is_verified=True)
UserNotification.objects.get_or_create(user=instance) UserNotification.objects.get_or_create(user=instance)
return guardian_data return guardian_data
else: else:
@@ -427,8 +451,8 @@ class AddGuardianSerializer(serializers.ModelSerializer):
"""Notification email""" """Notification email"""
junior_notification_email(email, full_name, email, password) junior_notification_email(email, full_name, email, password)
junior_approval_mail(email, full_name) junior_approval_mail(email, full_name)
send_notification.delay(INVITED_GUARDIAN, None, junior_data.auth.id, {}) send_notification_to_junior.delay(INVITED_GUARDIAN, guardian_data.user.id, junior_data.auth.id, {})
send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {}) send_notification_to_guardian.delay(APPROVED_JUNIOR, junior_data.auth.id, guardian_data.user.id, {})
return guardian_data return guardian_data
class StartTaskSerializer(serializers.ModelSerializer): class StartTaskSerializer(serializers.ModelSerializer):
@@ -439,7 +463,10 @@ class StartTaskSerializer(serializers.ModelSerializer):
""" remaining time to complete task""" """ remaining time to complete task"""
due_date = datetime.combine(obj.due_date, datetime.max.time()) due_date = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time # fetch real time
real_datetime = real_time() # real_datetime = real_time()
# new code
due_date = due_date.astimezone(pytz.utc)
real_datetime = timezone.now().astimezone(pytz.utc)
# Perform the subtraction # Perform the subtraction
if due_date > real_datetime: if due_date > real_datetime:
time_difference = due_date - real_datetime time_difference = due_date - real_datetime
@@ -454,3 +481,40 @@ class StartTaskSerializer(serializers.ModelSerializer):
instance.task_status = str(NUMBER['two']) instance.task_status = str(NUMBER['two'])
instance.save() instance.save()
return instance return instance
class ReAssignTaskSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = JuniorTask
fields = ('id', 'due_date')
def update(self, instance, validated_data):
instance.task_status = str(NUMBER['one'])
instance.due_date = validated_data.get('due_date')
instance.is_approved = False
instance.requested_on = None
instance.save()
return instance
class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = Junior
fields = ('id', )
def update(self, instance, validated_data):
instance.guardian_code = None
instance.guardian_code_status = str(NUMBER['one'])
instance.save()
return instance
class FAQSerializer(serializers.ModelSerializer):
# FAQ Serializer
class Meta(object):
# meta info
model = FAQ
fields = ('id', 'question', 'description')
+18 -2
View File
@@ -4,7 +4,9 @@ from django.urls import path, include
from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView, from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView,
InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView, InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView,
CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode, CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode,
InviteGuardianAPIView, StartTaskAPIView) InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView,
StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView,
CreateArticleCardAPIView, RemoveGuardianCodeAPIView, FAQViewSet)
"""Third party import""" """Third party import"""
from rest_framework import routers from rest_framework import routers
@@ -41,10 +43,24 @@ router.register('junior-points', JuniorPointsListAPIView, basename='junior-point
router.register('validate-referral-code', ValidateReferralCode, basename='validate-referral-code') router.register('validate-referral-code', ValidateReferralCode, basename='validate-referral-code')
# invite guardian API""" # invite guardian API"""
router.register('invite-guardian', InviteGuardianAPIView, basename='invite-guardian') router.register('invite-guardian', InviteGuardianAPIView, basename='invite-guardian')
# start article"""
router.register('start-article', StartArticleAPIView, basename='start-article')
# start assessment api"""
router.register('start-assessment', StartAssessmentAPIView, basename='start-assessment')
# check answer api"""
router.register('check-answer', CheckAnswerAPIView, basename='check-answer')
# start article"""
router.register('create-article-card', CreateArticleCardAPIView, basename='create-article-card')
# FAQ API
router.register('faq', FAQViewSet, basename='faq')
# Define url pattern""" # Define url pattern"""
urlpatterns = [ urlpatterns = [
path('api/v1/', include(router.urls)), path('api/v1/', include(router.urls)),
path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view()), path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view()),
path('api/v1/complete-task/', CompleteJuniorTaskAPIView.as_view()), path('api/v1/complete-task/', CompleteJuniorTaskAPIView.as_view()),
path('api/v1/start-task/', StartTaskAPIView.as_view()) path('api/v1/start-task/', StartTaskAPIView.as_view()),
path('api/v1/reassign-task/', ReAssignJuniorTaskAPIView.as_view()),
path('api/v1/complete-article/', CompleteArticleAPIView.as_view()),
path('api/v1/read-article-card/', ReadArticleCardAPIView.as_view()),
path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view()),
] ]
+4 -3
View File
@@ -4,6 +4,7 @@ from django.conf import settings
"""Third party Django app""" """Third party Django app"""
from templated_email import send_templated_mail from templated_email import send_templated_mail
from .models import JuniorPoints from .models import JuniorPoints
from base.constants import NUMBER
from django.db.models import F from django.db.models import F
# junior notification # junior notification
# email for sending email # email for sending email
@@ -50,11 +51,11 @@ def junior_approval_mail(guardian, full_name):
def update_positions_based_on_points(): def update_positions_based_on_points():
"""Update position of the junior""" """Update position of the junior"""
# First, retrieve all the JuniorPoints instances ordered by total_points in descending order. # First, retrieve all the JuniorPoints instances ordered by total_points in descending order.
juniors_points = JuniorPoints.objects.order_by('-total_points') juniors_points = JuniorPoints.objects.order_by('-total_points', 'created_at')
# Now, iterate through the queryset and update the position field based on the order. # Now, iterate through the queryset and update the position field based on the order.
position = 1 position = NUMBER['one']
for junior_point in juniors_points: for junior_point in juniors_points:
junior_point.position = position junior_point.position = position
junior_point.save() junior_point.save()
position += 1 position += NUMBER['one']
+328 -32
View File
@@ -5,8 +5,13 @@ from rest_framework import viewsets, status, generics,views
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.pagination import PageNumberPagination from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User from django.contrib.auth.models import User
from rest_framework.filters import SearchFilter
from django.db.models import F
import datetime import datetime
import requests import requests
from rest_framework.viewsets import GenericViewSet, mixins
"""Django app import""" """Django app import"""
# Import guardian's model, # Import guardian's model,
@@ -25,20 +30,25 @@ import requests
# Import upload_image_to_alibaba # Import upload_image_to_alibaba
# Import custom_response, custom_error_response # Import custom_response, custom_error_response
# Import constants # Import constants
from junior.models import Junior, JuniorPoints from django.db.models import Sum
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\ from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
JuniorArticleCard, FAQ)
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,
RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer, RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer,
AddGuardianSerializer, StartTaskSerializer) AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer,
RemoveGuardianCodeSerializer, FAQSerializer)
from guardian.models import Guardian, JuniorTask from guardian.models import Guardian, JuniorTask
from guardian.serializers import TaskDetailsSerializer from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER from base.constants import NUMBER, ARTICLE_STATUS, none
from account.utils import custom_response, custom_error_response from account.utils import custom_response, custom_error_response
from guardian.utils import upload_image_to_alibaba from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points from .utils import update_positions_based_on_points
from notifications.utils import send_notification from notifications.utils import send_notification, send_notification_to_junior
from notifications.constants import REMOVE_JUNIOR from notifications.constants import REMOVE_JUNIOR
from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer,
StartAssessmentSerializer)
""" Define APIs """ """ Define APIs """
# Define validate guardian code API, # Define validate guardian code API,
# update junior profile, # update junior profile,
@@ -57,7 +67,6 @@ from notifications.constants import REMOVE_JUNIOR
# Create your views here. # Create your views here.
class UpdateJuniorProfile(viewsets.ViewSet): class UpdateJuniorProfile(viewsets.ViewSet):
"""Update junior profile""" """Update junior profile"""
queryset = Junior.objects.all()
serializer_class = CreateJuniorSerializer serializer_class = CreateJuniorSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
@@ -91,7 +100,6 @@ class UpdateJuniorProfile(viewsets.ViewSet):
class ValidateGuardianCode(viewsets.ViewSet): class ValidateGuardianCode(viewsets.ViewSet):
"""Check guardian code exist or not""" """Check guardian code exist or not"""
queryset = Guardian.objects.all()
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
@@ -115,8 +123,14 @@ class JuniorListAPIView(viewsets.ModelViewSet):
serializer_class = JuniorDetailListSerializer serializer_class = JuniorDetailListSerializer
queryset = Junior.objects.all() queryset = Junior.objects.all()
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
search_fields = ['auth__first_name', 'auth__last_name']
http_method_names = ('get',) http_method_names = ('get',)
def get_queryset(self):
queryset = self.filter_queryset(self.queryset)
return queryset
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
""" junior list""" """ junior list"""
try: try:
@@ -124,7 +138,8 @@ class JuniorListAPIView(viewsets.ModelViewSet):
guardian_data = Guardian.objects.filter(user__email=request.user).last() guardian_data = Guardian.objects.filter(user__email=request.user).last()
# fetch junior object # fetch junior object
if guardian_data: if guardian_data:
queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code)) queryset = self.get_queryset()
queryset = queryset.filter(guardian_code__icontains=str(guardian_data.guardian_code))
# use JuniorDetailListSerializer serializer # use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(queryset, many=True) serializer = JuniorDetailListSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
@@ -132,35 +147,68 @@ class JuniorListAPIView(viewsets.ModelViewSet):
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class AddJuniorAPIView(viewsets.ModelViewSet): class AddJuniorAPIView(viewsets.ModelViewSet):
"""Add Junior by guardian""" """Add Junior by guardian"""
queryset = Junior.objects.all()
serializer_class = AddJuniorSerializer serializer_class = AddJuniorSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
http_method_names = ('post',) http_method_names = ('post',)
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
""" junior list""" """ junior list"""
try: try:
info_data = {'user': request.user, 'relationship': str(request.data['relationship']), 'email': request.data['email'], 'first_name': request.data['first_name'], info_data = {'user': request.user, 'relationship': str(request.data['relationship']),
'last_name': request.data['last_name']} 'email': request.data['email'], 'first_name': request.data['first_name'],
if User.objects.filter(username=request.data['email']): 'last_name': request.data['last_name'], 'image':None}
return custom_error_response(ERROR_CODE['2059'], response_status=status.HTTP_400_BAD_REQUEST) profile_image = request.data.get('image')
if profile_image:
# check image size
if profile_image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
# convert into file
filename = f"images/{profile_image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(profile_image, filename)
info_data.update({"image": image_url})
if user := User.objects.filter(username=request.data['email']).first():
data = self.associate_guardian(user)
if data == none:
return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST)
elif not data:
return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST)
return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK)
# use AddJuniorSerializer serializer # use AddJuniorSerializer serializer
serializer = AddJuniorSerializer(data=request.data, context=info_data) serializer = AddJuniorSerializer(data=request.data, context=info_data)
if serializer.is_valid(): if serializer.is_valid():
# save serializer # save serializer
serializer.save() serializer.save()
return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
def associate_guardian(self, user):
junior = Junior.objects.filter(auth__email=self.request.data['email']).first()
guardian = Guardian.objects.filter(user=self.request.user).first()
if not junior:
return none
if guardian.guardian_code in junior.guardian_code:
return False
if type(junior.guardian_code) is list:
junior.guardian_code.append(guardian.guardian_code)
else:
junior.guardian_code = [guardian.guardian_code]
junior.guardian_code_status = str(NUMBER['two'])
junior.save()
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior,
relationship=str(self.request.data['relationship']))
return True
class InvitedJuniorAPIView(viewsets.ModelViewSet): class InvitedJuniorAPIView(viewsets.ModelViewSet):
"""Junior list of assosicated guardian""" """Junior list of assosicated guardian"""
serializer_class = JuniorDetailListSerializer serializer_class = JuniorDetailListSerializer
queryset = Junior.objects.all()
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination pagination_class = PageNumberPagination
http_method_names = ('get',) http_method_names = ('get',)
@@ -190,7 +238,6 @@ class FilterJuniorAPIView(viewsets.ModelViewSet):
serializer_class = JuniorDetailListSerializer serializer_class = JuniorDetailListSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination pagination_class = PageNumberPagination
queryset = Junior.objects.all()
http_method_names = ('get',) http_method_names = ('get',)
def get_queryset(self): def get_queryset(self):
@@ -227,15 +274,15 @@ class RemoveJuniorAPIView(views.APIView):
junior_id = self.request.GET.get('id') junior_id = self.request.GET.get('id')
guardian = Guardian.objects.filter(user__email=self.request.user).last() guardian = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query # fetch junior query
junior_queryset = Junior.objects.filter(id=junior_id, guardian_code__icontains=str(guardian.guardian_code), junior_queryset = Junior.objects.filter(id=junior_id,
is_invited=True).last() guardian_code__icontains=str(guardian.guardian_code)).last()
if junior_queryset: if junior_queryset:
# use RemoveJuniorSerializer serializer # use RemoveJuniorSerializer serializer
serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True) serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True)
if serializer.is_valid(): if serializer.is_valid():
# save serializer # save serializer
serializer.save() serializer.save()
send_notification.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {}) send_notification_to_junior.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {})
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else: else:
@@ -246,10 +293,9 @@ class RemoveJuniorAPIView(views.APIView):
class JuniorTaskListAPIView(viewsets.ModelViewSet): class JuniorTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile""" """Update guardian profile"""
serializer_class = TaskDetailsSerializer serializer_class = TaskDetailsjuniorSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
http_method_names = ('get',) http_method_names = ('get',)
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
@@ -276,7 +322,7 @@ class JuniorTaskListAPIView(viewsets.ModelViewSet):
# use Pagination # use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request) paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer # use TaskDetailsSerializer serializer
serializer = TaskDetailsSerializer(paginated_queryset, many=True) serializer = TaskDetailsjuniorSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
@@ -301,8 +347,11 @@ class CompleteJuniorTaskAPIView(views.APIView):
image_url = upload_image_to_alibaba(image, filename) image_url = upload_image_to_alibaba(image, filename)
# fetch junior query # fetch junior query
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last() task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user
).select_related('guardian', 'junior').last()
if task_queryset: if task_queryset:
if task_queryset.junior.is_deleted or not task_queryset.junior.is_active:
return custom_error_response(ERROR_CODE['2074'], response_status=status.HTTP_400_BAD_REQUEST)
# use CompleteTaskSerializer serializer # use CompleteTaskSerializer serializer
if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]: if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]:
"""Already request send """ """Already request send """
@@ -324,16 +373,12 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
http_method_names = ('get',) http_method_names = ('get',)
def get_queryset(self):
"""get queryset"""
return JuniorTask.objects.filter(junior__auth__email=self.request.user).last()
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
"""profile view""" """profile view"""
try: try:
queryset = self.get_queryset()
# update position of junior
update_positions_based_on_points() update_positions_based_on_points()
queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last()
# update position of junior
serializer = JuniorPointsSerializer(queryset) serializer = JuniorPointsSerializer(queryset)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e: except Exception as e:
@@ -415,3 +460,254 @@ class StartTaskAPIView(views.APIView):
return custom_error_response(ERROR_CODE['2060'], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2060'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e: except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ReAssignJuniorTaskAPIView(views.APIView):
"""Update junior task API"""
serializer_class = ReAssignTaskSerializer
model = JuniorTask
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
task_id = self.request.data.get('task_id')
task_queryset = JuniorTask.objects.filter(id=task_id, guardian__user__email=self.request.user).last()
if task_queryset and task_queryset.task_status == str(NUMBER['six']):
# use StartTaskSerializer serializer
serializer = ReAssignTaskSerializer(task_queryset, data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3036'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
# task in another state
return custom_error_response(ERROR_CODE['2066'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class StartArticleAPIView(viewsets.ModelViewSet):
"""Start article"""
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
""" junior list"""
try:
junior_instance = Junior.objects.filter(auth=self.request.user).last()
article_id = request.data.get('article_id')
article_data = Article.objects.filter(id=article_id).last()
if not JuniorArticle.objects.filter(junior=junior_instance, article=article_data).last():
JuniorArticle.objects.create(junior=junior_instance, article=article_data, status=str(NUMBER['two']),
current_card_page=NUMBER['zero'], current_que_page=NUMBER['zero'])
if article_data:
question_query = ArticleSurvey.objects.filter(article=article_id)
for question in question_query:
if not JuniorArticlePoints.objects.filter(junior=junior_instance,
article=article_data,
question=question):
JuniorArticlePoints.objects.create(junior=junior_instance,
article=article_data,
question=question)
return custom_response(SUCCESS_CODE['3040'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class StartAssessmentAPIView(viewsets.ModelViewSet):
"""Junior Points viewset"""
serializer_class = StartAssessmentSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
article_id = self.request.GET.get('article_id')
# if referral_code:
article = Article.objects.filter(id=article_id, is_deleted=False).prefetch_related(
'article_cards', 'article_survey', 'article_survey__options'
).order_by('-created_at')
return article
def list(self, request, *args, **kwargs):
"""profile view"""
try:
queryset = self.get_queryset()
paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, context={"user":request.user}, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class CheckAnswerAPIView(viewsets.ModelViewSet):
"""Junior Points viewset"""
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
question_id = self.request.GET.get('question_id')
article = ArticleSurvey.objects.filter(id=question_id).last()
return article
def list(self, request, *args, **kwargs):
"""profile view"""
try:
answer_id = self.request.GET.get('answer_id')
current_page = self.request.GET.get('current_page')
queryset = self.get_queryset()
submit_ans = SurveyOption.objects.filter(id=answer_id, is_answer=True).last()
junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user,
question=queryset)
if submit_ans:
junior_article_points.update(submitted_answer=submit_ans, is_attempt=True, is_answer_correct=True)
JuniorPoints.objects.filter(junior__auth=self.request.user).update(total_points=
F('total_points') + queryset.points)
else:
junior_article_points.update(submitted_answer=submit_ans, is_attempt=True, earn_points=0,
is_answer_correct=False)
JuniorArticle.objects.filter(junior__auth=self.request.user,
article=queryset.article).update(
current_que_page=int(current_page) + NUMBER['one'])
return custom_response(None, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class CompleteArticleAPIView(views.APIView):
"""Remove junior API"""
permission_classes = [IsAuthenticated]
http_method_names = ('put', 'get',)
def put(self, request, format=None):
try:
article_id = self.request.data.get('article_id')
JuniorArticle.objects.filter(junior__auth=request.user, article__id=article_id).update(
is_completed=True, status=str(NUMBER['three'])
)
return custom_response(SUCCESS_CODE['3041'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
def get(self, request, *args, **kwargs):
""" junior list"""
try:
article_id = self.request.GET.get('article_id')
total_earn_points = JuniorArticlePoints.objects.filter(junior__auth=request.user,
article__id=article_id,
is_answer_correct=True).aggregate(
total_earn_points=Sum('earn_points'))['total_earn_points']
data = {"total_earn_points":total_earn_points}
return custom_response(SUCCESS_CODE['3042'], data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ReadArticleCardAPIView(views.APIView):
"""Remove junior API"""
permission_classes = [IsAuthenticated]
http_method_names = ('put',)
def put(self, request, *args, **kwargs):
""" junior list"""
try:
junior_instance = Junior.objects.filter(auth=self.request.user).last()
article = self.request.data.get('article_id')
article_card = self.request.data.get('article_card')
current_page = self.request.data.get('current_page')
JuniorArticleCard.objects.filter(junior=junior_instance,
article__id=article,
article_card__id=article_card).update(is_read=True)
JuniorArticle.objects.filter(junior=junior_instance,
article__id=article).update(current_card_page=int(current_page)+NUMBER['one'])
return custom_response(SUCCESS_CODE['3043'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class CreateArticleCardAPIView(viewsets.ModelViewSet):
"""Start article"""
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
""" junior list"""
try:
junior_instance = Junior.objects.filter(auth=self.request.user).last()
article_id = request.data.get('article_id')
article_data = Article.objects.filter(id=article_id).last()
if article_data:
article_cards = ArticleCard.objects.filter(article=article_id)
for article_card in article_cards:
if not JuniorArticleCard.objects.filter(junior=junior_instance,
article=article_data,
article_card=article_card):
JuniorArticleCard.objects.create(junior=junior_instance,
article=article_data,
article_card=article_card)
return custom_response(None, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class RemoveGuardianCodeAPIView(views.APIView):
"""Update junior task API"""
serializer_class = RemoveGuardianCodeSerializer
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
junior_queryset = Junior.objects.filter(auth=self.request.user).last()
if junior_queryset:
# use RemoveGuardianCodeSerializer serializer
serializer = RemoveGuardianCodeSerializer(junior_queryset, data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
# task in another state
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class FAQViewSet(GenericViewSet, mixins.CreateModelMixin,
mixins.ListModelMixin):
"""FAQ view set"""
serializer_class = FAQSerializer
permission_classes = [IsAuthenticated]
http_method_names = ['get', 'post']
def get_queryset(self):
return FAQ.objects.all()
def create(self, request, *args, **kwargs):
"""
faq create api method
:param request:
:param args:
:param kwargs:
:return: success message
"""
obj_data = [FAQ(**item) for item in request.data]
try:
FAQ.objects.bulk_create(obj_data)
return custom_response(SUCCESS_CODE["3045"], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
def list(self, request, *args, **kwargs):
"""
article list api method
:param request:
:param args:
:param kwargs:
:return: list of article
"""
queryset = self.get_queryset()
paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, data=serializer.data, response_status=status.HTTP_200_OK)
+10 -10
View File
@@ -3,7 +3,7 @@ notification constants file
""" """
from base.constants import NUMBER from base.constants import NUMBER
REGISTRATION = NUMBER['one'] REGISTRATION = NUMBER['one']
TASK_CREATED = NUMBER['two'] TASK_ASSIGNED = NUMBER['two']
INVITED_GUARDIAN = NUMBER['three'] INVITED_GUARDIAN = NUMBER['three']
APPROVED_JUNIOR = NUMBER['four'] APPROVED_JUNIOR = NUMBER['four']
REFERRAL_POINTS = NUMBER['five'] REFERRAL_POINTS = NUMBER['five']
@@ -21,17 +21,17 @@ NOTIFICATION_DICT = {
"title": "Successfully registered!", "title": "Successfully registered!",
"body": "You have registered successfully. Now login and complete your profile." "body": "You have registered successfully. Now login and complete your profile."
}, },
TASK_CREATED: { TASK_ASSIGNED: {
"title": "Task created!", "title": "New task assigned !!",
"body": "Task created successfully." "body": "{from_user} has assigned you a new task."
}, },
INVITED_GUARDIAN: { INVITED_GUARDIAN: {
"title": "Invite guardian", "title": "Invite guardian",
"body": "Invite guardian successfully" "body": "Invite guardian successfully"
}, },
APPROVED_JUNIOR: { APPROVED_JUNIOR: {
"title": "Approve junior", "title": "Approve junior !",
"body": "You have request from junior to associate with you" "body": "You have request from {from_user} to associate with you."
}, },
REFERRAL_POINTS: { REFERRAL_POINTS: {
"title": "Earn Referral points", "title": "Earn Referral points",
@@ -47,15 +47,15 @@ NOTIFICATION_DICT = {
}, },
SKIPPED_PROFILE_SETUP: { SKIPPED_PROFILE_SETUP: {
"title": "Skipped profile setup!", "title": "Skipped profile setup!",
"body": "Your guardian has been setup your profile." "body": "Your guardian {from_user} has setup your profile."
}, },
TASK_SUBMITTED: { TASK_SUBMITTED: {
"title": "Task submitted!", "title": "Task submitted!",
"body": "Your task has been submitted successfully." "body": "Your task has been submitted successfully."
}, },
TASK_ACTION: { TASK_ACTION: {
"title": "Task approval!", "title": "Task completion approval!",
"body": "You have request for task approval." "body": "You have request from {from_user} for task approval."
}, },
LEADERBOARD_RANKING: { LEADERBOARD_RANKING: {
"title": "Leader board rank!", "title": "Leader board rank!",
@@ -67,6 +67,6 @@ NOTIFICATION_DICT = {
}, },
TEST_NOTIFICATION: { TEST_NOTIFICATION: {
"title": "Test Notification", "title": "Test Notification",
"body": "This notification is for testing purpose" "body": "This notification is for testing purpose from {from_user}."
} }
} }
+18
View File
@@ -6,6 +6,7 @@ from rest_framework import serializers
# local imports # local imports
from notifications.utils import register_fcm_token from notifications.utils import register_fcm_token
from notifications.models import Notification
class RegisterDevice(serializers.Serializer): class RegisterDevice(serializers.Serializer):
@@ -26,3 +27,20 @@ class RegisterDevice(serializers.Serializer):
device_type = validated_data['type'] device_type = validated_data['type']
return register_fcm_token(self.context['user_id'], registration_id, return register_fcm_token(self.context['user_id'], registration_id,
validated_data['device_id'], device_type) validated_data['device_id'], device_type)
class NotificationListSerializer(serializers.ModelSerializer):
"""List of notification"""
class Meta(object):
"""meta info"""
model = Notification
fields = ['id', 'data', 'is_read', 'created_at']
class ReadNotificationSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = Notification
fields = ('id',)
+2 -1
View File
@@ -6,7 +6,7 @@ from django.urls import path, include
from rest_framework import routers from rest_framework import routers
# local imports # local imports
from notifications.views import NotificationViewSet from notifications.views import NotificationViewSet, ReadNotification
# initiate router # initiate router
router = routers.SimpleRouter() router = routers.SimpleRouter()
@@ -15,4 +15,5 @@ router.register('notifications', NotificationViewSet, basename='notifications')
urlpatterns = [ urlpatterns = [
path('api/v1/', include(router.urls)), path('api/v1/', include(router.urls)),
path('api/v1/read-notification/', ReadNotification.as_view()),
] ]
+67 -15
View File
@@ -10,6 +10,9 @@ from firebase_admin.messaging import Message, Notification as FirebaseNotificati
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from account.models import UserNotification from account.models import UserNotification
from account.utils import get_user_full_name
from guardian.models import Guardian
from junior.models import Junior
from notifications.constants import NOTIFICATION_DICT from notifications.constants import NOTIFICATION_DICT
from notifications.models import Notification from notifications.models import Notification
@@ -39,31 +42,80 @@ def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None:
print(e) print(e)
def get_basic_detail(notification_type, from_user_id, to_user_id): def get_basic_detail(from_user_id, to_user_id):
""" used to get the basic details """ """
notification_data = NOTIFICATION_DICT[notification_type] used to get the basic details
"""
from_user = User.objects.get(id=from_user_id) if from_user_id else None from_user = User.objects.get(id=from_user_id) if from_user_id else None
to_user = User.objects.get(id=to_user_id) to_user = User.objects.get(id=to_user_id)
return notification_data, from_user, to_user return from_user, to_user
@shared_task() def get_notification_data(notification_type, from_user, to_user, extra_data):
def send_notification(notification_type, from_user_id, to_user_id, extra_data): """
""" used to send the push for the given notification type """ get notification and push data
(notification_data, from_user, to_user) = get_basic_detail(notification_type, from_user_id, to_user_id) :param notification_type: notification_type
:param from_user: from_user obj
:param to_user: to_user obj
:param extra_data: any extra data provided
:return: notification and push data
"""
push_data = NOTIFICATION_DICT[notification_type].copy()
notification_data = push_data.copy()
notification_data['to_user'] = get_user_full_name(to_user)
if from_user:
from_user_name = get_user_full_name(from_user)
push_data['body'] = push_data['body'].format(from_user=from_user_name)
notification_data['body'] = notification_data['body'].format(from_user=from_user_name)
notification_data['from_user'] = from_user_name
notification_data.update(extra_data)
return notification_data, push_data
def send_notification(notification_type, from_user, to_user, extra_data):
"""
used to send the push for the given notification type
"""
# (from_user, to_user) = get_basic_detail(from_user_id, to_user_id)
notification_data, push_data = get_notification_data(notification_type, from_user, to_user, extra_data)
user_notification_type = UserNotification.objects.filter(user=to_user).first() user_notification_type = UserNotification.objects.filter(user=to_user).first()
# data = notification_data.data notification_data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()})
data = notification_data
Notification.objects.create(notification_type=notification_type, notification_from=from_user, Notification.objects.create(notification_type=notification_type, notification_from=from_user,
notification_to=to_user, data=data) notification_to=to_user, data=notification_data)
if user_notification_type.push_notification: if user_notification_type.push_notification:
data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()}) send_push(to_user, push_data)
send_push(to_user, data)
def send_push(user, data): def send_push(user, data):
""" used to send push notification to specific user """ """ used to send push notification to specific user """
notification_data = data.pop('data', None)
user.fcmdevice_set.filter(active=True).send_message( user.fcmdevice_set.filter(active=True).send_message(
Message(notification=FirebaseNotification(data['title'], data['body']), data=notification_data) Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
) )
@shared_task()
def send_notification_to_guardian(notification_type, from_user_id, to_user_id, extra_data):
from_user = None
if from_user_id:
from_user = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first()
extra_data['from_user_image'] = from_user.image
from_user = from_user.auth
to_user = Guardian.objects.filter(user_id=to_user_id).select_related('user').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.user, extra_data)
@shared_task()
def send_notification_to_junior(notification_type, from_user_id, to_user_id, extra_data):
from_user = None
if from_user_id:
from_user = Guardian.objects.filter(user_id=from_user_id).select_related('user').first()
extra_data['from_user_image'] = from_user.image
from_user = from_user.user
to_user = Junior.objects.filter(auth_id=to_user_id).select_related('auth').first()
extra_data['to_user_image'] = to_user.image
send_notification(notification_type, from_user, to_user.auth, extra_data)
+60 -9
View File
@@ -3,24 +3,42 @@ notifications views file
""" """
# django imports # django imports
from django.db.models import Q from django.db.models import Q
from rest_framework import viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework import viewsets, status, views
# local imports # local imports
from account.utils import custom_response from account.utils import custom_response, custom_error_response
from base.messages import SUCCESS_CODE from base.messages import SUCCESS_CODE, ERROR_CODE
from notifications.constants import TEST_NOTIFICATION from notifications.constants import TEST_NOTIFICATION
from notifications.serializers import RegisterDevice # Import serializer
from notifications.utils import send_notification from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
from notifications.utils import send_notification, send_notification_to_guardian, send_notification_to_junior
# Import model
from notifications.models import Notification
class NotificationViewSet(viewsets.GenericViewSet): class NotificationViewSet(viewsets.GenericViewSet):
""" used to do the notification actions """ """ used to do the notification actions """
serializer_class = None serializer_class = NotificationListSerializer
permission_classes = [IsAuthenticated, ] permission_classes = [IsAuthenticated, ]
def list(self, request, *args, **kwargs) -> Response:
""" list the notifications """
queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id']
).select_related('notification_to').order_by('-id')
paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
self.mark_notifications_as_read(serializer.data)
return custom_response(None, serializer.data)
@staticmethod
def mark_notifications_as_read(data):
""" used to mark notification queryset as read """
ids = [obj['id'] for obj in data]
Notification.objects.filter(id__in=ids).update(is_read=True)
@action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice) @action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice)
def fcm_registration(self, request): def fcm_registration(self, request):
""" """
@@ -32,11 +50,44 @@ class NotificationViewSet(viewsets.GenericViewSet):
serializer.save() serializer.save()
return custom_response(SUCCESS_CODE["3000"]) return custom_response(SUCCESS_CODE["3000"])
@action(methods=['get'], detail=False, url_path='test', url_name='test', serializer_class=None) @action(methods=['get'], detail=False, url_path='test', url_name='test')
def send_test_notification(self, request): def send_test_notification(self, request):
""" """
to send test notification to send test notification
:return: :return:
""" """
send_notification.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], {}) send_notification_to_guardian.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'],
{'task_id': None})
send_notification_to_junior.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'],
{'task_id': None})
return custom_response(SUCCESS_CODE["3000"]) return custom_response(SUCCESS_CODE["3000"])
@action(methods=['get'], detail=False, url_path='list', url_name='list',
serializer_class=NotificationListSerializer)
def notification_list(self, request):
"""
notification list
"""
try:
queryset = Notification.objects.filter(notification_to=request.user)
serializer = NotificationListSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ReadNotification(views.APIView):
"""Update notification API"""
serializer_class = ReadNotificationSerializer
model = Notification
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
notification_id = self.request.data.get('notification_id')
notification_queryset = Notification.objects.filter(id__in=notification_id,
notification_to=self.request.user).update(is_read=True)
if notification_queryset:
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
+3
View File
@@ -99,3 +99,6 @@ uritemplate==4.1.1
urllib3==1.26.16 urllib3==1.26.16
vine==5.0.0 vine==5.0.0
wcwidth==0.2.6 wcwidth==0.2.6
pandas==2.0.3
XlsxWriter==3.1.2
@@ -0,0 +1,19 @@
# Generated by Django 4.2.2 on 2023-08-01 07:35
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0003_defaultarticlecardimage_and_more'),
]
operations = [
migrations.AlterField(
model_name='surveyoption',
name='survey',
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='web_admin.articlesurvey'),
),
]
+3 -3
View File
@@ -49,14 +49,14 @@ class ArticleSurvey(models.Model):
def __str__(self): def __str__(self):
"""Return title""" """Return title"""
return f'{self.id} | {self.article}' return f'{self.id} | {self.question}'
class SurveyOption(models.Model): class SurveyOption(models.Model):
""" """
Survey Options model Survey Options model
""" """
survey = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='survey_options') survey = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='options')
option = models.CharField(max_length=255) option = models.CharField(max_length=255)
is_answer = models.BooleanField(default=False) is_answer = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
@@ -64,7 +64,7 @@ class SurveyOption(models.Model):
def __str__(self): def __str__(self):
"""Return title""" """Return title"""
return f'{self.id} | {self.survey}' return f'{self.id} | {self.option}'
class DefaultArticleCardImage(models.Model): class DefaultArticleCardImage(models.Model):
+18
View File
@@ -0,0 +1,18 @@
"""
web_admin pagination file
"""
# third party imports
from rest_framework.pagination import PageNumberPagination
from base.constants import NUMBER
class CustomPageNumberPagination(PageNumberPagination):
"""
custom paginator class
"""
# Set the desired page size
page_size = NUMBER['ten']
page_size_query_param = 'page_size'
# Set a maximum page size if needed
max_page_size = NUMBER['hundred']
@@ -0,0 +1,130 @@
"""
web_admin analytics serializer file
"""
# third party imports
from rest_framework import serializers
# django imports
from django.contrib.auth import get_user_model
# local imports
from base.constants import USER_TYPE
from junior.models import JuniorPoints, Junior
USER = get_user_model()
class JuniorLeaderboardSerializer(serializers.ModelSerializer):
"""
junior leaderboard serializer
"""
name = serializers.SerializerMethodField()
first_name = serializers.SerializerMethodField()
last_name = serializers.SerializerMethodField()
class Meta:
"""
meta class
"""
model = Junior
fields = ('id', 'name', 'first_name', 'last_name', 'is_active', 'image', 'is_deleted')
@staticmethod
def get_name(obj):
"""
:param obj: junior object
:return: full name
"""
return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name
@staticmethod
def get_first_name(obj):
"""
:param obj: junior object
:return: first name
"""
return obj.auth.first_name
@staticmethod
def get_last_name(obj):
"""
:param obj: junior object
:return: last name
"""
return obj.auth.last_name
class LeaderboardSerializer(serializers.ModelSerializer):
"""
leaderboard serializer
"""
junior = JuniorLeaderboardSerializer()
rank = serializers.IntegerField()
class Meta:
"""
meta class
"""
model = JuniorPoints
fields = ('total_points', 'rank', 'junior')
class UserCSVReportSerializer(serializers.ModelSerializer):
"""
user csv/xls report serializer
"""
phone_number = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
is_active = serializers.SerializerMethodField()
date_joined = serializers.SerializerMethodField()
class Meta:
"""
meta class
"""
model = USER
fields = ('first_name', 'last_name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined')
@staticmethod
def get_phone_number(obj):
"""
:param obj: user object
:return: user phone number
"""
if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()):
return f"+{profile.country_code}{profile.phone}" \
if profile.country_code and profile.phone else profile.phone
else:
return None
@staticmethod
def get_user_type(obj):
"""
:param obj: user object
:return: user type
"""
if obj.guardian_profile.all().first():
return dict(USER_TYPE).get('2').capitalize()
elif obj.junior_profile.all().first():
return dict(USER_TYPE).get('1').capitalize()
else:
return None
@staticmethod
def get_is_active(obj):
"""
:param obj: user object
:return: user type
"""
if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()):
return "Active" if profile.is_active else "Inactive"
@staticmethod
def get_date_joined(obj):
"""
:param obj: user obj
:return: formatted date
"""
date = obj.date_joined.strftime("%d %b %Y")
return date
+132 -76
View File
@@ -11,8 +11,8 @@ from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE
from base.messages import ERROR_CODE from base.messages import ERROR_CODE
from guardian.utils import upload_image_to_alibaba from guardian.utils import upload_image_to_alibaba
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage
from web_admin.utils import pop_id from web_admin.utils import pop_id, get_image_url
from junior.models import JuniorArticlePoints, JuniorArticle
USER = get_user_model() USER = get_user_model()
@@ -21,23 +21,20 @@ class ArticleCardSerializer(serializers.ModelSerializer):
Article Card serializer Article Card serializer
""" """
id = serializers.IntegerField(required=False) id = serializers.IntegerField(required=False)
image = serializers.FileField(required=False) image_name = serializers.CharField(required=False)
image_url = serializers.URLField(required=False) image_url = serializers.CharField(required=False)
class Meta: class Meta:
""" """
meta class meta class
""" """
model = ArticleCard model = ArticleCard
fields = ('id', 'title', 'description', 'image', 'image_url') fields = ('id', 'title', 'description', 'image_name', 'image_url')
def create(self, validated_data): def create(self, validated_data):
if 'image' in validated_data and validated_data['image'] is not None: validated_data['image_url'] = get_image_url(validated_data)
image = validated_data.pop('image') article = Article.objects.all().first()
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}" article_card = ArticleCard.objects.create(article=article, **validated_data)
# upload image on ali baba
validated_data['image_url'] = upload_image_to_alibaba(image, filename)
article_card = ArticleCard.objects.create(article_id='1', **validated_data)
return article_card return article_card
@@ -60,14 +57,14 @@ class ArticleSurveySerializer(serializers.ModelSerializer):
article survey serializer article survey serializer
""" """
id = serializers.IntegerField(required=False) id = serializers.IntegerField(required=False)
survey_options = SurveyOptionSerializer(many=True) options = SurveyOptionSerializer(many=True)
class Meta: class Meta:
""" """
meta class meta class
""" """
model = ArticleSurvey model = ArticleSurvey
fields = ('id', 'question', 'survey_options') fields = ('id', 'question', 'options')
class ArticleSerializer(serializers.ModelSerializer): class ArticleSerializer(serializers.ModelSerializer):
@@ -111,16 +108,12 @@ class ArticleSerializer(serializers.ModelSerializer):
for card in article_cards: for card in article_cards:
card = pop_id(card) card = pop_id(card)
if 'image' in card and card['image'] is not None: card['image_url'] = get_image_url(card)
image = card.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
card['image_url'] = upload_image_to_alibaba(image, filename)
ArticleCard.objects.create(article=article, **card) ArticleCard.objects.create(article=article, **card)
for survey in article_survey: for survey in article_survey:
survey = pop_id(survey) survey = pop_id(survey)
options = survey.pop('survey_options') options = survey.pop('options')
survey_obj = ArticleSurvey.objects.create(article=article, points=ARTICLE_SURVEY_POINTS, **survey) survey_obj = ArticleSurvey.objects.create(article=article, points=ARTICLE_SURVEY_POINTS, **survey)
for option in options: for option in options:
option = pop_id(option) option = pop_id(option)
@@ -139,40 +132,36 @@ class ArticleSerializer(serializers.ModelSerializer):
instance.title = validated_data.get('title', instance.title) instance.title = validated_data.get('title', instance.title)
instance.description = validated_data.get('description', instance.description) instance.description = validated_data.get('description', instance.description)
instance.save() instance.save()
prev_card = list(ArticleCard.objects.filter(article=instance).values_list('id', flat=True))
# Update or create cards # Update or create cards
for card_data in article_cards: for card_data in article_cards:
card_id = card_data.get('id', None) card_id = card_data.get('id', None)
if card_id: if card_id:
prev_card.remove(card_id)
card = ArticleCard.objects.get(id=card_id, article=instance) card = ArticleCard.objects.get(id=card_id, article=instance)
card.title = card_data.get('title', card.title) card.title = card_data.get('title', card.title)
card.description = card_data.get('description', card.description) card.description = card_data.get('description', card.description)
if 'image' in card_data and card_data['image'] is not None: card.image_url = get_image_url(card_data)
image = card_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
card.image_url = upload_image_to_alibaba(image, filename)
card.save() card.save()
else: else:
card_data = pop_id(card_data) card_data = pop_id(card_data)
if 'image' in card_data and card_data['image'] is not None: card_data['image_url'] = get_image_url(card_data)
image = card_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
card_data['image_url'] = upload_image_to_alibaba(image, filename)
ArticleCard.objects.create(article=instance, **card_data) ArticleCard.objects.create(article=instance, **card_data)
ArticleCard.objects.filter(id__in=prev_card, article=instance).delete()
prev_survey = list(ArticleSurvey.objects.filter(article=instance).values_list('id', flat=True))
# Update or create survey sections # Update or create survey sections
for survey_data in article_survey: for survey_data in article_survey:
survey_id = survey_data.get('id', None) survey_id = survey_data.get('id', None)
options_data = survey_data.pop('survey_options') options_data = survey_data.pop('options')
if survey_id: if survey_id:
prev_survey.remove(survey_id)
survey = ArticleSurvey.objects.get(id=survey_id, article=instance) survey = ArticleSurvey.objects.get(id=survey_id, article=instance)
survey.question = survey_data.get('question', survey.question) survey.question = survey_data.get('question', survey.question)
survey.save() survey.save()
else: else:
survey_data = pop_id(survey_data) survey_data = pop_id(survey_data)
survey = ArticleSurvey.objects.create(article=instance, **survey_data) survey = ArticleSurvey.objects.create(article=instance, points=ARTICLE_SURVEY_POINTS, **survey_data)
# Update or create survey options # Update or create survey options
for option_data in options_data: for option_data in options_data:
@@ -185,6 +174,7 @@ class ArticleSerializer(serializers.ModelSerializer):
else: else:
option_data = pop_id(option_data) option_data = pop_id(option_data)
SurveyOption.objects.create(survey=survey, **option_data) SurveyOption.objects.create(survey=survey, **option_data)
ArticleSurvey.objects.filter(id__in=prev_survey, article=instance).delete()
return instance return instance
@@ -210,6 +200,9 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
""" """
if 'image' not in attrs and attrs.get('image') is None: if 'image' not in attrs and attrs.get('image') is None:
raise serializers.ValidationError({'details': ERROR_CODE['2061']}) raise serializers.ValidationError({'details': ERROR_CODE['2061']})
image = attrs.get('image')
if image and image.size == NUMBER['zero']:
raise serializers.ValidationError(ERROR_CODE['2035'])
return attrs return attrs
def create(self, validated_data): def create(self, validated_data):
@@ -217,62 +210,125 @@ class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
to create and upload image to create and upload image
:return: card_image object :return: card_image object
""" """
image = validated_data.pop('image') validated_data['image_url'] = get_image_url(validated_data)
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
if image and image.size == NUMBER['zero']:
raise serializers.ValidationError(ERROR_CODE['2035'])
# upload image on ali baba
validated_data['image_url'] = upload_image_to_alibaba(image, filename)
card_image = DefaultArticleCardImage.objects.create(**validated_data) card_image = DefaultArticleCardImage.objects.create(**validated_data)
return card_image return card_image
class ArticleListSerializer(serializers.ModelSerializer):
class UserManagementListSerializer(serializers.ModelSerializer):
""" """
user management serializer serializer for article API
""" """
name = serializers.SerializerMethodField() article_cards = ArticleCardSerializer(many=True)
phone_number = serializers.SerializerMethodField() article_survey = ArticleSurveySerializer(many=True)
user_type = serializers.SerializerMethodField() total_points = serializers.SerializerMethodField('get_total_points')
is_completed = serializers.SerializerMethodField('get_is_completed')
class Meta: class Meta(object):
""" """
meta class meta class
""" """
model = USER model = Article
fields = ('name', 'email', 'phone_number', 'user_type', 'is_active') fields = ('id', 'title', 'description', 'article_cards', 'article_survey', 'total_points', 'is_completed')
@staticmethod def get_total_points(self, obj):
def get_name(obj): """total points of article"""
""" total_question = ArticleSurvey.objects.filter(article=obj).count()
:param obj: user object return total_question * NUMBER['five']
:return: full name
"""
return (obj.first_name + obj.last_name) if obj.last_name else obj.first_name
@staticmethod def get_is_completed(self, obj):
def get_phone_number(obj): """complete all question"""
""" context_data = self.context.get('user')
:param obj: user object junior_article = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
:return: user phone number if junior_article:
""" return junior_article.is_completed
if profile := obj.guardian_profile.all().first(): return False
return profile.phone
elif profile := obj.junior_profile.all().first():
return profile.phone
else:
return None
@staticmethod class ArticleQuestionSerializer(serializers.ModelSerializer):
def get_user_type(obj): """
article survey serializer
"""
id = serializers.IntegerField(required=False)
options = SurveyOptionSerializer(many=True)
is_attempt = serializers.SerializerMethodField('get_is_attempt')
correct_answer = serializers.SerializerMethodField('get_correct_answer')
attempted_answer = serializers.SerializerMethodField('get_attempted_answer')
def get_is_attempt(self, obj):
"""attempt question or not"""
context_data = self.context.get('user')
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data, question=obj).last()
if junior_article_obj:
return junior_article_obj.is_attempt
return False
def get_correct_answer(self, obj):
"""attempt question or not"""
ans_obj = SurveyOption.objects.filter(survey=obj, is_answer=True).last()
if ans_obj:
return ans_obj.id
return str("None")
def get_attempted_answer(self, obj):
"""attempt question or not"""
context_data = self.context.get('user')
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data,
question=obj, is_answer_correct=True).last()
if junior_article_obj:
return junior_article_obj.submitted_answer.id
return None
class Meta(object):
""" """
:param obj: user object meta class
:return: user type
""" """
if obj.guardian_profile.all().first(): model = ArticleSurvey
return dict(USER_TYPE).get('2') fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer')
elif obj.junior_profile.all().first():
return dict(USER_TYPE).get('1') class StartAssessmentSerializer(serializers.ModelSerializer):
else: """
return None serializer for article API
"""
article_survey = ArticleQuestionSerializer(many=True)
current_page = serializers.SerializerMethodField('get_current_page')
def get_current_page(self, obj):
"""current page"""
context_data = self.context.get('user')
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
if data:
return data.current_que_page
return NUMBER['zero']
class Meta(object):
"""
meta class
"""
model = Article
fields = ('article_survey', 'current_page')
class ArticleCardlistSerializer(serializers.ModelSerializer):
"""
Article Card serializer
"""
id = serializers.IntegerField(required=False)
image_name = serializers.CharField(required=False)
image_url = serializers.CharField(required=False)
current_page = serializers.SerializerMethodField('get_current_page')
def get_current_page(self, obj):
"""current page"""
context_data = self.context.get('user')
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj.article).last()
if data:
return data.current_card_page
return NUMBER['zero']
class Meta(object):
"""
meta class
"""
model = ArticleCard
fields = ('id', 'title', 'description', 'image_name', 'image_url', 'current_page')
+87 -41
View File
@@ -1,88 +1,134 @@
""" """
web_admin auth serializers file web_admin auth serializers file
""" """
# python imports
from datetime import datetime from datetime import datetime
# django imports # django imports
from rest_framework import serializers from rest_framework import serializers
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from django.conf import settings
from django.utils import timezone from django.utils import timezone
from rest_framework import status
from templated_email import send_templated_mail
# local imports # local imports
from account.models import UserEmailOtp from account.models import UserEmailOtp
from account.utils import custom_error_response from base.constants import USER_TYPE
from base.messages import ERROR_CODE from base.messages import ERROR_CODE
from guardian.tasks import generate_otp from guardian.tasks import generate_otp
from base.tasks import send_email_otp
USER = get_user_model() USER = get_user_model()
class AdminForgotPasswordSerializer(serializers.ModelSerializer): class AdminOTPSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True) """
admin forgot password serializer
"""
email = serializers.EmailField()
class Meta: class Meta:
"""
meta class
"""
model = USER model = USER
fields = ('email',) fields = ('email',)
def validate(self, attrs): def validate(self, attrs):
""" used to validate the incoming data """ """ used to validate the incoming data """
user = USER.objects.filter(email=attrs['email']).first() user = USER.objects.filter(email=attrs.get('email')).first()
if not user: if not user:
raise serializers.ValidationError(ERROR_CODE['2004']) raise serializers.ValidationError({'details': ERROR_CODE['2004']})
elif not user.is_superuser: elif not user.is_superuser:
raise serializers.ValidationError(ERROR_CODE['2036']) raise serializers.ValidationError({'details': ERROR_CODE['2063']})
attrs.update({'user': user}) attrs.update({'user': user})
return attrs return attrs
def create(self, validated_data): def create(self, validated_data):
"""
to send otp
:return: user_data
"""
email = validated_data['email'] email = validated_data['email']
try:
USER.objects.get(email=email)
except USER.DoesNotExist:
return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND)
verification_code = generate_otp() verification_code = generate_otp()
# Send the verification code to the user's email # Send the verification code to the user's email
from_email = settings.EMAIL_FROM_ADDRESS send_email_otp.delay(email, verification_code)
recipient_list = [email]
send_templated_mail(
template_name='email_reset_verification.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'verification_code': verification_code
}
)
expiry = timezone.now() + timezone.timedelta(days=1) expiry = timezone.now() + timezone.timedelta(days=1)
user_data, created = UserEmailOtp.objects.get_or_create(email=email) user_data, created = UserEmailOtp.objects.update_or_create(email=email,
if created: defaults={
user_data.expired_at = expiry "otp": verification_code,
if user_data: "expired_at": expiry,
user_data.otp = verification_code "user_type": dict(USER_TYPE).get('3'),
user_data.expired_at = expiry })
user_data.save()
return user_data return user_data
class AdminVerifyOTPSerializer(serializers.Serializer): class AdminVerifyOTPSerializer(serializers.Serializer):
"""
admin verify otp serializer
"""
email = serializers.EmailField()
otp = serializers.CharField(max_length=6, min_length=6) otp = serializers.CharField(max_length=6, min_length=6)
class Meta: class Meta:
""" meta class """ """ meta class """
fields = ('otp',) fields = ('email', 'otp',)
def validate(self, attrs): def validate(self, attrs):
otp = attrs.pop('otp') """
to validate data
:return: validated data
"""
email = attrs.get('email')
otp = attrs.get('otp')
# fetch email otp object of the user # fetch email otp object of the user
user_opt_details = UserEmailOtp.objects.filter(otp=otp).last() user_otp_details = UserEmailOtp.objects.filter(email=email, otp=otp).last()
if not user_opt_details: if not user_otp_details:
raise serializers.ValidationError(ERROR_CODE['2008']) raise serializers.ValidationError({'details': ERROR_CODE['2008']})
if user_opt_details.expired_at.replace(tzinfo=None) < datetime.utcnow(): if user_otp_details.user_type != dict(USER_TYPE).get('3'):
raise serializers.ValidationError(ERROR_CODE['2029']) raise serializers.ValidationError({'details': ERROR_CODE['2008']})
user_details = USER.objects.filter(email=user_opt_details.email).last() if user_otp_details.expired_at.replace(tzinfo=None) < datetime.utcnow():
user_opt_details.delete() raise serializers.ValidationError({'details': ERROR_CODE['2029']})
if user_details: user_otp_details.is_verified = True
return attrs user_otp_details.save()
return attrs
class AdminCreatePasswordSerializer(serializers.ModelSerializer):
"""
admin create new password serializer
"""
email = serializers.EmailField()
new_password = serializers.CharField()
confirm_password = serializers.CharField()
class Meta:
"""
meta class
"""
model = USER
fields = ('email', 'new_password', 'confirm_password')
def validate(self, attrs):
"""
to validate data
:return: validated data
"""
email = attrs.get('email')
new_password = attrs.get('new_password')
confirm_password = attrs.get('confirm_password')
# matching password
if new_password != confirm_password:
raise serializers.ValidationError({'details': ERROR_CODE['2065']})
user_otp_details = UserEmailOtp.objects.filter(email=email, is_verified=True).last()
if not user_otp_details:
raise serializers.ValidationError({'details': ERROR_CODE['2064']})
if user_otp_details.user_type != dict(USER_TYPE).get('3'):
raise serializers.ValidationError({'details': ERROR_CODE['2063']})
user_otp_details.delete()
return attrs
@@ -0,0 +1,295 @@
"""
web_admin user_management serializers file
"""
# django imports
from rest_framework import serializers
from django.contrib.auth import get_user_model
from base.constants import USER_TYPE, GUARDIAN, JUNIOR
# local imports
from base.messages import ERROR_CODE, SUCCESS_CODE
from guardian.models import Guardian
from junior.models import Junior
USER = get_user_model()
class UserManagementListSerializer(serializers.ModelSerializer):
"""
user management serializer
"""
name = serializers.SerializerMethodField()
country_code = serializers.SerializerMethodField()
phone = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
is_active = serializers.SerializerMethodField()
class Meta:
"""
meta class
"""
model = USER
fields = ('id', 'name', 'email', 'country_code', 'phone', 'user_type', 'is_active')
@staticmethod
def get_name(obj):
"""
:param obj: user object
:return: full name
"""
return f"{obj.first_name} {obj.last_name}" if obj.last_name else obj.first_name
@staticmethod
def get_country_code(obj):
"""
:param obj: user object
:return: user phone number
"""
if profile := obj.guardian_profile.all().first():
return profile.country_code if profile.country_code else None
elif profile := obj.junior_profile.all().first():
return profile.country_code if profile.country_code else None
else:
return None
@staticmethod
def get_phone(obj):
"""
:param obj: user object
:return: user phone number
"""
if profile := obj.guardian_profile.all().first():
return profile.phone if profile.phone else None
elif profile := obj.junior_profile.all().first():
return profile.phone if profile.phone else None
else:
return None
@staticmethod
def get_user_type(obj):
"""
:param obj: user object
:return: user type
"""
if obj.guardian_profile.all().first():
return dict(USER_TYPE).get('2')
elif obj.junior_profile.all().first():
return dict(USER_TYPE).get('1')
else:
return None
@staticmethod
def get_is_active(obj):
"""
:param obj: user object
:return: user type
"""
if profile := obj.guardian_profile.all().first():
return profile.is_active
elif profile := obj.junior_profile.all().first():
return profile.is_active
else:
return obj.is_active
class GuardianSerializer(serializers.ModelSerializer):
"""
guardian serializer
"""
name = serializers.SerializerMethodField()
first_name = serializers.SerializerMethodField()
last_name = serializers.SerializerMethodField()
username = serializers.SerializerMethodField()
email = serializers.EmailField(required=False)
class Meta:
"""
meta class
"""
model = Guardian
fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone',
'is_active', 'country_name', 'image', 'email', 'is_deleted')
def validate(self, attrs):
"""
to validate request data
:return: validated attrs
"""
email = attrs.get('email')
phone = attrs.get('phone')
if USER.objects.filter(email=email).exclude(id=self.context.get('user_id')).exists():
raise serializers.ValidationError({'details': ERROR_CODE['2003']})
if Guardian.objects.filter(phone=phone).exclude(user__id=self.context.get('user_id')).exists():
raise serializers.ValidationError({'details': ERROR_CODE['2012']})
return attrs
def update(self, instance, validated_data):
"""
to update user and its related profile
:param instance: user's guardian object
:param validated_data:
:return: guardian object
"""
instance.user.email = self.validated_data.get('email', instance.user.email)
instance.user.username = self.validated_data.get('email', instance.user.username)
instance.user.save()
instance.country_code = validated_data.get('country_code', instance.country_code)
instance.phone = validated_data.get('phone', instance.phone)
instance.save()
return instance
@staticmethod
def get_name(obj):
"""
:param obj: guardian object
:return: full name
"""
return f"{obj.user.first_name} {obj.user.last_name}" if obj.user.last_name else obj.user.first_name
@staticmethod
def get_first_name(obj):
"""
:param obj: guardian object
:return: first name
"""
return obj.user.first_name
@staticmethod
def get_last_name(obj):
"""
:param obj: guardian object
:return: last name
"""
return obj.user.last_name
@staticmethod
def get_username(obj):
"""
:param obj: guardian object
:return: email
"""
return obj.user.username
class JuniorSerializer(serializers.ModelSerializer):
"""
junior serializer
"""
name = serializers.SerializerMethodField()
first_name = serializers.SerializerMethodField()
last_name = serializers.SerializerMethodField()
username = serializers.SerializerMethodField()
email = serializers.EmailField(required=False)
class Meta:
"""
meta class
"""
model = Junior
fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone',
'is_active', 'country_name', 'image', 'email', 'is_deleted')
def validate(self, attrs):
"""
to validate request data
:return: validated attrs
"""
email = attrs.get('email')
phone = attrs.get('phone')
if email and USER.objects.filter(email=email).exclude(id=self.context.get('user_id')).exists():
raise serializers.ValidationError({'details': ERROR_CODE['2003']})
if phone and Junior.objects.filter(phone=phone).exclude(auth__id=self.context.get('user_id')).exists():
raise serializers.ValidationError({'details': ERROR_CODE['2012']})
return attrs
def update(self, instance, validated_data):
"""
:param instance: user's junior object
:param validated_data: validated data
:return: instance
"""
instance.auth.email = self.validated_data.get('email', instance.auth.email)
instance.auth.username = self.validated_data.get('email', instance.auth.username)
instance.auth.save(update_fields=['email', 'username'])
instance.country_code = validated_data.get('country_code', instance.country_code)
instance.phone = validated_data.get('phone', instance.phone)
instance.save(update_fields=['country_code', 'phone'])
return instance
@staticmethod
def get_name(obj):
"""
:param obj: junior object
:return: full name
"""
return f"{obj.auth.first_name} {obj.auth.last_name}" if obj.auth.last_name else obj.auth.first_name
@staticmethod
def get_first_name(obj):
"""
:param obj: junior object
:return: first name
"""
return obj.auth.first_name
@staticmethod
def get_last_name(obj):
"""
:param obj: junior object
:return: last name
"""
return obj.auth.last_name
@staticmethod
def get_username(obj):
"""
:param obj: junior object
:return: email
"""
return obj.auth.username
class UserManagementDetailSerializer(serializers.ModelSerializer):
"""
user management details serializer
"""
user_type = serializers.SerializerMethodField()
guardian_profile = GuardianSerializer(many=True)
junior_profile = JuniorSerializer(many=True)
associated_users = serializers.SerializerMethodField()
class Meta:
"""
meta class
"""
model = USER
fields = ('id', 'user_type', 'email', 'guardian_profile', 'junior_profile', 'associated_users')
def get_user_type(self, obj):
"""
:param obj: user object
:return: user type
"""
return GUARDIAN if self.context['user_type'] == GUARDIAN else JUNIOR
def get_associated_users(self, obj):
"""
:param obj: user object
:return: associated user
"""
if self.context['user_type'] == GUARDIAN:
profile = obj.guardian_profile.all().only('user_id', 'guardian_code').first()
if profile.guardian_code:
junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code],
is_verified=True).select_related('auth')
serializer = JuniorSerializer(junior, many=True)
return serializer.data
elif self.context['user_type'] == JUNIOR:
profile = obj.junior_profile.all().only('auth_id', 'guardian_code').first()
if profile.guardian_code:
guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code,
is_verified=True).select_related('user')
serializer = GuardianSerializer(guardian, many=True)
return serializer.data
else:
return None
+11 -4
View File
@@ -6,18 +6,25 @@ from django.urls import path, include
from rest_framework import routers from rest_framework import routers
# local imports # local imports
from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet, UserManagementViewSet from web_admin.views.analytics import AnalyticsViewSet
from web_admin.views.auth import ForgetAndResetPasswordViewSet from web_admin.views.article import (ArticleViewSet, DefaultArticleCardImagesViewSet, ArticleListViewSet,
ArticleCardListViewSet)
from web_admin.views.auth import ForgotAndResetPasswordViewSet
from web_admin.views.user_management import UserManagementViewSet
# initiate router # initiate router
router = routers.SimpleRouter() router = routers.SimpleRouter()
router.register('article', ArticleViewSet, basename='article') router.register('article', ArticleViewSet, basename='article')
router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images') router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images')
router.register('user', UserManagementViewSet, basename='user') router.register('user-management', UserManagementViewSet, basename='user')
router.register('analytics', AnalyticsViewSet, basename='user-analytics')
router.register('article-list', ArticleListViewSet, basename='article-list')
router.register('article-card-list', ArticleCardListViewSet, basename='article-card-list')
# forgot and reset password api for admin # forgot and reset password api for admin
router.register('admin', ForgetAndResetPasswordViewSet, basename='admin') router.register('admin', ForgotAndResetPasswordViewSet, basename='admin')
urlpatterns = [ urlpatterns = [
path('api/v1/', include(router.urls)), path('api/v1/', include(router.urls)),
+48
View File
@@ -1,6 +1,11 @@
""" """
web_utils file web_utils file
""" """
import base64
import datetime
from base.constants import ARTICLE_CARD_IMAGE_FOLDER, DATE_FORMAT
from guardian.utils import upload_image_to_alibaba, upload_base64_image_to_alibaba
def pop_id(data): def pop_id(data):
@@ -11,3 +16,46 @@ def pop_id(data):
""" """
data.pop('id') if 'id' in data else data data.pop('id') if 'id' in data else data
return data return data
def get_image_url(data):
"""
to get image url
:param data:
:return: image url
"""
if 'image_url' in data and 'http' in data['image_url']:
if 'image_name' in data:
data.pop('image_name')
return data['image_url']
elif 'image_url' in data and type(data['image_url']) == str and data['image_url'].startswith('data:image'):
base64_image = base64.b64decode(data.get('image_url').split(',')[1])
image_name = data.pop('image_name') if 'image_name' in data else f"{data['title']}.jpg"
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image_name}"
# upload image on ali baba
image_url = upload_base64_image_to_alibaba(base64_image, filename)
return image_url
elif 'image' in data and data['image'] is not None:
image = data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(image, filename)
return image_url
def get_dates(start_date, end_date):
"""
to get start and end date
:param start_date: format (yyyy-mm-dd)
:param end_date: format (yyyy-mm-dd)
:return: start and end date
"""
if start_date and end_date:
start_date = datetime.datetime.strptime(start_date, DATE_FORMAT).date()
end_date = datetime.datetime.strptime(end_date, DATE_FORMAT).date()
else:
end_date = datetime.date.today()
start_date = end_date - datetime.timedelta(days=6)
return start_date, end_date
+237
View File
@@ -0,0 +1,237 @@
"""
web_admin analytics view file
"""
# python imports
import datetime
import io
import pandas as pd
import xlsxwriter
# third party imports
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
# django imports
from django.contrib.auth import get_user_model
from django.db.models import Q
from django.db.models import Count
from django.db.models.functions import TruncDate
from django.db.models import F, Window
from django.db.models.functions.window import Rank
from django.http import HttpResponse
# local imports
from account.utils import custom_response
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED, DATE_FORMAT, TASK_STATUS
from guardian.models import JuniorTask
from guardian.utils import upload_excel_file_to_alibaba
from junior.models import JuniorPoints
from web_admin.pagination import CustomPageNumberPagination
from web_admin.permission import AdminPermission
from web_admin.serializers.analytics_serializer import LeaderboardSerializer, UserCSVReportSerializer
from web_admin.utils import get_dates
USER = get_user_model()
class AnalyticsViewSet(GenericViewSet):
"""
analytics api view
to get user report (active users, guardians and juniors counts)
to get new user sign up report
to get task report (completed, in-progress, requested and rejected tasks count)
to get junior leaderboard and ranking
"""
serializer_class = None
permission_classes = [IsAuthenticated, AdminPermission]
def get_queryset(self):
user_qs = USER.objects.filter(
(Q(junior_profile__is_verified=True) | Q(guardian_profile__is_verified=True)),
is_superuser=False
).prefetch_related('guardian_profile',
'junior_profile'
).exclude(junior_profile__isnull=True,
guardian_profile__isnull=True).order_by('-date_joined')
return user_qs
@action(methods=['get'], url_name='users-count', url_path='users-count', detail=False)
def total_users_count(self, request, *args, **kwargs):
"""
api method to get total users, guardians and juniors
:param request: start_date: date format (yyyy-mm-dd)
:param request: end_date: date format (yyyy-mm-dd)
:return:
"""
start_date, end_date = get_dates(request.query_params.get('start_date'),
request.query_params.get('end_date'))
user_qs = self.get_queryset()
queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1))))
data = {'total_users': queryset.count(),
'total_guardians': queryset.filter(junior_profile__isnull=True).count(),
'total_juniors': queryset.filter(guardian_profile__isnull=True).count()}
return custom_response(None, data)
@action(methods=['get'], url_name='new-signups', url_path='new-signups', detail=False)
def new_signups(self, request, *args, **kwargs):
"""
api method to get new signups
:param request: start_date: date format (yyyy-mm-dd)
:param request: end_date: date format (yyyy-mm-dd)
:return:
"""
start_date, end_date = get_dates(request.query_params.get('start_date'),
request.query_params.get('end_date'))
user_qs = self.get_queryset()
signup_data = user_qs.filter(date_joined__range=[start_date, (end_date + datetime.timedelta(days=1))]
).annotate(date=TruncDate('date_joined')
).values('date').annotate(signups=Count('id')).order_by('date')
return custom_response(None, signup_data)
@action(methods=['get'], url_name='assign-tasks', url_path='assign-tasks', detail=False)
def assign_tasks_report(self, request, *args, **kwargs):
"""
api method to get assign tasks count for (completed, in-progress, requested and rejected) task
:param request: start_date: date format (yyyy-mm-dd)
:param request: end_date: date format (yyyy-mm-dd)
:return:
"""
start_date, end_date = get_dates(request.query_params.get('start_date'),
request.query_params.get('end_date'))
assign_tasks = JuniorTask.objects.filter(
created_at__range=[start_date, (end_date + datetime.timedelta(days=1))]
).exclude(task_status__in=[PENDING, EXPIRED])
data = {
'task_completed': assign_tasks.filter(task_status=COMPLETED).count(),
'task_in_progress': assign_tasks.filter(task_status=IN_PROGRESS).count(),
'task_requested': assign_tasks.filter(task_status=REQUESTED).count(),
'task_rejected': assign_tasks.filter(task_status=REJECTED).count(),
}
return custom_response(None, data)
@action(methods=['get'], url_name='junior-leaderboard', url_path='junior-leaderboard', detail=False,
serializer_class=LeaderboardSerializer)
def junior_leaderboard(self, request):
"""
to get junior leaderboard and rank
:param request:
:return:
"""
queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window(
expression=Rank(),
order_by=[F('total_points').desc(), 'junior__created_at']
)).order_by('-total_points', 'junior__created_at')
paginator = CustomPageNumberPagination()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data)
@action(methods=['get'], url_name='export-excel', url_path='export-excel', detail=False)
def export_excel(self, request):
"""
to export users count, task details and top juniors in csv/excel file
:param request: start_date: date format (yyyy-mm-dd)
:param request: end_date: date format (yyyy-mm-dd)
:return:
"""
response = HttpResponse(content_type='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet')
response['Content-Disposition'] = 'attachment; filename="ZOD_Bank_Analytics.xlsx"'
start_date, end_date = get_dates(request.query_params.get('start_date'),
request.query_params.get('end_date'))
# Use BytesIO for binary data
buffer = io.BytesIO()
# Create an XlsxWriter Workbook object
workbook = xlsxwriter.Workbook(buffer)
# Add sheets
sheets = ['Users', 'Assign Tasks', 'Juniors Leaderboard']
for sheet_name in sheets:
worksheet = workbook.add_worksheet(name=sheet_name)
# sheet 1 for Total Users
if sheet_name == 'Users':
user_qs = self.get_queryset()
queryset = user_qs.filter(date_joined__range=(start_date, (end_date + datetime.timedelta(days=1))))
serializer = UserCSVReportSerializer(queryset, many=True)
df_users = pd.DataFrame([
{'Name': f"{user['first_name']} {user['last_name']}",
'Email': user['email'], 'Phone Number': user['phone_number'],
'User Type': user['user_type'], 'Status': user['is_active'],
'Date Joined': user['date_joined']}
for user in serializer.data
])
write_excel_worksheet(worksheet, df_users)
# sheet 2 for Assign Task
elif sheet_name == 'Assign Tasks':
assign_tasks = JuniorTask.objects.filter(
created_at__range=[start_date, (end_date + datetime.timedelta(days=1))]
).exclude(task_status__in=[PENDING, EXPIRED])
df_tasks = pd.DataFrame([
{'Task Name': task.task_name, 'Task Status': dict(TASK_STATUS).get(task.task_status).capitalize()}
for task in assign_tasks
])
write_excel_worksheet(worksheet, df_tasks)
# sheet 3 for Juniors Leaderboard and rank
elif sheet_name == 'Juniors Leaderboard':
queryset = JuniorPoints.objects.prefetch_related('junior', 'junior__auth').annotate(rank=Window(
expression=Rank(),
order_by=[F('total_points').desc(), 'junior__created_at']
)).order_by('-total_points', 'junior__created_at')[:15]
df_leaderboard = pd.DataFrame([
{
'Junior Name': f"{junior.junior.auth.first_name} {junior.junior.auth.last_name}"
if junior.junior.auth.last_name else junior.junior.auth.first_name,
'Points': junior.total_points,
'Rank': junior.rank
}
for junior in queryset
])
write_excel_worksheet(worksheet, df_leaderboard)
# Close the workbook to save the content
workbook.close()
# Reset the buffer position and write the content to the response
buffer.seek(0)
response.write(buffer.getvalue())
buffer.close()
filename = f"{'analytics'}/{'ZOD_Bank_Analytics.xlsx'}"
file_link = upload_excel_file_to_alibaba(response, filename)
return custom_response(None, file_link)
def write_excel_worksheet(worksheet, dataframe):
"""
to perform write action on worksheets
:param worksheet:
:param dataframe:
:return: worksheet
"""
for idx, col in enumerate(dataframe.columns):
# Write header
worksheet.write(0, idx, col)
for row_num, row in enumerate(dataframe.values, start=1):
for col_num, value in enumerate(row):
worksheet.write(row_num, col_num, value)
return worksheet
+85 -41
View File
@@ -4,7 +4,7 @@ web_admin views file
# django imports # django imports
from rest_framework.viewsets import GenericViewSet, mixins from rest_framework.viewsets import GenericViewSet, mixins
from rest_framework.filters import OrderingFilter, SearchFilter from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework import status from rest_framework import status, viewsets
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny from rest_framework.permissions import IsAuthenticated, AllowAny
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@@ -16,8 +16,8 @@ from base.messages import SUCCESS_CODE, ERROR_CODE
from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticleCardImage from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticleCardImage
from web_admin.permission import AdminPermission from web_admin.permission import AdminPermission
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer, from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer,
DefaultArticleCardImageSerializer, DefaultArticleCardImageSerializer, ArticleListSerializer,
UserManagementListSerializer) ArticleCardlistSerializer)
USER = get_user_model() USER = get_user_model()
@@ -30,13 +30,13 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
serializer_class = ArticleSerializer serializer_class = ArticleSerializer
permission_classes = [IsAuthenticated, AdminPermission] permission_classes = [IsAuthenticated, AdminPermission]
queryset = Article queryset = Article
filter_backends = (OrderingFilter, SearchFilter,) filter_backends = (SearchFilter,)
search_fields = ['title'] search_fields = ['title']
http_method_names = ['get', 'post', 'put', 'delete'] http_method_names = ['get', 'post', 'put', 'delete']
def get_queryset(self): def get_queryset(self):
article = self.queryset.objects.filter(is_deleted=False).prefetch_related( article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
'article_cards', 'article_survey', 'article_survey__survey_options' 'article_cards', 'article_survey', 'article_survey__options'
).order_by('-created_at') ).order_by('-created_at')
queryset = self.filter_queryset(article) queryset = self.filter_queryset(article)
return queryset return queryset
@@ -44,9 +44,20 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
""" """
article create api method article create api method
:param request: :param request: { "title": "string", "description": "string",
:param args: "article_cards": [
:param kwargs: { "title": "string",
"description": "string",
"image_name": "string",
"image_url": "string"
} ],
"article_survey": [
{ "question": "string",
"options": [
{ "option": "string",
"is_answer": true }
] }
] }
:return: success message :return: success message
""" """
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
@@ -57,9 +68,24 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
def update(self, request, *args, **kwargs): def update(self, request, *args, **kwargs):
""" """
article update api method article update api method
:param request: :param request: article_id,
:param args: { "title": "string", "description": "string",
:param kwargs: "article_cards": [
{ "id": 0,
"title": "string",
"description": "string",
"image_name": "string",
"image_url": "string"
} ],
"article_survey": [
{ "id": 0,
"question": "string",
"options": [
{ "id": 0,
"option": "string",
"is_answer": true
} ]
} ] }
:return: success message :return: success message
""" """
article = self.get_object() article = self.get_object()
@@ -72,22 +98,19 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
""" """
article list api method article list api method
:param request: :param request:
:param args:
:param kwargs:
:return: list of article :return: list of article
""" """
queryset = self.get_queryset() queryset = self.get_queryset()
count = queryset.count()
paginator = self.pagination_class() paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request) paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True) serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, data=serializer.data) return custom_response(None, data=serializer.data, count=count)
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
""" """
article detail api method article detail api method
:param request: :param request: article_id
:param args:
:param kwargs:
:return: article detail data :return: article detail data
""" """
queryset = self.get_object() queryset = self.get_object()
@@ -97,9 +120,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
def destroy(self, request, *args, **kwargs): def destroy(self, request, *args, **kwargs):
""" """
article delete (soft delete) api method article delete (soft delete) api method
:param request: :param request: article_id
:param args:
:param kwargs:
:return: success message :return: success message
""" """
article = self.get_object() article = self.get_object()
@@ -109,8 +130,8 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
return custom_response(SUCCESS_CODE["3029"]) return custom_response(SUCCESS_CODE["3029"])
return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST)
@action(methods=['get'], url_name='remove_card', url_path='remove_card', @action(methods=['get'], url_name='remove-card', url_path='remove-card',
detail=True, serializer_class=None) detail=True)
def remove_article_card(self, request, *args, **kwargs): def remove_article_card(self, request, *args, **kwargs):
""" """
article card remove (delete) api method article card remove (delete) api method
@@ -125,8 +146,8 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
except AttributeError: except AttributeError:
return custom_error_response(ERROR_CODE["2042"], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE["2042"], response_status=status.HTTP_400_BAD_REQUEST)
@action(methods=['get'], url_name='remove_survey', url_path='remove_survey', @action(methods=['get'], url_name='remove-survey', url_path='remove-survey',
detail=True, serializer_class=None) detail=True)
def remove_article_survey(self, request, *args, **kwargs): def remove_article_survey(self, request, *args, **kwargs):
""" """
article survey remove (delete) api method article survey remove (delete) api method
@@ -170,13 +191,16 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m
api to upload and list default article card images api to upload and list default article card images
""" """
serializer_class = DefaultArticleCardImageSerializer serializer_class = DefaultArticleCardImageSerializer
# permission_classes = [IsAuthenticated, AdminPermission] permission_classes = [IsAuthenticated, AdminPermission]
queryset = DefaultArticleCardImage.objects.all() queryset = DefaultArticleCardImage.objects.all()
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
""" """
api method to upload default article card images api method to upload default article card images
:param request: :param request: {
"image_name": "string",
"image": "image_file"
}
:return: success message :return: success message
""" """
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
@@ -195,30 +219,50 @@ class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, m
return custom_response(None, data=serializer.data) return custom_response(None, data=serializer.data)
class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin): class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin):
""" """
api to manage (list, view, edit) user article api
""" """
serializer_class = UserManagementListSerializer serializer_class = ArticleListSerializer
permission_classes = [] permission_classes = [IsAuthenticated,]
queryset = USER.objects.prefetch_related( queryset = Article
'guardian_profile', 'junior_profile') http_method_names = ['get',]
def get_queryset(self): def get_queryset(self):
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'): article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
return self.queryset.filter(junior_profile__isnull=True) 'article_cards', 'article_survey', 'article_survey__options'
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'): ).order_by('-created_at')
return self.queryset.filter(guardian_profile__isnull=True) queryset = self.filter_queryset(article)
else: return queryset
return self.queryset
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
""" """
api method to list all the user article list api method
:param request: :param request:
:return: :param args:
:param kwargs:
:return: list of article
""" """
queryset = self.get_queryset() queryset = self.get_queryset()
serializer = self.serializer_class(queryset, many=True) serializer = self.serializer_class(queryset, context={"user": request.user}, many=True)
return custom_response(None, data=serializer.data) return custom_response(None, data=serializer.data)
class ArticleCardListViewSet(viewsets.ModelViewSet):
"""Junior Points viewset"""
serializer_class = ArticleCardlistSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
"""get queryset"""
return ArticleCard.objects.filter(article=self.request.GET.get('article_id'))
def list(self, request, *args, **kwargs):
"""profile view"""
try:
queryset = self.get_queryset()
# article card list
serializer = ArticleCardlistSerializer(queryset, context={"user": self.request.user}, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
+43 -20
View File
@@ -2,42 +2,65 @@
web_admin auth views file web_admin auth views file
""" """
# django imports # django imports
from rest_framework.viewsets import GenericViewSet, mixins from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action from rest_framework.decorators import action
from rest_framework import status
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
# local imports # local imports
from account.utils import custom_response from account.utils import custom_response, custom_error_response
from base.constants import USER_TYPE
from base.messages import SUCCESS_CODE, ERROR_CODE from base.messages import SUCCESS_CODE, ERROR_CODE
from web_admin.permission import AdminPermission from web_admin.serializers.auth_serializer import (AdminOTPSerializer, AdminVerifyOTPSerializer,
from web_admin.serializers.auth_serializer import AdminForgotPasswordSerializer, AdminVerifyOTPSerializer AdminCreatePasswordSerializer)
USER = get_user_model() USER = get_user_model()
class ForgetAndResetPasswordViewSet(GenericViewSet): class ForgotAndResetPasswordViewSet(GenericViewSet):
"""
to reset admin password
"""
queryset = None queryset = None
@action(methods=['post'], url_name='forgot-password', url_path='forgot-password', @action(methods=['post'], url_name='otp', url_path='otp',
detail=False, serializer_class=AdminForgotPasswordSerializer) detail=False, serializer_class=AdminOTPSerializer)
def admin_forgot_password(self, request): def admin_otp(self, request):
"""
api method to send otp
:param request: {"email": "string"}
:return: success message
"""
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True) if serializer.is_valid():
serializer.save() serializer.save()
return custom_response(SUCCESS_CODE['3015']) return custom_response(SUCCESS_CODE['3015'])
return custom_error_response(ERROR_CODE['2000'], status.HTTP_400_BAD_REQUEST)
@action(methods=['post'], url_name='verify-otp', url_path='verify-otp', @action(methods=['post'], url_name='verify-otp', url_path='verify-otp',
detail=False, serializer_class=AdminVerifyOTPSerializer) detail=False, serializer_class=AdminVerifyOTPSerializer)
def admin_verify_otp(self, request): def admin_verify_otp(self, request):
"""
api method to verify otp
:param request: {"email": "string", "otp": "otp"}
:return: success message
"""
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True) if serializer.is_valid():
return custom_response(SUCCESS_CODE['3011']) return custom_response(SUCCESS_CODE['3011'])
return custom_error_response(ERROR_CODE['2008'], status.HTTP_400_BAD_REQUEST)
@action(methods=['post'], url_name='resend-otp', url_path='resend-otp', @action(methods=['post'], url_name='create-password', url_path='create-password',
detail=False, serializer_class=AdminForgotPasswordSerializer) detail=False, serializer_class=AdminCreatePasswordSerializer)
def admin_resend_otp(self, request): def admin_create_password(self, request):
"""
api method to create new password
:param request: {"email": "string", "new_password": "string", "confirm_password": "string"}
:return: success message
"""
serializer = self.serializer_class(data=request.data) serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True) if serializer.is_valid():
serializer.save() user = USER.objects.filter(email=serializer.validated_data.get('email')).first()
return custom_response(SUCCESS_CODE['3015']) user.set_password(serializer.validated_data.get('new_password'))
user.save()
return custom_response(SUCCESS_CODE['3006'])
return custom_error_response(ERROR_CODE['2064'], status.HTTP_400_BAD_REQUEST)
+129
View File
@@ -0,0 +1,129 @@
"""
web_admin user_management views file
"""
# django imports
from rest_framework.viewsets import GenericViewSet, mixins
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny
from django.contrib.auth import get_user_model
from django.db.models import Q
# local imports
from account.utils import custom_response, custom_error_response
from base.constants import USER_TYPE
from base.messages import SUCCESS_CODE, ERROR_CODE
from guardian.models import Guardian
from junior.models import Junior
from web_admin.permission import AdminPermission
from web_admin.serializers.user_management_serializer import (UserManagementListSerializer,
UserManagementDetailSerializer, GuardianSerializer,
JuniorSerializer)
USER = get_user_model()
class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin,
mixins.RetrieveModelMixin, mixins.UpdateModelMixin):
"""
api to manage (list, view, edit) user
"""
serializer_class = UserManagementListSerializer
permission_classes = [IsAuthenticated, AdminPermission]
queryset = USER.objects.filter(
(Q(junior_profile__is_verified=True) | Q(guardian_profile__is_verified=True)),
is_superuser=False).prefetch_related('guardian_profile',
'junior_profile'
).exclude(junior_profile__isnull=True,
guardian_profile__isnull=True).order_by('-date_joined')
filter_backends = (SearchFilter,)
search_fields = ['first_name', 'last_name']
http_method_names = ['get', 'post', 'patch']
def get_queryset(self):
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
queryset = self.queryset.filter(junior_profile__isnull=True)
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
queryset = self.queryset.filter(guardian_profile__isnull=True)
else:
queryset = self.queryset
queryset = self.filter_queryset(queryset)
return queryset
def list(self, request, *args, **kwargs):
"""
api method to list all the user
:param request: user_type {'guardian' for Guardian list, 'junior' for Junior list}
:return:
"""
queryset = self.get_queryset()
paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, data=serializer.data, count=queryset.count())
def retrieve(self, request, *args, **kwargs):
"""
to get details of single user
:param request: user_id along with
user_type {'guardian' for Guardian, 'junior' for Junior} mandatory
:return: user details
"""
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]:
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
queryset = self.queryset
queryset = queryset.filter(id=kwargs['pk'])
serializer = UserManagementDetailSerializer(
queryset, many=True,
context={'user_type': self.request.query_params.get('user_type')}
)
return custom_response(None, data=serializer.data)
def partial_update(self, request, *args, **kwargs):
"""
api method to update user detail (email, phone number)
:param request: user_id along with
user_type {'guardian' for Guardian, 'junior' for Junior} mandatory
:return: success message
"""
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]:
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
guardian = Guardian.objects.filter(user_id=kwargs['pk'], is_verified=True).first()
serializer = GuardianSerializer(guardian,
request.data, context={'user_id': kwargs['pk']})
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
junior = Junior.objects.filter(auth_id=kwargs['pk'], is_verified=True).select_related('auth').first()
serializer = JuniorSerializer(junior,
request.data, context={'user_id': kwargs['pk']})
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE['3037'])
@action(methods=['get'], url_name='change-status', url_path='change-status', detail=True)
def change_status(self, request, *args, **kwargs):
"""
api to change user status (mark as active or inactive)
:param request: user_id along with
user_type {'guardian' for Guardian, 'junior' for Junior} mandatory
:return: success message
"""
if self.request.query_params.get('user_type') not in [dict(USER_TYPE).get('1'), dict(USER_TYPE).get('2')]:
return custom_error_response(ERROR_CODE['2067'], status.HTTP_400_BAD_REQUEST)
queryset = self.queryset
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
user_obj = queryset.filter(id=kwargs['pk']).first()
obj = user_obj.guardian_profile.all().first()
obj.is_active = False if obj.is_active else True
obj.save()
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
user_obj = queryset.filter(id=kwargs['pk']).first()
obj = user_obj.junior_profile.all().first()
obj.is_active = False if obj.is_active else True
obj.save()
return custom_response(SUCCESS_CODE['3038'])
+33
View File
@@ -125,6 +125,7 @@ SIMPLE_JWT = {
# Database # Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases # https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = { DATABASES = {
# default db setting
'default': { 'default': {
'ENGINE': 'django.contrib.gis.db.backends.postgis', 'ENGINE': 'django.contrib.gis.db.backends.postgis',
'NAME':os.getenv('DB_NAME'), 'NAME':os.getenv('DB_NAME'),
@@ -177,6 +178,35 @@ AUTH_PASSWORD_VALIDATORS = [
}, },
] ]
# database query logs settings
# Allows us to check db hits
# useful to optimize db query and hit
LOGGING = {
"version": 1,
"filters": {
"require_debug_true": {
"()": "django.utils.log.RequireDebugTrue"
}
},
"handlers": {
"console": {
"level": "DEBUG",
"filters": [
"require_debug_true"
],
"class": "logging.StreamHandler"
}
},
# database logger
"loggers": {
"django.db.backends": {
"level": "DEBUG",
"handlers": [
"console"
]
}
}
}
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/ # https://docs.djangoproject.com/en/3.0/topics/i18n/
@@ -217,6 +247,7 @@ CORS_ALLOW_HEADERS = (
'x-requested-with', 'x-requested-with',
) )
# CORS header settings
CORS_EXPOSE_HEADERS = ( CORS_EXPOSE_HEADERS = (
'Access-Control-Allow-Origin: *', 'Access-Control-Allow-Origin: *',
) )
@@ -272,5 +303,7 @@ STATIC_URL = 'static/'
# define static root # define static root
STATIC_ROOT = 'static' STATIC_ROOT = 'static'
# media url
MEDIA_URL = "/media/" MEDIA_URL = "/media/"
# media path
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'media') MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'media')