Compare commits

..

215 Commits

Author SHA1 Message Date
d5c30f2029 added pagination in leaderboard admin 2023-08-11 15:56:22 +05:30
18cb885410 Merge pull request #193 from KiwiTechLLC/ZBKADM-71
leaderboard ranking
2023-08-11 14:13:05 +05:30
4d7209df19 Merge pull request #194 from KiwiTechLLC/sprint4
remove deleted user from leaderboard
2023-08-11 13:40:48 +05:30
94c76b7f2e remove deleted user from leaderboard 2023-08-11 13:39:25 +05:30
af8ea39200 leaderboard ranking 2023-08-11 13:37:44 +05:30
6e9304b2cc Merge pull request #192 from KiwiTechLLC/sprint4
is complete article key
2023-08-11 13:04:54 +05:30
5eab8f4907 is complete article key 2023-08-11 13:04:05 +05:30
f750487b88 Merge pull request #190 from KiwiTechLLC/sprint4
add correct and attempt answer
2023-08-11 12:21:47 +05:30
8b0032f6d2 junior point table 2023-08-11 12:18:35 +05:30
bb65f81200 add correct and attempt answer 2023-08-11 11:48:02 +05:30
04db40e100 Merge pull request #189 from KiwiTechLLC/sprint4
create article
2023-08-10 21:24:15 +05:30
1e162255d7 create article 2023-08-10 20:17:24 +05:30
a479f3cb62 Merge pull request #187 from KiwiTechLLC/sprint4
task points
2023-08-10 19:06:32 +05:30
fbd6f9ece5 task points 2023-08-10 19:05:33 +05:30
11c84208b7 task points 2023-08-10 19:00:37 +05:30
80dffb7942 Merge pull request #184 from KiwiTechLLC/ZBKADM-71
image name fix, remove '|' character
2023-08-10 18:21:10 +05:30
0a8c2cf56d image name fix, remove '|' character 2023-08-10 18:17:49 +05:30
03fb4f4176 Merge pull request #182 from KiwiTechLLC/sprint4
current page update in article api
2023-08-10 15:51:09 +05:30
22afe7e555 current page update in article api 2023-08-10 15:43:31 +05:30
69723b362f current page update in article api 2023-08-10 15:30:46 +05:30
9e612c7171 Merge pull request #181 from KiwiTechLLC/sprint4
junior points
2023-08-10 12:20:34 +05:30
7a9be0326a junior points 2023-08-10 12:19:08 +05:30
aaa1c55227 Merge pull request #180 from KiwiTechLLC/sprint4
guardian code
2023-08-10 12:07:49 +05:30
656f0da89a guardian code 2023-08-10 12:06:52 +05:30
28e71e132c Merge pull request #178 from KiwiTechLLC/sprint4
Sprint4
2023-08-10 11:56:53 +05:30
b30643299f junior points 2023-08-10 11:56:19 +05:30
b6b70af13f remove guardian code request 2023-08-10 11:02:32 +05:30
22dd7fc10b remove guardian code request 2023-08-10 10:55:33 +05:30
924bc20193 Merge pull request #176 from KiwiTechLLC/sprint4
points
2023-08-09 19:21:50 +05:30
af7582b9e2 points 2023-08-09 19:20:56 +05:30
856d27d803 Merge pull request #175 from KiwiTechLLC/sprint4
points remove from serializer
2023-08-09 18:54:12 +05:30
19f56280e4 points remove from serializer 2023-08-09 18:53:18 +05:30
d3800dbc85 Merge pull request #173 from KiwiTechLLC/sprint4
guardian code update
2023-08-09 18:10:18 +05:30
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
32eaa6c3f2 guardian code update 2023-08-09 17:18:07 +05:30
11ff8fc700 conflict resolved 2023-08-09 17:03:42 +05:30
9c75cb1615 analytics section api, users counts, new signup count, task report 2023-08-09 16:57:19 +05:30
3f8b0e2eb7 Merge pull request #171 from KiwiTechLLC/sprint4
financial learning section api
2023-08-09 16:44:27 +05:30
69ce6857e8 financial learning section api 2023-08-09 16:36:33 +05:30
3efa31efe4 Merge pull request #169 from KiwiTechLLC/sprint4
Sprint4
2023-08-09 14:39:01 +05:30
275b09d2ea Merge branch 'sprint4' of github.com:KiwiTechLLC/ZODBank-Backend into sprint4 2023-08-09 14:38:09 +05:30
3cb0e3ed8b guardian code add in junior list api 2023-08-09 14:37:38 +05:30
823c5ea4c4 Merge pull request #168 from KiwiTechLLC/ZBKADM-69
otp, verify otp and login bug resolved
2023-08-09 14:21:58 +05:30
c9ee482512 otp, verify otp and login bug resolved 2023-08-09 14:16:20 +05:30
08750813ce Merge pull request #167 from KiwiTechLLC/sprint4
Sprint4
2023-08-09 13:54:08 +05:30
75f4f66285 Merge branch 'dev' into sprint4 2023-08-09 13:49:39 +05:30
881bda739b article card api, check answer api 2023-08-09 13:48:02 +05:30
d86f082e58 article card api, check answer api 2023-08-09 13:37:06 +05:30
b12c5071fe Merge pull request #164 from KiwiTechLLC/sprint4
changes in add junior api
2023-08-08 16:37:08 +05:30
f75201b3dd changes in add junior api 2023-08-08 16:36:03 +05:30
45ea7013f8 Merge pull request #162 from KiwiTechLLC/sprint4
junior list api
2023-08-08 16:06:52 +05:30
7d428f5eb5 junior list api 2023-08-08 16:05:56 +05:30
9ff64e64ef Merge pull request #160 from KiwiTechLLC/sprint4
top junior api
2023-08-08 15:04:03 +05:30
4b23394569 top junior api 2023-08-08 15:03:24 +05:30
81893c8841 Merge pull request #159 from KiwiTechLLC/ZBKADM-69
change in admin login
2023-08-08 14:38:43 +05:30
10a1ea9b76 change in admin login 2023-08-08 14:29:57 +05:30
7fca493d0d Merge pull request #157 from KiwiTechLLC/sprint4
junior position changes and decrease api response time
2023-08-08 14:02:27 +05:30
85e4ae8761 junior position changes and decrease api response time 2023-08-08 13:54:04 +05:30
6ebd5ae410 Merge pull request #156 from KiwiTechLLC/sprint4
guardian list api changes
2023-08-08 12:35:43 +05:30
f57b111555 guardian list api changes 2023-08-08 11:48:53 +05:30
6596414675 guardian list api changes 2023-08-08 11:46:27 +05:30
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
8cf7148114 Merge pull request #154 from KiwiTechLLC/ZBKADM-69
change method to get real time
2023-08-07 19:19:00 +05:30
b98443479f Merge pull request #153 from KiwiTechLLC/sprint4
junior article points table
2023-08-07 19:12:55 +05:30
f917244265 junior article points table 2023-08-07 19:12:14 +05:30
ceb5bc13c3 Merge pull request #152 from KiwiTechLLC/ZBKADM-69
task list api modified
2023-08-07 18:22:20 +05:30
ae0fc4fe8d article list api 2023-08-07 17:06:31 +05:30
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
f2cf1488e9 changes in edit api, included first name, last name and username 2023-08-07 15:12:54 +05:30
0426974539 Merge pull request #150 from KiwiTechLLC/sprint4
guardian list api
2023-08-07 13:44:20 +05:30
b0e26f41b9 Merge branch 'dev' into sprint4 2023-08-07 13:43:49 +05:30
c20249730a guardian list api 2023-08-07 13:42:47 +05:30
b3b499e661 guardian list api 2023-08-07 13:41:33 +05:30
f377e283fd guardian list api 2023-08-07 13:25:01 +05:30
648a2ec4d5 Merge pull request #149 from KiwiTechLLC/ZBKADM-69
admin login api modified
2023-08-07 12:13:51 +05:30
83ec922584 admin login api modified 2023-08-07 12:10:36 +05:30
88221ec77a notifications API 2023-08-07 11:28:34 +05:30
e853346910 notifications API 2023-08-07 11:26:23 +05:30
a088764b7b Merge pull request #148 from KiwiTechLLC/ZBKADM-69
edit user validations, active-inactive api
2023-08-07 11:04:20 +05:30
4a2f36cde8 edit user validations, active-inactive api 2023-08-07 11:01:37 +05:30
401ee1ddf8 Merge pull request #147 from KiwiTechLLC/ZBKADM-69
Zbkadm 69
2023-08-04 16:46:05 +05:30
baacb1a18f user edit api 2023-08-04 16:37:46 +05:30
5f1c645e3a user list api 2023-08-04 16:27:46 +05:30
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
756bea0471 user detail view api, searching user list, count 2023-08-04 13:15:32 +05:30
465632f519 Merge pull request #144 from KiwiTechLLC/ZBKADM-69
user detail api, changed image upload method
2023-08-03 18:38:53 +05:30
b9e2d9bc8a user detail api, changed image upload method 2023-08-03 18:35:19 +05:30
d82c8cd4ae Merge pull request #143 from KiwiTechLLC/ZBKADM-69
user detail api, changed image upload method
2023-08-03 18:24:56 +05:30
dd0f2b027a task list api modified 2023-08-03 18:17:01 +05:30
3806d1f3a6 user detail api, changed image upload method 2023-08-03 18:14:54 +05:30
f3e2ab9a34 notification list modified and mark as read method changed 2023-08-02 20:45:46 +05:30
685f627707 change method to get real time 2023-08-02 19:16:29 +05:30
b2d172eae5 Merge pull request #141 from KiwiTechLLC/sprint4
migration file
2023-08-02 16:58:24 +05:30
404825dc85 migration file 2023-08-02 16:57:40 +05:30
6e6f0a55d0 Merge pull request #140 from KiwiTechLLC/sprint4
changes in remove guardian code api
2023-08-02 16:26:56 +05:30
75d0b12008 changes in remove guardian code api 2023-08-02 16:26:20 +05:30
2a4011ca5d Merge pull request #139 from KiwiTechLLC/sprint4
sprint4 reassign task after expired
2023-08-02 15:21:38 +05:30
ebb468166e sprint4 reassign task after expired 2023-08-02 15:20:43 +05:30
06176912ee Merge pull request #137 from KiwiTechLLC/sprint4
change junior task list api response
2023-08-02 14:24:38 +05:30
ed8fc156ac change junior task list api response 2023-08-02 14:23:36 +05:30
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
af121f5a53 changes in article update api and get image url method 2023-08-02 11:16:12 +05:30
4c3aa03e13 Merge pull request #134 from KiwiTechLLC/sprint4
api response time
2023-08-02 11:00:42 +05:30
f3a8b52617 api response time 2023-08-02 11:00:02 +05:30
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
d3564efbb9 change in update api, added method to upload and get image url 2023-08-01 20:15:01 +05:30
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
ee92c98f34 article list api changes, changed related name for survey_options to options 2023-08-01 13:14:22 +05:30
fbce04b97e Merge pull request #129 from KiwiTechLLC/ZBKADM-67
fixed add junior api
2023-07-28 19:30:39 +05:30
afaed69eec fixed add junior api 2023-07-28 19:19:59 +05:30
3c0eaf676e Merge pull request #128 from KiwiTechLLC/ZBKADM-67
fixed add junior api
2023-07-28 19:00:41 +05:30
6ffaa66a4d fixed add junior api 2023-07-28 18:57:11 +05:30
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
d68f24543d changes in approve junior and add juinior api, added search in junior list api 2023-07-28 17:32:02 +05:30
3534561697 Merge pull request #126 from KiwiTechLLC/ZBKADM-67
some changes in artcle api
2023-07-28 16:50:26 +05:30
de774111c0 some changes in artcle api 2023-07-28 16:08:40 +05:30
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
151a177e76 some changes in admin password reset related api 2023-07-27 19:29:12 +05:30
73a62186a7 Merge pull request #122 from KiwiTechLLC/ZBKADM-67
added request status in expired task cronjob
2023-07-27 15:32:17 +05:30
83c66ab3b6 added request status in expired task cronjob 2023-07-27 15:31:25 +05:30
94e53e1dcd Merge pull request #121 from KiwiTechLLC/ZBKADM-67
added task to send otp on email
2023-07-27 13:12:36 +05:30
68b77ef766 added task to send otp on email 2023-07-27 12:59:41 +05:30
a2000459d3 Merge pull request #119 from KiwiTechLLC/ZBKADM-67
some changes in forgot password api
2023-07-27 11:06:09 +05:30
c079f3ceca some changes in forgot password api 2023-07-27 10:59:10 +05:30
671cdfc27b Merge pull request #118 from KiwiTechLLC/ZBKADM-67
change password api
2023-07-26 17:51:32 +05:30
a83e27b12a Merge branch 'dev' into ZBKADM-67 2023-07-26 17:45:22 +05:30
87aa1af02b change password api 2023-07-26 17:16:36 +05:30
5f3026f08f Merge pull request #116 from KiwiTechLLC/sprint3
jira-28 notification
2023-07-26 16:51:46 +05:30
a323ad7df7 jira-28 notification 2023-07-26 16:42:26 +05:30
2b6e943d8c jira-28 notification 2023-07-26 16:38:36 +05:30
bca0d8d49b Merge pull request #114 from KiwiTechLLC/sprint3
requirement .txt file add
2023-07-26 11:08:02 +05:30
b860d937f5 requirement .txt file add 2023-07-26 11:06:58 +05:30
11b10bf751 Merge pull request #112 from KiwiTechLLC/ZBKADM-67
added api for forgot password, verify otp and resend otp for admin
2023-07-25 19:57:49 +05:30
d0ef4391cc conflict resolved 2023-07-25 19:51:27 +05:30
5a1b419863 Merge pull request #111 from KiwiTechLLC/sprint3
jira-290 cron job for expired task
2023-07-25 19:49:24 +05:30
7bf0e62604 added api for forgot password, verify otp and resend otp for admin 2023-07-25 19:43:14 +05:30
80843502a6 jira-290 cron job for expired task 2023-07-25 19:13:25 +05:30
3b31d08996 Merge pull request #109 from KiwiTechLLC/sprint3
Sprint3
2023-07-25 17:34:44 +05:30
3cdd685ea8 sonar issues 2023-07-25 17:21:18 +05:30
c4152c0773 Merge branch 'sprint3' of github.com:KiwiTechLLC/ZODBank-Backend into sprint3 2023-07-25 17:15:35 +05:30
8a454c3f97 sonar issues 2023-07-25 17:15:16 +05:30
71405780f8 Merge branch 'dev' into sprint3 2023-07-25 17:07:58 +05:30
a8558b5e6e sonar issues 2023-07-25 17:06:26 +05:30
7c4f9b2506 jira-291 start task api 2023-07-25 16:54:48 +05:30
cc3490b059 Merge pull request #108 from KiwiTechLLC/ZBKADM-67
article card folder name change
2023-07-25 15:52:22 +05:30
32476149bc article card folder name change 2023-07-25 15:50:43 +05:30
c57224ad0e Merge pull request #107 from KiwiTechLLC/sprint3
jira-28 invite guardian
2023-07-25 15:29:36 +05:30
c8a8e1149b jira-28 invite guardian 2023-07-25 15:27:29 +05:30
7c69dfd49f Merge pull request #106 from KiwiTechLLC/ZBKADM-67
modified yml file, default article card image upload api,
2023-07-25 14:46:56 +05:30
6dcf045131 Merge branch 'dev' into ZBKADM-67 2023-07-25 14:22:37 +05:30
6e84814117 modified yml file, default article card image upload api, 2023-07-25 14:09:51 +05:30
c587ba322e Merge pull request #105 from KiwiTechLLC/sprint3
start task api
2023-07-25 13:29:32 +05:30
2db0e47e3b resolve conflict 2023-07-25 13:21:20 +05:30
52cdd4b0ba resolve conflict 2023-07-25 13:19:53 +05:30
6be0682913 junior guardian relation table 2023-07-25 13:17:27 +05:30
bfeeb5e41f Merge branch 'dev' into sprint3 2023-07-25 12:56:48 +05:30
bb56fc7a33 try and except in api 2023-07-25 12:53:56 +05:30
e6d45f3bf2 start task api 2023-07-25 12:02:59 +05:30
49b648e86b Merge pull request #103 from KiwiTechLLC/sprint3
Sprint3
2023-07-25 11:33:08 +05:30
79e85fa968 jira-27 invite guardian api 2023-07-25 11:17:57 +05:30
096961976d sonar issues 2023-07-24 15:28:54 +05:30
4d104bab1c Merge pull request #101 from KiwiTechLLC/sprint3
bugs and push notification for create task
2023-07-24 15:19:40 +05:30
ed28117a7f bugs and push notification for create task 2023-07-24 15:10:59 +05:30
0a7c081f63 Merge branch 'qa' into dev 2023-07-24 11:03:59 +05:30
a2b4f3b758 update position of the junior 2023-07-24 11:00:22 +05:30
d86f7d3436 guardian code 2023-07-21 18:38:16 +05:30
a5a309cf5b comment top junior call api 2023-07-21 18:33:18 +05:30
0a5eaa233f add qa top -junior url 2023-07-21 17:33:21 +05:30
b32d4def2d add qa top -junior url 2023-07-21 16:25:21 +05:30
410282036e Merge pull request #99 from KiwiTechLLC/sprint3
add dev url for top junior api
2023-07-21 16:23:07 +05:30
2a225ea79d add dev url for top junior api 2023-07-21 16:19:30 +05:30
65e012dd60 Merge pull request #98 from KiwiTechLLC/sprint3
add print in junior list
2023-07-21 14:49:52 +05:30
8013767d71 add print in junior list 2023-07-21 14:49:38 +05:30
fdd0b88b11 add print in junior list 2023-07-21 14:48:18 +05:30
bb1351c309 Merge pull request #96 from KiwiTechLLC/ZBKBCK-001
article api changes
2023-07-21 12:32:32 +05:30
c15bd2339a Merge pull request #97 from KiwiTechLLC/dev
Dev
2023-07-21 12:07:26 +05:30
b8382e225c Merge pull request #95 from KiwiTechLLC/ZBKBCK-001
added article point
2023-07-20 18:25:23 +05:30
6fe29f0070 Merge pull request #94 from KiwiTechLLC/sprint3
real time changes
2023-07-20 17:13:31 +05:30
96d71d951e real time changes 2023-07-20 17:09:49 +05:30
effad5cddc Merge pull request #93 from KiwiTechLLC/dev
Dev
2023-07-20 13:25:53 +05:30
8c0da25814 Merge branch 'qa' into dev 2023-07-20 13:25:42 +05:30
b032300ab7 Merge pull request #92 from KiwiTechLLC/sprint3
admin login
2023-07-20 12:45:39 +05:30
4b09ff41cb admin login 2023-07-20 12:44:48 +05:30
8762c00dba Merge pull request #90 from KiwiTechLLC/ZDBBCK-001
sign up issue resolved
2023-07-20 12:31:36 +05:30
e2f737c5b4 Merge pull request #91 from KiwiTechLLC/sprint3
Sprint3
2023-07-20 12:29:02 +05:30
0f5dfc2e9a google login chnages 2023-07-20 12:27:22 +05:30
b488b08f3f google login chnages 2023-07-20 12:25:41 +05:30
7102c44055 Merge pull request #89 from KiwiTechLLC/sprint3
Sprint3
2023-07-19 18:05:07 +05:30
8e2d9c44b9 Merge pull request #88 from KiwiTechLLC/ZDBBCK-001
celery settings
2023-07-19 17:59:15 +05:30
fa96364901 Merge branch 'dev' into sprint3 2023-07-19 17:51:58 +05:30
49abf12ce8 jira-34 referral code validation API 2023-07-19 17:50:10 +05:30
9f9926da14 jira-34 referral code validation API 2023-07-19 17:45:20 +05:30
555d8231e7 Merge pull request #87 from KiwiTechLLC/ZDBBCK-001
notification set up and celery configuration
2023-07-19 17:04:06 +05:30
2e3870dc53 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint3 2023-07-19 15:04:04 +05:30
b3f35d271b change access token duration 2023-07-14 12:45:08 +05:30
d3b7b6cd07 Merge pull request #64 from KiwiTechLLC/dev
Dev
2023-07-13 17:33:54 +05:30
f7cfd53594 Merge pull request #62 from KiwiTechLLC/dev
Dev
2023-07-13 16:42:28 +05:30
75bc387bec Merge pull request #60 from KiwiTechLLC/dev
Dev
2023-07-13 15:43:21 +05:30
0c760a58f3 Merge pull request #57 from KiwiTechLLC/dev
Dev
2023-07-13 11:29:55 +05:30
b156e62dc8 Merge pull request #55 from KiwiTechLLC/dev
Dev
2023-07-12 16:53:30 +05:30
b11137bfad changes in files 2023-07-12 16:27:52 +05:30
23fb2a0695 Merge pull request #51 from KiwiTechLLC/dev
Dev
2023-07-11 13:15:46 +05:30
fe8ddd02a5 Merge pull request #49 from KiwiTechLLC/dev
Dev
2023-07-11 12:06:29 +05:30
3dc16ce79c Merge pull request #44 from KiwiTechLLC/dev
Dev
2023-07-09 19:27:19 +05:30
b75756c43a Merge pull request #41 from KiwiTechLLC/dev
Dev
2023-07-07 18:06:40 +05:30
c01c2b4e23 Merge pull request #37 from KiwiTechLLC/dev
Dev
2023-07-06 20:18:01 +05:30
ddfc4e946c Merge branch 'qa' of github.com:KiwiTechLLC/ZODBank-Backend into qa 2023-07-03 12:58:35 +05:30
f2533d9a08 Merge pull request #30 from KiwiTechLLC/dev
textual changes
2023-06-30 17:22:16 +05:30
4e22dd9871 Merge pull request #28 from KiwiTechLLC/dev
jira-6 change country name length
2023-06-30 16:25:39 +05:30
e5b88794ad Merge pull request #26 from KiwiTechLLC/dev
jira-274 user can use same number multiple time
2023-06-30 15:53:50 +05:30
6cbe824d9e jira-13 alibaba bucket name change 2023-06-30 15:24:49 +05:30
df438241ce Merge pull request #24 from KiwiTechLLC/dev
Dev
2023-06-30 11:18:19 +05:30
bb06238623 Merge pull request #21 from KiwiTechLLC/dev
Dev
2023-06-29 21:48:53 +05:30
ada987413b Merge pull request #18 from KiwiTechLLC/dev
Dev
2023-06-29 21:25:18 +05:30
323168b7d0 Merge pull request #11 from KiwiTechLLC/dev
Dev
2023-06-28 11:56:12 +05:30
65 changed files with 3401 additions and 595 deletions

2
.gitignore vendored
View File

@ -21,4 +21,4 @@ static/*
__pycache__/
*.env
ve/*
celerybeat-schedule

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-20 11:38
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0008_userdevicedetails'),
]
operations = [
migrations.AlterField(
model_name='userdevicedetails',
name='device_id',
field=models.CharField(blank=True, max_length=500, null=True),
),
]

View File

@ -152,7 +152,7 @@ class UserDeviceDetails(models.Model):
"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_device_details')
"""Device ID"""
device_id = models.CharField(max_length=500)
device_id = models.CharField(max_length=500, null=True, blank=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)

View File

@ -137,6 +137,36 @@ class ForgotPasswordSerializer(serializers.Serializer):
"""Forget password serializer"""
email = serializers.EmailField()
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', '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):
"""Super admin serializer"""
user_type = serializers.SerializerMethodField('get_user_type')
@ -144,12 +174,12 @@ class SuperUserSerializer(serializers.ModelSerializer):
refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj):
refresh = RefreshToken.for_user(obj.auth)
refresh = RefreshToken.for_user(obj)
access_token = str(refresh.access_token)
return access_token
def get_refresh_token(self, obj):
refresh = RefreshToken.for_user(obj.user)
refresh = RefreshToken.for_user(obj)
refresh_token = str(refresh)
return refresh_token

View File

@ -8,7 +8,7 @@
<tr>
<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;">
Hi {{full_name}},
Hello,
</p>
</td>
</tr>

View File

@ -28,14 +28,15 @@ from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVer
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView)
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView,
AdminLoginViewSet)
"""Router"""
router = routers.SimpleRouter()
"""API End points with router"""
router.register('user', UserLogin, basename='user')
"""super admin login"""
router.register('admin', UserLogin, basename='admin')
router.register('admin', AdminLoginViewSet, basename='admin')
"""google login end point"""
router.register('google-login', GoogleLoginViewSet, basename='admin')
router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp')

View File

@ -22,8 +22,9 @@ from junior.models import Junior
from guardian.models import Guardian
from account.models import UserDelete
from base.messages import ERROR_CODE
from django.utils import timezone
from base.constants import NUMBER
from junior.models import JuniorPoints
# Define delete
# user account condition,
# Define delete
@ -91,7 +92,9 @@ def junior_account_update(user_tb):
junior_data.is_active = False
junior_data.is_verified = False
junior_data.guardian_code = '{}'
junior_data.guardian_code_status = str(NUMBER['one'])
junior_data.save()
JuniorPoints.objects.filter(junior=junior_data).delete()
def guardian_account_update(user_tb):
"""update guardian account after delete the user account"""
@ -138,12 +141,14 @@ def send_support_email(name, sender, subject, message):
}
)
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"""
if not data:
"""when data is 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):
@ -228,3 +233,4 @@ def generate_code(value, user_id):
return code
OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1)

View File

@ -1,7 +1,7 @@
"""Account view """
from notifications.utils import remove_fcm_token
"""Django import"""
# django imports
from datetime import datetime, timedelta
from rest_framework import viewsets, status, views
from rest_framework.decorators import action
@ -18,32 +18,34 @@ import google.auth.transport.requests
from rest_framework import status
import requests
from rest_framework.response import Response
from rest_framework import mixins
from django.conf import settings
"""App Import"""
# local imports
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 account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification
from django.contrib.auth.models import User
"""Account serializer"""
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer,
DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer,
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer)
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer,
AdminLoginSerializer)
from rest_framework_simplejwt.tokens import RefreshToken
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, ZOD, JUN, GRD
from guardian.tasks import generate_otp
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
generate_code)
generate_code, OTP_EXPIRY)
from junior.serializers import JuniorProfileSerializer
from guardian.serializers import GuardianProfileSerializer
class GoogleLoginMixin(object):
"""google login mixin"""
@staticmethod
def google_login(self, request):
def google_login(request):
"""google login function"""
access_token = request.data.get('access_token')
user_type = request.data.get('user_type')
@ -95,6 +97,8 @@ class GoogleLoginMixin(object):
referral_code=generate_code(ZOD, user_obj.id)
)
serializer = JuniorSerializer(junior_query)
position = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_query, position=position)
if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
image=profile_picture,signup_method='2',
@ -145,6 +149,8 @@ class SigninWithApple(views.APIView):
junior_code=generate_code(JUN, user.id),
referral_code=generate_code(ZOD, user.id))
serializer = JuniorSerializer(junior_query)
position = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_query, position=position)
if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
signup_method='3',
@ -228,7 +234,7 @@ class ForgotPasswordAPIView(views.APIView):
'verification_code': verification_code
}
)
expiry = timezone.now() + timezone.timedelta(days=1)
expiry = OTP_EXPIRY
user_data, created = UserEmailOtp.objects.get_or_create(email=email)
if created:
user_data.expired_at = expiry
@ -243,7 +249,6 @@ class ForgotPasswordAPIView(views.APIView):
class SendPhoneOtp(viewsets.ModelViewSet):
"""Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer
def create(self, request, *args, **kwargs):
otp = generate_otp()
@ -260,7 +265,6 @@ class SendPhoneOtp(viewsets.ModelViewSet):
class UserPhoneVerification(viewsets.ModelViewSet):
"""Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer
def list(self, request, *args, **kwargs):
try:
@ -327,29 +331,45 @@ class UserLogin(viewsets.ViewSet):
@action(methods=['post'], detail=False)
def admin_login(self, request):
username = request.data.get('username')
email = request.data.get('email')
password = request.data.get('password')
user = authenticate(request, username=username, password=password)
try:
if user is not None:
login(request, user)
if user.is_superuser:
serializer = SuperUserSerializer(user)
return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED)
except Exception as e:
logging.error(e)
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
data = {"auth_token": access_token, "refresh_token":refresh_token, "user_type": '3'}
return custom_response(None, data, response_status=status.HTTP_200_OK)
user = User.objects.filter(email__iexact=email, is_superuser=True
).only('id', 'first_name', 'last_name', 'email', 'is_superuser').first()
if not user or not user.check_password(password):
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST)
serializer = SuperUserSerializer(user)
return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
class AdminLoginViewSet(viewsets.GenericViewSet):
"""
admin login api
"""
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):
"""User Email verification"""
serializer_class = EmailVerificationSerializer
queryset = UserEmailOtp.objects.all()
def list(self, request, *args, **kwargs):
try:
@ -392,14 +412,13 @@ class UserEmailVerification(viewsets.ModelViewSet):
class ReSendEmailOtp(viewsets.ModelViewSet):
"""Send otp on phone"""
queryset = UserEmailOtp.objects.all()
serializer_class = EmailVerificationSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
otp = generate_otp()
if User.objects.filter(email=request.data['email']):
expiry = timezone.now() + timezone.timedelta(days=1)
expiry = OTP_EXPIRY
email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email'])
if created:
email_data.expired_at = expiry
@ -415,7 +434,6 @@ class ReSendEmailOtp(viewsets.ModelViewSet):
class ProfileAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset"""
queryset = User.objects.all()
serializer_class = JuniorProfileSerializer
permission_classes = [IsAuthenticated]
@ -434,7 +452,6 @@ class ProfileAPIViewSet(viewsets.ModelViewSet):
class UploadImageAPIViewSet(viewsets.ModelViewSet):
"""upload task image"""
queryset = DefaultTaskImages.objects.all()
serializer_class = DefaultTaskImagesSerializer
def create(self, request, *args, **kwargs):
"""upload images"""
@ -453,7 +470,6 @@ class UploadImageAPIViewSet(viewsets.ModelViewSet):
class DefaultImageAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset"""
queryset = DefaultTaskImages.objects.all()
serializer_class = DefaultTaskImagesDetailsSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
@ -484,7 +500,6 @@ class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
class UserNotificationAPIViewSet(viewsets.ModelViewSet):
"""notification viewset"""
queryset = UserNotification.objects.all()
serializer_class = UserNotificationSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
@ -496,7 +511,6 @@ class UserNotificationAPIViewSet(viewsets.ModelViewSet):
class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
"""Update notification viewset"""
queryset = UserNotification.objects.all()
serializer_class = UpdateUserNotificationSerializer
permission_classes = [IsAuthenticated]

View File

@ -61,7 +61,8 @@ TASK_STATUS = (
('2', 'in-progress'),
('3', 'rejected'),
('4', 'requested'),
('5', 'completed')
('5', 'completed'),
('6', 'expired')
)
# sign up method
SIGNUP_METHODS = (
@ -69,6 +70,18 @@ SIGNUP_METHODS = (
('2', 'google'),
('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 = (
('1', 'parent'),
@ -82,6 +95,7 @@ IN_PROGRESS = 2
REJECTED = 3
REQUESTED = 4
COMPLETED = 5
EXPIRED = 6
TASK_POINTS = 5
# duplicate name used defined in constant PROJECT_NAME
PROJECT_NAME = 'Zod Bank'
@ -103,3 +117,8 @@ MAX_ARTICLE_CARD = 6
# min and max survey
MIN_ARTICLE_SURVEY = 5
MAX_ARTICLE_SURVEY = 10
# already register
Already_register_user = "duplicate key value violates unique constraint"
ARTICLE_CARD_IMAGE_FOLDER = 'article-card-images'

View File

@ -23,15 +23,15 @@ ERROR_CODE_REQUIRED = {
# 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",
"2002": "Invalid login credentials.",
"2002": "Invalid username or password.",
"2003": "An account already exists with this email address.",
"2004": "User not found.",
"2005": "Your account has been activated.",
"2006": "Your account is not 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.",
"2010": "Invalid Link.",
"2011": "Your profile has not been completed yet.",
@ -42,7 +42,7 @@ ERROR_CODE = {
"2016": "Invalid search.",
"2017": "{model} object with {pk} does not exist",
"2018": "Attached File not found",
"2019": "Either File extension or File size doesn't meet the requirements",
"2019": "Invalid Referral code",
"2020": "Enter valid mobile number",
"2021": "Already register",
"2022": "Invalid Guardian code",
@ -54,7 +54,7 @@ ERROR_CODE = {
"2026": "New password should not same as old password",
"2027": "data should contain `identityToken`",
"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",
# invalid password
"2031": "Invalid password",
@ -74,7 +74,28 @@ ERROR_CODE = {
"2041": "Article with given id doesn't exist.",
"2042": "Article Card with given id doesn't exist.",
"2043": "Article Survey with given id doesn't exist.",
"2044": "Task does not exist"
"2044": "Task does not exist",
"2045": "Invalid guardian",
# past due date
"2046": "Due date must be future date",
# invalid junior id msg
"2047": "Invalid Junior ID ",
"2048": "Choose right file for image",
# task request
"2049": "This task is already requested ",
"2059": "Already exist junior",
# task status
"2060": "Task does not exist or not in pending state",
"2061": "Please insert image or check the image is valid or not.",
# email not null
"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"
}
"""Success message code"""
SUCCESS_CODE = {
@ -90,7 +111,7 @@ SUCCESS_CODE = {
# Success code for link verified
"3005": "Your account is deleted successfully.",
# 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
"3007": "Your password has been changed successfully.",
# Success code for valid link
@ -103,8 +124,8 @@ SUCCESS_CODE = {
"3012": "Phone OTP Verified successfully",
"3013": "Valid Guardian code",
"3014": "Password has been updated successfully.",
"3015": "Verification code sent on your email.",
"3016": "Send otp on your Email successfully",
"3015": "Verification code has been sent on your email.",
"3016": "An OTP has been sent on your email.",
"3017": "Profile image update successfully",
"3018": "Task created successfully",
"3019": "Support Email sent successfully",
@ -120,7 +141,26 @@ SUCCESS_CODE = {
"3029": "Article has been deleted successfully.",
"3030": "Article Card has been removed successfully.",
"3031": "Article Survey has been removed successfully.",
"3032": "Task request sent successfully"
"3032": "Task request sent successfully",
"3033": "Valid Referral code",
"3034": "Invite guardian 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",
}
"""status code error"""
STATUS_CODE_ERROR = {

29
base/tasks.py Normal file
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

BIN
celerybeat-schedule Normal file

Binary file not shown.

View File

@ -3,6 +3,7 @@ services:
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "8000:8000"
volumes:
@ -13,6 +14,7 @@ services:
web:
build: .
container_name: django
restart: always
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
volumes:
- .:/usr/src/app

View File

@ -0,0 +1,23 @@
# Generated by Django 4.2.2 on 2023-07-24 13:21
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0016_juniortask_completed_on_juniortask_rejected_on_and_more'),
]
operations = [
migrations.AddField(
model_name='juniortask',
name='is_invited',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='juniortask',
name='is_password_set',
field=models.BooleanField(default=True),
),
]

View File

@ -0,0 +1,21 @@
# Generated by Django 4.2.2 on 2023-07-24 13:23
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('guardian', '0017_juniortask_is_invited_juniortask_is_password_set'),
]
operations = [
migrations.RemoveField(
model_name='juniortask',
name='is_invited',
),
migrations.RemoveField(
model_name='juniortask',
name='is_password_set',
),
]

View File

@ -0,0 +1,23 @@
# Generated by Django 4.2.2 on 2023-07-24 13:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0018_remove_juniortask_is_invited_and_more'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='is_invited',
field=models.BooleanField(default=False),
),
migrations.AddField(
model_name='guardian',
name='is_password_set',
field=models.BooleanField(default=True),
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-25 07:44
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0019_guardian_is_invited_guardian_is_password_set'),
]
operations = [
migrations.AlterField(
model_name='juniortask',
name='task_status',
field=models.CharField(choices=[('1', 'pending'), ('2', 'in-progress'), ('3', 'rejected'), ('4', 'requested'), ('5', 'completed'), ('6', 'expired')], default=1, max_length=15),
),
]

View File

@ -5,7 +5,7 @@ from django.contrib.auth import get_user_model
"""Import Django app"""
from base.constants import GENDERS, TASK_STATUS, PENDING, TASK_POINTS, SIGNUP_METHODS
"""import Junior model"""
from junior.models import Junior
import junior.models
"""Add user model"""
User = get_user_model()
@ -53,6 +53,10 @@ class Guardian(models.Model):
gender = models.CharField(choices=GENDERS, max_length=15, null=True, blank=True, default=None)
"""date of birth of the guardian"""
dob = models.DateField(max_length=15, null=True, blank=True, default=None)
# invited junior"""
is_invited = models.BooleanField(default=False)
# Profile activity"""
is_password_set = models.BooleanField(default=True)
"""Profile activity"""
is_active = models.BooleanField(default=True)
"""guardian is verified or not"""
@ -101,7 +105,7 @@ class JuniorTask(models.Model):
"""image that is uploaded by junior"""
image = models.URLField(null=True, blank=True, default=None)
"""associated junior with the task"""
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='junior', verbose_name='Junior')
junior = models.ForeignKey('junior.Junior', on_delete=models.CASCADE, related_name='junior', verbose_name='Junior')
"""task status"""
task_status = models.CharField(choices=TASK_STATUS, max_length=15, default=PENDING)
"""task is active or not"""

View File

@ -1,5 +1,5 @@
"""Serializer of Guardian"""
"""Third party Django app"""
# third party imports
import logging
from rest_framework import serializers
# Import Refresh token of jwt
@ -7,7 +7,8 @@ from rest_framework_simplejwt.tokens import RefreshToken
from django.db import transaction
from django.contrib.auth.models import User
from datetime import datetime, time
"""Import Django app"""
import pytz
from django.utils import timezone
# Import guardian's model,
# Import junior's model,
# Import account's model,
@ -16,16 +17,19 @@ from datetime import datetime, time
# Import messages from
# base package,
# Import some functions
# from utils file"""
# local imports
from .models import Guardian, JuniorTask
from account.models import UserProfile, UserEmailOtp, UserNotification
from account.utils import generate_code
from junior.serializers import JuniorDetailSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, JUN, ZOD, GRD
from junior.models import Junior, JuniorPoints
from .utils import real_time, convert_timedelta_into_datetime
from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
# notification's constant
from notifications.constants import TASK_POINTS, TASK_REJECTED
# send notification function
from notifications.utils import send_notification
# In this serializer file
@ -61,11 +65,13 @@ class UserSerializer(serializers.ModelSerializer):
try:
"""Create user profile"""
user = User.objects.create_user(username=email, email=email, password=password)
UserNotification.objects.create(user=user)
UserNotification.objects.get_or_create(user=user)
if user_type == str(NUMBER['one']):
# create junior profile
Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
referral_code=generate_code(ZOD, user.id))
junior = Junior.objects.create(auth=user, junior_code=generate_code(JUN, 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']):
# create guardian profile
Guardian.objects.create(user=user, guardian_code=generate_code(GRD, user.id),
@ -78,7 +84,7 @@ class UserSerializer(serializers.ModelSerializer):
otp_verified = False
if otp and otp.is_verified:
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",
})
@ -141,7 +147,6 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
guardian.country_code = validated_data.get('country_code', guardian.country_code)
guardian.passcode = validated_data.get('passcode', guardian.passcode)
guardian.country_name = validated_data.get('country_name', guardian.country_name)
guardian.referral_code_used = validated_data.get('referral_code_used', guardian.referral_code_used)
guardian.image = validated_data.get('image', guardian.image)
"""Complete profile of the junior if below all data are filled"""
complete_profile_field = all([guardian.gender, guardian.country_name,
@ -150,6 +155,9 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
guardian.is_complete_profile = False
if complete_profile_field:
guardian.is_complete_profile = True
referral_code_used = validated_data.get('referral_code_used')
update_referral_points(guardian.referral_code, referral_code_used)
guardian.referral_code_used = validated_data.get('referral_code_used', guardian.referral_code_used)
guardian.save()
return guardian
@ -167,6 +175,14 @@ class TaskSerializer(serializers.ModelSerializer):
"""Meta info"""
model = JuniorTask
fields = ['id', 'task_name','task_description','points', 'due_date', 'junior', 'default_image']
def validate_due_date(self, value):
"""validation on due date"""
if value < datetime.today().date():
raise serializers.ValidationError({"details": ERROR_CODE['2046'],
"code": 400, "status": "failed",
})
return value
def create(self, validated_data):
"""create default task image data"""
validated_data['guardian'] = Guardian.objects.filter(user=self.context['user']).last()
@ -198,19 +214,56 @@ class GuardianDetailSerializer(serializers.ModelSerializer):
"""Meta info"""
model = Guardian
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',
'updated_at']
class TaskDetailsSerializer(serializers.ModelSerializer):
"""Task detail serializer"""
junior = JuniorDetailSerializer()
remaining_time = serializers.SerializerMethodField('get_remaining_time')
is_expired = serializers.SerializerMethodField('get_is_expired')
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()
# 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'
def get_is_expired(self, obj):
""" task expired or not"""
if obj.due_date < datetime.today().date():
return True
return False
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', 'is_expired',
'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
@ -226,7 +279,6 @@ class TaskDetailsSerializer(serializers.ModelSerializer):
'requested_on', 'rejected_on', 'completed_on',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TopJuniorSerializer(serializers.ModelSerializer):
"""Top junior serializer"""
junior = JuniorDetailSerializer()
@ -235,7 +287,7 @@ class TopJuniorSerializer(serializers.ModelSerializer):
class Meta(object):
"""Meta info"""
model = JuniorPoints
fields = ['id', 'junior', 'total_task_points', 'position', 'created_at', 'updated_at']
fields = ['id', 'junior', 'total_points', 'position', 'created_at', 'updated_at']
def to_representation(self, instance):
"""Convert instance to representation"""
@ -290,6 +342,7 @@ class GuardianProfileSerializer(serializers.ModelSerializer):
'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method',
'updated_at', 'passcode']
class ApproveJuniorSerializer(serializers.ModelSerializer):
"""approve junior serializer"""
class Meta(object):
@ -301,9 +354,12 @@ class ApproveJuniorSerializer(serializers.ModelSerializer):
"""update guardian code"""
instance = self.context['junior']
instance.guardian_code = [self.context['guardian_code']]
instance.guardian_code_approved = True
instance.guardian_code_status = str(NUMBER['two'])
instance.save()
return instance
class ApproveTaskSerializer(serializers.ModelSerializer):
"""approve task serializer"""
class Meta(object):
@ -313,27 +369,96 @@ class ApproveTaskSerializer(serializers.ModelSerializer):
def create(self, validated_data):
"""update task status """
instance = self.context['task_instance']
junior = self.context['junior']
junior_details = Junior.objects.filter(id=junior).last()
junior_data, created = JuniorPoints.objects.get_or_create(junior=junior_details)
if self.context['action'] == str(NUMBER['one']):
# approve the task
instance.task_status = str(NUMBER['five'])
instance.is_approved = True
# update total task point
junior_data.total_task_points = junior_data.total_task_points + instance.points
# update complete time of task
instance.completed_on = real_time()
else:
# reject the task
instance.task_status = str(NUMBER['three'])
instance.is_approved = False
# update total task point
junior_data.total_task_points = junior_data.total_task_points - instance.points
# update reject time of task
instance.rejected_on = real_time()
instance.save()
junior_data.save()
return instance
with transaction.atomic():
instance = self.context['task_instance']
junior = self.context['junior']
junior_details = Junior.objects.filter(id=junior).last()
junior_data, created = JuniorPoints.objects.get_or_create(junior=junior_details)
if self.context['action'] == str(NUMBER['one']):
# approve the task
instance.task_status = str(NUMBER['five'])
instance.is_approved = True
# update total task point
junior_data.total_points = junior_data.total_points + instance.points
# update complete time of task
# instance.completed_on = real_time()
instance.completed_on = timezone.now().astimezone(pytz.utc)
send_notification.delay(TASK_POINTS, None, junior_details.auth.id, {})
else:
# reject the task
instance.task_status = str(NUMBER['three'])
instance.is_approved = False
# update reject time of task
# instance.rejected_on = real_time()
instance.rejected_on = timezone.now().astimezone(pytz.utc)
send_notification.delay(TASK_REJECTED, None, junior_details.auth.id, {})
instance.save()
junior_data.save()
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

View File

@ -2,7 +2,8 @@
"""Django import"""
from django.urls import path, include
from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView,
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView)
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView,
GuardianListAPIView)
"""Third party import"""
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')
# Approve junior API"""
router.register('approve-task', ApproveTaskAPIView, basename='approve-task')
# guardian list API"""
router.register('guardian-list', GuardianListAPIView, basename='guardian-list')
# Define Url pattern"""
urlpatterns = [
path('api/v1/', include(router.urls)),

View File

@ -3,15 +3,25 @@
import oss2
"""Import setting"""
from django.conf import settings
import logging
import requests
from django.core.exceptions import ObjectDoesNotExist
"""Import tempfile"""
import tempfile
# Import date time module's function
from datetime import datetime, time
# Import real time client module
import ntplib
# import Number constant
from base.constants import NUMBER
# Import Junior's model
from junior.models import Junior, JuniorPoints
# Import guardian's model
from .models import JuniorTask
# Import app from celery
from zod_bank.celery import app
# notification's constant
from notifications.constants import REFERRAL_POINTS
# send notification function
from notifications.utils import send_notification
# Define upload image on
# ali baba cloud
# firstly save image
@ -21,13 +31,20 @@ from base.constants import NUMBER
# bucket and reform the image url"""
# fetch real time data without depend on system time
# convert time delta into date time object
# update junior total points
# update referral points
# if any one use referral code of the junior
# junior earn 5 points
def upload_image_to_alibaba(image, filename):
"""upload image 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(image.read())
if type(image) == bytes:
temp_file.write(image)
else:
temp_file.write(image.read())
"""auth of bucket"""
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
"""fetch bucket details"""
@ -38,16 +55,20 @@ def upload_image_to_alibaba(image, filename):
new_filename = filename.replace(' ', '%20')
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}"
def real_time():
""" real time """
# Fetch real time.
# time is not depend on system time
# Get the current datetime
ntp_client = ntplib.NTPClient()
ntp_server = 'pool.ntp.org'
response = ntp_client.request(ntp_server)
current_datetime = datetime.fromtimestamp(response.tx_time)
return current_datetime
"""fetch real time from world time api"""
url = time_url
response = requests.get(url)
if response.status_code == 200:
data = response.json()
time_str = data['datetime']
realtime = datetime.fromisoformat(time_str.replace('Z', '+00:00')).replace(tzinfo=None)
return realtime
else:
logging.error("Could not fetch error")
return None
def convert_timedelta_into_datetime(time_difference):
"""convert date time"""
@ -59,3 +80,30 @@ def convert_timedelta_into_datetime(time_difference):
# Create a new time object with the extracted time components
time_only = time(hours, minutes, seconds, microseconds)
return time_only
def update_referral_points(referral_code, referral_code_used):
"""Update benefit of referral points"""
junior_queryset = Junior.objects.filter(referral_code=referral_code_used).last()
if junior_queryset and junior_queryset.referral_code != referral_code:
# create data if junior points is not exist
junior_query, created = JuniorPoints.objects.get_or_create(junior=junior_queryset)
if junior_query:
# update junior total points and referral points
junior_query.total_points = junior_query.total_points + NUMBER['five']
junior_query.referral_points = junior_query.referral_points + NUMBER['five']
junior_query.save()
send_notification.delay(REFERRAL_POINTS, None, junior_queryset.auth.id, {})
@app.task
def update_expired_task_status(data=None):
"""
Update task of the status if due date is in past
"""
try:
task_status = [str(NUMBER['one']), str(NUMBER['two']), str(NUMBER['four'])]
JuniorTask.objects.filter(due_date__lt=datetime.today().date(),
task_status__in=task_status).update(task_status=str(NUMBER['six']))
except ObjectDoesNotExist as e:
logging.error(str(e))

View File

@ -1,10 +1,17 @@
"""Views of Guardian"""
# django imports
# Import IsAuthenticated
# Import viewsets and status
# Import PageNumberPagination
# Import User
# Import timezone
from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status
from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User
from rest_framework.filters import SearchFilter
from django.utils import timezone
# Import guardian's model,
@ -18,19 +25,20 @@ from django.utils import timezone
# from utils file
# Import account's serializer
# Import account's task
# Import notification constant
# Import send_notification function
from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializer, TaskDetailsSerializer,
TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer)
TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer,
GuardianDetailListSerializer)
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 .tasks import generate_otp
from account.utils import send_otp_email
from account.utils import custom_response, custom_error_response
from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email
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 notifications.constants import REGISTRATION
from notifications.constants import REGISTRATION, TASK_CREATED, LEADERBOARD_RANKING
from notifications.utils import send_notification
""" Define APIs """
@ -51,19 +59,21 @@ class SignupViewset(viewsets.ModelViewSet):
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
"""Create user profile"""
if request.data['user_type'] in ['1', '2']:
if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]:
serializer = UserSerializer(context=request.data['user_type'], data=request.data)
if serializer.is_valid():
user = serializer.save()
"""Generate otp"""
otp = generate_otp()
expiry = timezone.now() + timezone.timedelta(days=1)
# expire otp after 1 day
expiry = OTP_EXPIRY
# create user email otp object
UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
user_type=str(request.data['user_type']), expired_at=expiry)
"""Send email to the register user"""
send_otp_email(request.data['email'], otp)
send_notification(REGISTRATION, None, user.id, {})
# send push notification for registration
send_notification.delay(REGISTRATION, None, user.id, {})
return custom_response(SUCCESS_CODE['3001'],
response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
@ -72,38 +82,39 @@ class SignupViewset(viewsets.ModelViewSet):
class UpdateGuardianProfile(viewsets.ViewSet):
"""Update guardian profile"""
queryset = Guardian.objects.all()
serializer_class = CreateGuardianSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
"""Create guardian profile"""
data = request.data
image = request.data.get('image')
image_url = ''
if image:
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
filename = f"images/{image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(image, filename)
data = {"image":image_url}
serializer = CreateGuardianSerializer(context={"user":request.user,
"first_name":request.data.get('first_name'),
"last_name": request.data.get('last_name'),
"image":image_url},
data=data)
if serializer.is_valid():
"""save serializer"""
serializer.save()
return custom_response(None, serializer.data,response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
try:
data = request.data
image = request.data.get('image')
image_url = ''
if image:
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
filename = f"images/{image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(image, filename)
data = {"image":image_url}
serializer = CreateGuardianSerializer(context={"user":request.user,
"first_name":request.data.get('first_name'),
"last_name": request.data.get('last_name'),
"image":image_url},
data=data)
if serializer.is_valid():
"""save serializer"""
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, 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 AllTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
queryset = JuniorTask.objects.all()
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
@ -113,66 +124,113 @@ class AllTaskListAPIView(viewsets.ModelViewSet):
serializer = TaskDetailsSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
# class TaskListAPIView(viewsets.ModelViewSet):
# """Update guardian profile"""
# serializer_class = TaskDetailsSerializer
# permission_classes = [IsAuthenticated]
# pagination_class = PageNumberPagination
# http_method_names = ('get',)
#
# def list(self, request, *args, **kwargs):
# """Create guardian profile"""
# try:
# status_value = self.request.GET.get('status')
# search = self.request.GET.get('search')
# if search and str(status_value) == '0':
# queryset = JuniorTask.objects.filter(guardian__user=request.user,
# task_name__icontains=search).order_by('due_date', 'created_at')
# elif search and str(status_value) != '0':
# queryset = JuniorTask.objects.filter(guardian__user=request.user,task_name__icontains=search,
# task_status=status_value).order_by('due_date', 'created_at')
# if search is None and str(status_value) == '0':
# 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 TaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
search_fields = ['task_name', ]
pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
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):
"""Create guardian profile"""
status_value = self.request.GET.get('status')
search = self.request.GET.get('search')
if search and str(status_value) == '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user,
task_name__icontains=search).order_by('due_date', 'created_at')
elif search and str(status_value) != '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user,task_name__icontains=search,
task_status=status_value).order_by('due_date', 'created_at')
if search is None and str(status_value) == '0':
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')
queryset = self.get_queryset()
if status_value and status_value != '0':
queryset = queryset.filter(task_status=status_value)
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = TaskDetailsSerializer(paginated_queryset, many=True)
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class CreateTaskAPIView(viewsets.ModelViewSet):
"""create task for junior"""
serializer_class = TaskSerializer
queryset = JuniorTask.objects.all()
http_method_names = ('post', )
def create(self, request, *args, **kwargs):
image = request.data['default_image']
data = request.data
if 'https' in str(image):
image_data = image
else:
filename = f"images/{image}"
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
image_url = upload_image_to_alibaba(image, filename)
image_data = image_url
data.pop('default_image')
# use TaskSerializer serializer
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data)
if serializer.is_valid():
# save serializer
serializer.save()
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)
try:
image = request.data['default_image']
junior = request.data['junior']
allowed_extensions = ['.jpg', '.jpeg', '.png']
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)
if not junior.isnumeric():
"""junior value must be integer"""
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
data = request.data
if 'https' in str(image):
image_data = image
else:
filename = f"images/{image}"
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
image_url = upload_image_to_alibaba(image, filename)
image_data = image_url
data.pop('default_image')
# use TaskSerializer serializer
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data)
if serializer.is_valid():
# save serializer
serializer.save()
junior_id = Junior.objects.filter(id=junior).last()
send_notification.delay(TASK_CREATED, None, junior_id.auth.id, {})
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)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class SearchTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
def get_queryset(self):
"""Get the queryset for the view"""
@ -184,21 +242,23 @@ class SearchTaskListAPIView(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
queryset = self.get_queryset()
paginator = self.pagination_class()
# use pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskSerializer serializer
serializer = TaskDetailsSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
try:
queryset = self.get_queryset()
paginator = self.pagination_class()
# use pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskSerializer 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 TopJuniorListAPIView(viewsets.ModelViewSet):
"""Top juniors list"""
queryset = JuniorPoints.objects.all()
serializer_class = TopJuniorSerializer
permission_classes = [IsAuthenticated]
queryset = JuniorPoints.objects.all()
def get_serializer_context(self):
# context list
@ -208,15 +268,17 @@ class TopJuniorListAPIView(viewsets.ModelViewSet):
def list(self, request, *args, **kwargs):
"""Fetch junior list of those who complete their tasks"""
junior_total_points = self.get_queryset().order_by('-total_task_points')
# Update the position field for each JuniorPoints object
for index, junior in enumerate(junior_total_points):
junior.position = index + 1
junior.save()
serializer = self.get_serializer(junior_total_points, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
try:
junior_total_points = self.get_queryset().order_by('-total_points')
# Update the position field for each JuniorPoints object
for index, junior in enumerate(junior_total_points):
junior.position = index + 1
send_notification.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {})
junior.save()
serializer = self.get_serializer(junior_total_points[:NUMBER['fifteen']], 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 ApproveJuniorAPIView(viewsets.ViewSet):
@ -233,19 +295,26 @@ class ApproveJuniorAPIView(viewsets.ViewSet):
def create(self, request, *args, **kwargs):
""" junior list"""
queryset = self.get_queryset()
# action 1 is use for approve and 2 for reject
if request.data['action'] == '1':
# use ApproveJuniorSerializer serializer
serializer = ApproveJuniorSerializer(context={"guardian_code": queryset[0].guardian_code,
"junior": queryset[1], "action": request.data['action']},
data=request.data)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
else:
return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK)
try:
queryset = self.get_queryset()
# action 1 is use for approve and 2 for reject
if request.data['action'] == '1':
# use ApproveJuniorSerializer serializer
serializer = ApproveJuniorSerializer(context={"guardian_code": queryset[0].guardian_code,
"junior": queryset[1], "action": request.data['action']},
data=request.data)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
else:
queryset[1].guardian_code = None
queryset[1].guardian_code_status = str(NUMBER['one'])
queryset[1].save()
return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ApproveTaskAPIView(viewsets.ViewSet):
"""approve junior by guardian"""
@ -264,20 +333,46 @@ class ApproveTaskAPIView(viewsets.ViewSet):
def create(self, request, *args, **kwargs):
""" junior list"""
# action 1 is use for approve and 2 for reject
queryset = self.get_queryset()
# use ApproveJuniorSerializer serializer
serializer = ApproveTaskSerializer(context={"guardian_code": queryset[0].guardian_code,
"task_instance": queryset[1],
"action": str(request.data['action']),
"junior": self.request.data['junior_id']},
data=request.data)
if str(request.data['action']) == str(NUMBER['one']) and serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3025'], response_status=status.HTTP_200_OK)
elif str(request.data['action']) == str(NUMBER['two']) and serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3026'], response_status=status.HTTP_200_OK)
else:
return custom_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST)
try:
queryset = self.get_queryset()
# use ApproveJuniorSerializer serializer
serializer = ApproveTaskSerializer(context={"guardian_code": queryset[0].guardian_code,
"task_instance": queryset[1],
"action": str(request.data['action']),
"junior": self.request.data['junior_id']},
data=request.data)
unexpected_task_status = [str(NUMBER['five']), str(NUMBER['six'])]
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):
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3025'], response_status=status.HTTP_200_OK)
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):
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3026'], response_status=status.HTTP_200_OK)
else:
return custom_response(ERROR_CODE['2038'], 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 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)

View File

@ -2,8 +2,26 @@
"""Third party Django app"""
from django.contrib import admin
"""Import Django app"""
from .models import Junior, JuniorPoints
from .models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
JuniorArticleCard)
# Register your models here.
@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)
class JuniorAdmin(admin.ModelAdmin):
"""Junior Admin"""
@ -16,8 +34,18 @@ class JuniorAdmin(admin.ModelAdmin):
@admin.register(JuniorPoints)
class JuniorPointsAdmin(admin.ModelAdmin):
"""Junior Points Admin"""
list_display = ['junior', 'total_task_points', 'position']
list_display = ['junior', 'total_points', 'position']
def __str__(self):
"""Return email id"""
return self.junior.auth.email
@admin.register(JuniorGuardianRelationship)
class JuniorGuardianRelationshipAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['guardian', 'junior', 'relationship']
@admin.register(JuniorArticlePoints)
class JuniorArticlePointsAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['junior', 'article', 'question', 'submitted_answer', 'is_answer_correct']

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-19 09:40
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('junior', '0014_junior_is_password_set'),
]
operations = [
migrations.RenameField(
model_name='juniorpoints',
old_name='total_task_points',
new_name='total_points',
),
]

View File

@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-19 11:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0015_rename_total_task_points_juniorpoints_total_points'),
]
operations = [
migrations.AddField(
model_name='juniorpoints',
name='referral_points',
field=models.IntegerField(blank=True, default=0, null=True),
),
]

View File

@ -0,0 +1,31 @@
# Generated by Django 4.2.2 on 2023-07-25 07:44
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('guardian', '0020_alter_juniortask_task_status'),
('junior', '0016_juniorpoints_referral_points'),
]
operations = [
migrations.CreateModel(
name='JuniorGuardianRelationship',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('relationship', models.CharField(blank=True, choices=[('1', 'parent'), ('2', 'legal_guardian')], default='1', max_length=31, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('guardian', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guardian_relation', to='guardian.guardian', verbose_name='Guardian')),
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_relation', to='junior.junior', verbose_name='Junior')),
],
options={
'verbose_name': 'Junior Guardian Relation',
'verbose_name_plural': 'Junior Guardian Relation',
'db_table': 'junior_guardian_relation',
},
),
]

View File

@ -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),
),
]

View File

@ -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')),
],
),
]

View File

@ -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),
),
]

View File

@ -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'),
),
]

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')),
],
),
]

View File

@ -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')),
],
),
]

View File

@ -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),
),
]

View File

@ -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'),
),
]

View File

@ -6,7 +6,11 @@ from django.contrib.auth import get_user_model
"""Import ArrayField"""
from django.contrib.postgres.fields import ArrayField
"""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
from guardian.models import Guardian
# Import web admin's model
from web_admin.models import SurveyOption, ArticleSurvey, Article, ArticleCard
"""Define User model"""
User = get_user_model()
# Create your models here.
@ -31,7 +35,7 @@ User = get_user_model()
"""Define junior points model"""
# points of the junior
# position of the junior
# define junior guardian relation model
class Junior(models.Model):
"""Junior model"""
auth = models.ForeignKey(User, on_delete=models.CASCADE, related_name='junior_profile', verbose_name='Email')
@ -46,9 +50,6 @@ class Junior(models.Model):
dob = models.DateField(max_length=15, null=True, blank=True, default=None)
# Image of the junior"""
image = models.URLField(null=True, blank=True, default=None)
# relationship"""
relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True,
default='1')
# Sign up method"""
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
# Codes"""
@ -71,6 +72,11 @@ class Junior(models.Model):
passcode = models.IntegerField(null=True, blank=True, default=None)
# junior is verified or not"""
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"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@ -89,8 +95,10 @@ class Junior(models.Model):
class JuniorPoints(models.Model):
"""Junior model"""
junior = models.OneToOneField(Junior, on_delete=models.CASCADE, related_name='junior_points')
# Contact details"""
total_task_points = models.IntegerField(blank=True, null=True, default=0)
# Total earned points"""
total_points = models.IntegerField(blank=True, null=True, default=0)
# referral points"""
referral_points = models.IntegerField(blank=True, null=True, default=0)
# position of the junior"""
position = models.IntegerField(blank=True, null=True, default=99999)
# Profile created and updated time"""
@ -107,3 +115,88 @@ class JuniorPoints(models.Model):
def __str__(self):
"""Return email id"""
return f'{self.junior.auth}'
class JuniorGuardianRelationship(models.Model):
"""Junior Guardian relationship model"""
guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE, related_name='guardian_relation',
verbose_name='Guardian')
# associated junior with the task
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='junior_relation', verbose_name='Junior')
# relation between guardian and junior"""
relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True,
default='1')
"""Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
db_table = 'junior_guardian_relation'
"""verbose name of the model"""
verbose_name = 'Junior Guardian Relation'
verbose_name_plural = 'Junior Guardian Relation'
def __str__(self):
"""Return email id"""
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)
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)
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)
def __str__(self):
"""Return title"""
return f'{self.id} | {self.article}'

View File

@ -1,5 +1,8 @@
"""Serializer file for junior"""
"""Import Django 3rd party app"""
# third party imports
import pytz
# django imports
from rest_framework import serializers
from django.contrib.auth.models import User
from django.db import transaction
@ -7,16 +10,20 @@ from datetime import datetime
from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken
"""Import django app"""
# local imports
from account.utils import send_otp_email, generate_code
from junior.models import Junior, JuniorPoints
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints
from guardian.tasks import generate_otp
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 account.models import UserEmailOtp
from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail
from guardian.utils import real_time
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
from notifications.utils import send_notification
from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION,
TASK_SUBMITTED)
class ListCharField(serializers.ListField):
@ -86,13 +93,18 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
"""condition for guardian code"""
if 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.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {})
junior.dob = validated_data.get('dob', junior.dob)
junior.passcode = validated_data.get('passcode', junior.passcode)
junior.country_name = validated_data.get('country_name', junior.country_name)
"""Update country code and phone number"""
junior.phone = validated_data.get('phone', junior.phone)
junior.country_code = validated_data.get('country_code', junior.country_code)
junior.referral_code_used = validated_data.get('referral_code_used', junior.referral_code_used)
junior.image = validated_data.get('image', junior.image)
"""Complete profile of the junior if below all data are filled"""
complete_profile_field = all([junior.gender, junior.country_name, junior.image,
@ -100,6 +112,9 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
junior.is_complete_profile = False
if complete_profile_field:
junior.is_complete_profile = True
referral_code_used = validated_data.get('referral_code_used')
update_referral_points(junior.referral_code, referral_code_used)
junior.referral_code_used = validated_data.get('referral_code_used', junior.referral_code_used)
junior.save()
return junior
@ -131,7 +146,7 @@ class JuniorDetailSerializer(serializers.ModelSerializer):
"""Meta info"""
model = Junior
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', 'created_at',
'image', 'updated_at']
class JuniorDetailListSerializer(serializers.ModelSerializer):
@ -167,10 +182,11 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
data = JuniorPoints.objects.filter(junior=obj).last()
if data:
return data.position
return 99999
def get_points(self, obj):
data = sum(JuniorTask.objects.filter(junior=obj, task_status=COMPLETED).values_list('points', flat=True))
return data
data = JuniorPoints.objects.filter(junior=obj).last()
if data:
return data.total_points
return NUMBER['zero']
def get_in_progress_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=IN_PROGRESS).count()
@ -195,10 +211,10 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
class Meta(object):
"""Meta info"""
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',
'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']
class JuniorProfileSerializer(serializers.ModelSerializer):
"""junior serializer"""
@ -241,22 +257,14 @@ class JuniorProfileSerializer(serializers.ModelSerializer):
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',
'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method',
'is_invited', 'passcode']
'is_invited', 'passcode', 'guardian_code_approved']
class AddJuniorSerializer(serializers.ModelSerializer):
"""Add junior serializer"""
auth_token = serializers.SerializerMethodField('get_auth_token')
def get_auth_token(self, obj):
"""auth token"""
refresh = RefreshToken.for_user(obj)
access_token = str(refresh.access_token)
return access_token
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'gender','dob', 'relationship', 'auth_token', 'is_invited']
fields = ['id', 'gender', 'dob', 'is_invited']
def create(self, validated_data):
@ -264,32 +272,40 @@ class AddJuniorSerializer(serializers.ModelSerializer):
with transaction.atomic():
email = self.context['email']
guardian = self.context['user']
relationship = self.context['relationship']
profile_image = self.context['image']
full_name = self.context['first_name'] + ' ' + self.context['last_name']
guardian_data = Guardian.objects.filter(user__username=guardian).last()
user_data = User.objects.create(username=email, email=email,
first_name=self.context['first_name'],
last_name=self.context['last_name'])
password = User.objects.make_random_password()
user_data.set_password(password)
user_data.save()
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
image=profile_image,
dob=validated_data.get('dob'), is_invited=True,
relationship=validated_data.get('relationship'),
guardian_code=[guardian_data.guardian_code],
junior_code=generate_code(JUN, user_data.id),
referral_code=generate_code(ZOD, user_data.id),
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,
relationship=relationship)
total_junior = Junior.objects.all().count()
JuniorPoints.objects.create(junior=junior_data, position=total_junior)
"""Generate otp"""
otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1)
UserEmailOtp.objects.create(email=email, otp=otp_value,
user_type='1', expired_at=expiry_time, is_verified=True)
"""Send email to the register user"""
send_otp_email(email, otp_value)
# add push notification
UserNotification.objects.get_or_create(user=user_data)
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail(guardian, full_name)
# push notification
send_notification.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {})
return junior_data
@ -304,6 +320,7 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
if instance:
instance.is_invited = False
instance.guardian_code = '{}'
instance.guardian_code_status = str(NUMBER['1'])
instance.save()
return instance
@ -316,10 +333,13 @@ class CompleteTaskSerializer(serializers.ModelSerializer):
fields = ('id', 'image')
def update(self, instance, validated_data):
instance.image = validated_data.get('image', instance.image)
instance.requested_on = real_time()
# instance.requested_on = real_time()
instance.requested_on = timezone.now().astimezone(pytz.utc)
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
send_notification.delay(TASK_SUBMITTED, None, instance.junior.auth.id, {})
send_notification.delay(TASK_ACTION, None, instance.guardian.user.id, {})
return instance
class JuniorPointsSerializer(serializers.ModelSerializer):
@ -331,6 +351,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
requested_task = serializers.SerializerMethodField('get_requested_task')
rejected_task = serializers.SerializerMethodField('get_rejected_task')
pending_task = serializers.SerializerMethodField('get_pending_task')
expired_task = serializers.SerializerMethodField('get_expired_task')
position = serializers.SerializerMethodField('get_position')
def get_junior_id(self, obj):
@ -346,7 +367,7 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
"""total points"""
points = JuniorPoints.objects.filter(junior=obj.junior).last()
if points:
return points.total_task_points
return points.total_points
def get_in_progress_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=IN_PROGRESS).count()
@ -366,8 +387,122 @@ class JuniorPointsSerializer(serializers.ModelSerializer):
def get_pending_task(self, obj):
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):
"""Meta info"""
model = Junior
fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task',
'requested_task', 'rejected_task']
'requested_task', 'rejected_task', 'expired_task']
class AddGuardianSerializer(serializers.ModelSerializer):
"""Add guardian serializer"""
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id']
def create(self, validated_data):
""" invite and create guardian"""
with transaction.atomic():
email = self.context['email']
junior = self.context['user']
relationship = self.context['relationship']
full_name = self.context['first_name'] + ' ' + self.context['last_name']
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()
if instance:
guardian_data = Guardian.objects.filter(user=instance).update(is_invited=True,
referral_code=generate_code(ZOD,
instance.id),
referral_code_used=junior_data.referral_code,
is_verified=True)
UserNotification.objects.get_or_create(user=instance)
return guardian_data
else:
user = User.objects.create(username=email, email=email,
first_name=self.context['first_name'],
last_name=self.context['last_name'])
password = User.objects.make_random_password()
user.set_password(password)
user.save()
guardian_data = Guardian.objects.create(user=user, is_invited=True,
referral_code=generate_code(ZOD, user.id),
referral_code_used=junior_data.referral_code,
is_password_set=False, is_verified=True)
"""Generate otp"""
otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1)
UserEmailOtp.objects.create(email=email, otp=otp_value,
user_type=str(NUMBER['two']), expired_at=expiry_time,
is_verified=True)
UserNotification.objects.get_or_create(user=user)
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship)
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail(email, full_name)
send_notification.delay(INVITED_GUARDIAN, None, junior_data.auth.id, {})
send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {})
return guardian_data
class StartTaskSerializer(serializers.ModelSerializer):
"""User task Serializer"""
task_duration = serializers.SerializerMethodField('get_task_duration')
def get_task_duration(self, obj):
""" remaining time to complete task"""
due_date = datetime.combine(obj.due_date, datetime.max.time())
# fetch 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
if due_date > real_datetime:
time_difference = due_date - real_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 class"""
model = JuniorTask
fields = ('id', 'task_duration')
def update(self, instance, validated_data):
instance.task_status = str(NUMBER['two'])
instance.save()
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_status = str(NUMBER['one'])
instance.save()
return instance

View File

@ -3,7 +3,10 @@
from django.urls import path, include
from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView,
InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView,
CompleteJuniorTaskAPIView, JuniorPointsListAPIView)
CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode,
InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView,
StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView,
CreateArticleCardAPIView, RemoveGuardianCodeAPIView)
"""Third party import"""
from rest_framework import routers
@ -36,9 +39,26 @@ router.register('filter-junior', FilterJuniorAPIView, basename='filter-junior')
router.register('junior-task-list', JuniorTaskListAPIView, basename='junior-task-list')
# junior's task list API"""
router.register('junior-points', JuniorPointsListAPIView, basename='junior-points')
# validate referral code API"""
router.register('validate-referral-code', ValidateReferralCode, basename='validate-referral-code')
# invite guardian API"""
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')
# Define url pattern"""
urlpatterns = [
path('api/v1/', include(router.urls)),
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/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()),
]

View File

@ -3,7 +3,9 @@
from django.conf import settings
"""Third party Django app"""
from templated_email import send_templated_mail
from .models import JuniorPoints
from base.constants import NUMBER
from django.db.models import F
# junior notification
# email for sending email
# when guardian create junior profile
@ -45,3 +47,15 @@ def junior_approval_mail(guardian, full_name):
}
)
return full_name
def update_positions_based_on_points():
"""Update position of the junior"""
# First, retrieve all the JuniorPoints instances ordered by total_points in descending order.
juniors_points = JuniorPoints.objects.order_by('-total_points', 'created_at')
# Now, iterate through the queryset and update the position field based on the order.
position = NUMBER['one']
for junior_point in juniors_points:
junior_point.position = position
junior_point.save()
position += NUMBER['one']

View File

@ -4,6 +4,11 @@ import os
from rest_framework import viewsets, status, generics,views
from rest_framework.permissions import IsAuthenticated
from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User
from rest_framework.filters import SearchFilter
from django.db.models import F
import datetime
import requests
"""Django app import"""
@ -18,16 +23,30 @@ import requests
# from utils file
# Import account's serializer
# Import account's task
from junior.models import Junior
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\
RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer)
# import junior serializer
# Import update_positions_based_on_points
# Import upload_image_to_alibaba
# Import custom_response, custom_error_response
# Import constants
from django.db.models import Sum
from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
JuniorArticleCard)
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,
RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer,
AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer,
RemoveGuardianCodeSerializer)
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.constants import NUMBER
from base.constants import NUMBER, ARTICLE_STATUS
from account.utils import custom_response, custom_error_response
from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points
from notifications.utils import send_notification
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 validate guardian code API,
# update junior profile,
@ -37,52 +56,64 @@ from guardian.utils import upload_image_to_alibaba
# search junior API
# remove junior API,
# approve junior API
# create referral code
# validation API
# invite guardian API
# by junior
# Start task
# by junior API
# Create your views here.
class UpdateJuniorProfile(viewsets.ViewSet):
"""Update junior profile"""
queryset = Junior.objects.all()
serializer_class = CreateJuniorSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
"""Use CreateJuniorSerializer"""
request_data = request.data
image = request.data.get('image')
image_url = ''
if image:
if image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
filename = f"images/{image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(image, filename)
request_data = {"image": image_url}
serializer = CreateJuniorSerializer(context={"user":request.user, "image":image_url,
"first_name": request.data.get('first_name'),
"last_name": request.data.get('last_name')
},
data=request_data)
if serializer.is_valid():
"""save serializer"""
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
try:
request_data = request.data
image = request.data.get('image')
image_url = ''
if image:
# check image size
if image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
# convert into file
filename = f"images/{image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(image, filename)
request_data = {"image": image_url}
serializer = CreateJuniorSerializer(context={"user":request.user, "image":image_url,
"first_name": request.data.get('first_name'),
"last_name": request.data.get('last_name')
},
data=request_data)
if serializer.is_valid():
"""save serializer"""
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, 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 ValidateGuardianCode(viewsets.ViewSet):
"""Check guardian code exist or not"""
queryset = Guardian.objects.all()
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""check guardian code"""
guardian_code = self.request.GET.get('guardian_code').split(',')
for code in guardian_code:
# fetch guardian object
guardian_data = Guardian.objects.filter(guardian_code=code).exists()
if guardian_data:
return custom_response(SUCCESS_CODE['3013'], response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2022"], response_status=status.HTTP_400_BAD_REQUEST)
try:
guardian_code = self.request.GET.get('guardian_code').split(',')
for code in guardian_code:
# fetch guardian object
guardian_data = Guardian.objects.filter(guardian_code=code).exists()
if guardian_data:
# successfully check guardian code
return custom_response(SUCCESS_CODE['3013'], response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2022"], 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 JuniorListAPIView(viewsets.ModelViewSet):
"""Junior list of assosicated guardian"""
@ -90,59 +121,103 @@ class JuniorListAPIView(viewsets.ModelViewSet):
serializer_class = JuniorDetailListSerializer
queryset = Junior.objects.all()
permission_classes = [IsAuthenticated]
filter_backends = (SearchFilter,)
search_fields = ['auth__first_name', 'auth__last_name']
http_method_names = ('get',)
def get_queryset(self):
queryset = self.filter_queryset(self.queryset)
return queryset
def list(self, request, *args, **kwargs):
""" junior list"""
auth_token = self.request.META['HTTP_AUTHORIZATION']
headers_token = {
'Authorization': auth_token
}
requests.get(os.getenv('BASE_URL') + '/api/v1/top-junior/', headers=headers_token)
guardian_data = Guardian.objects.filter(user__email=request.user).last()
queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code))
# use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
try:
update_positions_based_on_points()
guardian_data = Guardian.objects.filter(user__email=request.user).last()
# fetch junior object
if guardian_data:
queryset = self.get_queryset()
queryset = queryset.filter(guardian_code__icontains=str(guardian_data.guardian_code))
# use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(ERROR_CODE['2045'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class AddJuniorAPIView(viewsets.ModelViewSet):
"""Add Junior by guardian"""
queryset = Junior.objects.all()
serializer_class = AddJuniorSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
""" junior list"""
info = {'user': request.user, 'email': request.data['email'], 'first_name': request.data['first_name'],
'last_name': request.data['last_name']}
# use AddJuniorSerializer serializer
serializer = AddJuniorSerializer(data=request.data, context=info)
if serializer.is_valid():
# save serializer
serializer.save()
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)
try:
info_data = {'user': request.user, 'relationship': str(request.data['relationship']),
'email': request.data['email'], 'first_name': request.data['first_name'],
'last_name': request.data['last_name'], 'image':None}
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():
self.associate_guardian(user)
return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK)
# use AddJuniorSerializer serializer
serializer = AddJuniorSerializer(data=request.data, context=info_data)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
def associate_guardian(self, user):
junior = Junior.objects.filter(auth=user).first()
guardian = Guardian.objects.filter(user=self.request.user).first()
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):
"""Junior list of assosicated guardian"""
serializer_class = JuniorDetailListSerializer
queryset = Junior.objects.all()
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
http_method_names = ('get',)
def get_queryset(self):
"""Get the queryset for the view"""
guardian = Guardian.objects.filter(user__email=self.request.user).last()
junior_queryset = Junior.objects.filter(guardian_code__icontains=str(guardian.guardian_code),
is_invited=True)
is_invited=True)
return junior_queryset
def list(self, request, *args, **kwargs):
""" junior list"""
queryset = self.get_queryset()
paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
try:
queryset = self.get_queryset()
paginator = self.pagination_class()
# pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(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 FilterJuniorAPIView(viewsets.ModelViewSet):
@ -150,25 +225,29 @@ class FilterJuniorAPIView(viewsets.ModelViewSet):
serializer_class = JuniorDetailListSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
queryset = Junior.objects.all()
http_method_names = ('get',)
def get_queryset(self):
"""Get the queryset for the view"""
title = self.request.GET.get('title')
guardian_data = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code),
is_invited=True, auth__first_name=title)
return queryset
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
queryset = self.get_queryset()
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
try:
queryset = self.get_queryset()
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(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 RemoveJuniorAPIView(views.APIView):
@ -178,51 +257,62 @@ class RemoveJuniorAPIView(views.APIView):
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
junior_id = self.request.GET.get('id')
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
junior_queryset = Junior.objects.filter(id=junior_id, guardian_code__icontains=str(guardian.guardian_code),
is_invited=True).last()
if junior_queryset:
# use RemoveJuniorSerializer serializer
serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
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)
else:
return custom_error_response(ERROR_CODE['2034'], response_status=status.HTTP_400_BAD_REQUEST)
try:
junior_id = self.request.GET.get('id')
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
junior_queryset = Junior.objects.filter(id=junior_id,
guardian_code__icontains=str(guardian.guardian_code)).last()
if junior_queryset:
# use RemoveJuniorSerializer serializer
serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
send_notification.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {})
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)
else:
return custom_error_response(ERROR_CODE['2034'], 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 JuniorTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
serializer_class = TaskDetailsjuniorSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
http_method_names = ('get',)
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
status_value = self.request.GET.get('status')
search = self.request.GET.get('search')
if search and str(status_value) == '0':
queryset = JuniorTask.objects.filter(junior__auth=request.user,
task_name__icontains=search).order_by('due_date', 'created_at')
elif search and str(status_value) != '0':
queryset = JuniorTask.objects.filter(junior__auth=request.user, task_name__icontains=search,
task_status=status_value).order_by('due_date', 'created_at')
if search is None and str(status_value) == '0':
queryset = JuniorTask.objects.filter(junior__auth=request.user).order_by('due_date', 'created_at')
elif search is None and str(status_value) != '0':
queryset = JuniorTask.objects.filter(junior__auth=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)
try:
status_value = self.request.GET.get('status')
search = self.request.GET.get('search')
if search and str(status_value) == '0':
# search with title and for all task list
queryset = JuniorTask.objects.filter(junior__auth=request.user,
task_name__icontains=search).order_by('due_date', 'created_at')
elif search and str(status_value) != '0':
# search with title and fetch task list with status wise
queryset = JuniorTask.objects.filter(junior__auth=request.user, task_name__icontains=search,
task_status=status_value).order_by('due_date', 'created_at')
if search is None and str(status_value) == '0':
# fetch all task list
queryset = JuniorTask.objects.filter(junior__auth=request.user).order_by('due_date', 'created_at')
elif search is None and str(status_value) != '0':
# fetch task list with status wise
queryset = JuniorTask.objects.filter(junior__auth=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 = TaskDetailsjuniorSerializer(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 CompleteJuniorTaskAPIView(views.APIView):
@ -237,12 +327,19 @@ class CompleteJuniorTaskAPIView(views.APIView):
image = request.data['image']
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
# create file
filename = f"images/{image.name}"
# upload image
filename = f"images/{image.name}"
image_url = upload_image_to_alibaba(image, filename)
# fetch junior query
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last()
if task_queryset:
# use RemoveJuniorSerializer serializer
# use CompleteTaskSerializer serializer
if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]:
"""Already request send """
return custom_error_response(ERROR_CODE['2049'], response_status=status.HTTP_400_BAD_REQUEST)
serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True)
if serializer.is_valid():
# save serializer
@ -258,17 +355,297 @@ class JuniorPointsListAPIView(viewsets.ModelViewSet):
"""Junior Points viewset"""
serializer_class = JuniorPointsSerializer
permission_classes = [IsAuthenticated]
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):
"""profile view"""
queryset = self.get_queryset()
token = self.request.META['HTTP_AUTHORIZATION']
headers = {
'Authorization': token
}
requests.get(os.getenv('BASE_URL') + '/api/v1/top-junior/', headers=headers)
serializer = JuniorPointsSerializer(queryset)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
try:
update_positions_based_on_points()
queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last()
# update position of junior
serializer = JuniorPointsSerializer(queryset)
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 ValidateReferralCode(viewsets.ViewSet):
"""Check guardian code exist or not"""
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
"""Get queryset based on referral_code."""
referral_code = self.request.GET.get('referral_code')
if referral_code:
# search referral code in guardian model
guardian_queryset = Guardian.objects.filter(referral_code=referral_code).last()
if guardian_queryset:
return guardian_queryset
else:
# search referral code in junior model
junior_queryset = Junior.objects.filter(referral_code=referral_code).last()
if junior_queryset:
return junior_queryset
return None
def list(self, request, *args, **kwargs):
"""check guardian code"""
try:
if self.get_queryset():
return custom_response(SUCCESS_CODE['3033'], response_status=status.HTTP_200_OK)
return custom_error_response(ERROR_CODE["2019"], 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 InviteGuardianAPIView(viewsets.ModelViewSet):
"""Invite guardian by junior"""
serializer_class = AddGuardianSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
""" junior list"""
try:
if request.data['email'] == '':
return custom_error_response(ERROR_CODE['2062'], response_status=status.HTTP_400_BAD_REQUEST)
info = {'user': request.user, 'email': request.data['email'], 'first_name': request.data['first_name'],
'last_name': request.data['last_name'], 'relationship': str(request.data['relationship'])}
# use AddJuniorSerializer serializer
serializer = AddGuardianSerializer(data=request.data, context=info)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3034'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.error, 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 StartTaskAPIView(views.APIView):
"""Update junior task API"""
serializer_class = StartTaskSerializer
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, junior__auth__email=self.request.user).last()
if task_queryset and task_queryset.task_status == str(NUMBER['one']):
# use StartTaskSerializer serializer
serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3035'], serializer.data, 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['2060'], 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 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)

View File

@ -1,7 +1,19 @@
"""
notification constants file
"""
REGISTRATION = 1
from base.constants import NUMBER
REGISTRATION = NUMBER['one']
TASK_CREATED = NUMBER['two']
INVITED_GUARDIAN = NUMBER['three']
APPROVED_JUNIOR = NUMBER['four']
REFERRAL_POINTS = NUMBER['five']
TASK_POINTS = NUMBER['six']
TASK_REJECTED = NUMBER['seven']
SKIPPED_PROFILE_SETUP = NUMBER['eight']
TASK_SUBMITTED = NUMBER['nine']
TASK_ACTION = NUMBER['ten']
LEADERBOARD_RANKING = NUMBER['eleven']
REMOVE_JUNIOR = NUMBER['twelve']
TEST_NOTIFICATION = 99
NOTIFICATION_DICT = {
@ -9,6 +21,50 @@ NOTIFICATION_DICT = {
"title": "Successfully registered!",
"body": "You have registered successfully. Now login and complete your profile."
},
TASK_CREATED: {
"title": "Task created!",
"body": "Task created successfully."
},
INVITED_GUARDIAN: {
"title": "Invite guardian",
"body": "Invite guardian successfully"
},
APPROVED_JUNIOR: {
"title": "Approve junior",
"body": "You have request from junior to associate with you"
},
REFERRAL_POINTS: {
"title": "Earn Referral points",
"body": "You earn 5 points for referral."
},
TASK_POINTS: {
"title": "Earn Task points!",
"body": "You earn 5 points for task."
},
TASK_REJECTED: {
"title": "Task rejected!",
"body": "Your task has been rejected."
},
SKIPPED_PROFILE_SETUP: {
"title": "Skipped profile setup!",
"body": "Your guardian has been setup your profile."
},
TASK_SUBMITTED: {
"title": "Task submitted!",
"body": "Your task has been submitted successfully."
},
TASK_ACTION: {
"title": "Task approval!",
"body": "You have request for task approval."
},
LEADERBOARD_RANKING: {
"title": "Leader board rank!",
"body": "Your rank is ."
},
REMOVE_JUNIOR: {
"title": "Disassociate by guardian!",
"body": "Your guardian disassociate you ."
},
TEST_NOTIFICATION: {
"title": "Test Notification",
"body": "This notification is for testing purpose"

View File

@ -6,6 +6,7 @@ from rest_framework import serializers
# local imports
from notifications.utils import register_fcm_token
from notifications.models import Notification
class RegisterDevice(serializers.Serializer):
@ -26,3 +27,19 @@ class RegisterDevice(serializers.Serializer):
device_type = validated_data['type']
return register_fcm_token(self.context['user_id'], registration_id,
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']
class ReadNotificationSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = Notification
fields = ('id',)

View File

@ -6,7 +6,7 @@ from django.urls import path, include
from rest_framework import routers
# local imports
from notifications.views import NotificationViewSet
from notifications.views import NotificationViewSet, ReadNotification
# initiate router
router = routers.SimpleRouter()
@ -15,4 +15,5 @@ router.register('notifications', NotificationViewSet, basename='notifications')
urlpatterns = [
path('api/v1/', include(router.urls)),
path('api/v1/read-notification/', ReadNotification.as_view()),
]

View File

@ -52,7 +52,6 @@ def send_notification(notification_type, from_user_id, to_user_id, extra_data):
""" used to send the push for the given notification type """
(notification_data, from_user, to_user) = get_basic_detail(notification_type, from_user_id, to_user_id)
user_notification_type = UserNotification.objects.filter(user=to_user).first()
# data = notification_data.data
data = notification_data
Notification.objects.create(notification_type=notification_type, notification_from=from_user,
notification_to=to_user, data=data)

View File

@ -3,24 +3,40 @@ notifications views file
"""
# django imports
from django.db.models import Q
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework import viewsets, status, views
# local imports
from account.utils import custom_response
from base.messages import SUCCESS_CODE
from account.utils import custom_response, custom_error_response
from base.messages import SUCCESS_CODE, ERROR_CODE
from notifications.constants import TEST_NOTIFICATION
from notifications.serializers import RegisterDevice
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
from notifications.utils import send_notification
from notifications.models import Notification
class NotificationViewSet(viewsets.GenericViewSet):
""" used to do the notification actions """
serializer_class = None
serializer_class = NotificationListSerializer
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)
def fcm_registration(self, request):
"""
@ -32,7 +48,7 @@ class NotificationViewSet(viewsets.GenericViewSet):
serializer.save()
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):
"""
to send test notification
@ -40,3 +56,33 @@ class NotificationViewSet(viewsets.GenericViewSet):
"""
send_notification.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], {})
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)

View File

@ -8,6 +8,7 @@ async-timeout==4.0.2
billiard==4.1.0
boto3==1.26.157
botocore==1.29.157
CacheControl==0.13.1
cachetools==5.3.1
celery==5.3.1
certifi==2023.5.7
@ -41,8 +42,22 @@ django-timezone-field==5.1
djangorestframework==3.14.0
djangorestframework-simplejwt==5.2.2
drf-yasg==1.21.6
fcm-django==2.0.0
firebase-admin==6.2.0
google-api-core==2.11.1
google-api-python-client==2.93.0
google-auth==2.21.0
google-auth-httplib2==0.1.0
google-cloud-core==2.3.3
google-cloud-firestore==2.11.1
google-cloud-storage==2.10.0
google-crc32c==1.5.0
google-resumable-media==2.5.0
googleapis-common-protos==1.59.1
grpcio==1.56.0
grpcio-status==1.56.0
gunicorn==20.1.0
httplib2==0.22.0
idna==3.4
inflection==0.5.1
itypes==1.2.0
@ -58,12 +73,15 @@ packaging==23.1
phonenumbers==8.13.15
Pillow==9.5.0
prompt-toolkit==3.0.38
proto-plus==1.22.3
protobuf==4.23.4
psycopg==3.1.9
pyasn1==0.5.0
pyasn1-modules==0.3.0
pycparser==2.21
pycryptodome==3.18.0
PyJWT==2.7.0
pyparsing==3.1.0
python-crontab==2.7.1
python-dateutil==2.8.2
python-dotenv==1.0.0
@ -81,5 +99,3 @@ uritemplate==4.1.1
urllib3==1.26.16
vine==5.0.0
wcwidth==0.2.6
fcm-django==2.0.0

View File

@ -5,7 +5,7 @@ web_admin admin file
from django.contrib import admin
# local imports
from web_admin.models import Article, ArticleCard, ArticleSurvey, SurveyOption
from web_admin.models import Article, ArticleCard, ArticleSurvey, SurveyOption, DefaultArticleCardImage
@admin.register(Article)
@ -17,7 +17,7 @@ class ArticleAdmin(admin.ModelAdmin):
@admin.register(ArticleCard)
class ArticleCardAdmin(admin.ModelAdmin):
"""Article Card Admin"""
list_display = ['id', 'article', 'title', 'description', 'image']
list_display = ['id', 'article', 'title', 'description', 'image_url']
@admin.register(ArticleSurvey)
@ -30,3 +30,9 @@ class ArticleSurveyAdmin(admin.ModelAdmin):
class SurveyOptionAdmin(admin.ModelAdmin):
"""Survey Option Admin"""
list_display = ['id', 'survey', 'option', 'is_answer']
@admin.register(DefaultArticleCardImage)
class DefaultArticleCardImagesAdmin(admin.ModelAdmin):
"""Default Article Card Images Option Admin"""
list_display = ['image_name', 'image_url']

View File

@ -0,0 +1,28 @@
# Generated by Django 4.2.2 on 2023-07-24 14:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0002_alter_articlecard_image'),
]
operations = [
migrations.CreateModel(
name='DefaultArticleCardImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image_name', models.CharField(max_length=20)),
('image_url', models.URLField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
),
migrations.RenameField(
model_name='articlecard',
old_name='image',
new_name='image_url',
),
]

View File

@ -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'),
),
]

View File

@ -28,7 +28,7 @@ class ArticleCard(models.Model):
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='article_cards')
title = models.CharField(max_length=255)
description = models.TextField()
image = models.URLField(null=True, blank=True, default=None)
image_url = models.URLField(null=True, blank=True, default=None)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
@ -49,14 +49,14 @@ class ArticleSurvey(models.Model):
def __str__(self):
"""Return title"""
return f'{self.id} | {self.article}'
return f'{self.id} | {self.question}'
class SurveyOption(models.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)
is_answer = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
@ -64,4 +64,18 @@ class SurveyOption(models.Model):
def __str__(self):
"""Return title"""
return f'{self.id} | {self.survey}'
return f'{self.id} | {self.option}'
class DefaultArticleCardImage(models.Model):
"""
Default images upload in oss bucket
"""
image_name = models.CharField(max_length=20)
image_url = models.URLField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
"""return image_name as an object"""
return self.image_name

13
web_admin/pagination.py Normal file
View File

@ -0,0 +1,13 @@
"""
web_admin pagination file
"""
from rest_framework.pagination import PageNumberPagination
class CustomPageNumberPagination(PageNumberPagination):
"""
custom paginator class
"""
page_size = 10 # Set the desired page size
page_size_query_param = 'page_size'
max_page_size = 100 # Set a maximum page size if needed

View File

@ -1,172 +0,0 @@
"""
web_admin serializers file
"""
# django imports
from rest_framework import serializers
from base.constants import ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE_SURVEY, MAX_ARTICLE_SURVEY
# local imports
from base.messages import ERROR_CODE
from guardian.utils import upload_image_to_alibaba
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey
from web_admin.utils import pop_id
class ArticleCardSerializer(serializers.ModelSerializer):
"""
Article Card serializer
"""
id = serializers.IntegerField(required=False)
image = serializers.FileField(required=False)
class Meta:
"""
meta class
"""
model = ArticleCard
fields = ('id', 'title', 'description', 'image')
def create(self, validated_data):
image = validated_data.get('image', '')
filename = f"article/{image.name}"
# upload image on ali baba
validated_data['image'] = upload_image_to_alibaba(image, filename)
article_card = ArticleCard.objects.create(article_id='1', **validated_data)
return article_card
class SurveyOptionSerializer(serializers.ModelSerializer):
"""
survey option serializer
"""
id = serializers.IntegerField(required=False)
class Meta:
"""
meta class
"""
model = SurveyOption
fields = ('id', 'option', 'is_answer')
class ArticleSurveySerializer(serializers.ModelSerializer):
"""
article survey serializer
"""
id = serializers.IntegerField(required=False)
survey_options = SurveyOptionSerializer(many=True)
class Meta:
"""
meta class
"""
model = ArticleSurvey
fields = ('id', 'question', 'survey_options')
class ArticleSerializer(serializers.ModelSerializer):
"""
serializer for article API
"""
article_cards = ArticleCardSerializer(many=True)
article_survey = ArticleSurveySerializer(many=True)
class Meta:
"""
meta class
"""
model = Article
fields = ('id', 'title', 'description', 'article_cards', 'article_survey')
def validate(self, attrs):
"""
to validate request data
:param attrs:
:return: validated attrs
"""
article_cards = attrs.get('article_cards', None)
article_survey = attrs.get('article_survey', None)
if article_cards is None or len(article_cards) > int(MAX_ARTICLE_CARD):
raise serializers.ValidationError({'details': ERROR_CODE['2039']})
if article_survey is None or len(article_survey) < int(MIN_ARTICLE_SURVEY) or int(
MAX_ARTICLE_SURVEY) < len(article_survey):
raise serializers.ValidationError({'details': ERROR_CODE['2040']})
return attrs
def create(self, validated_data):
"""
to create article.
ID in post data dict is for update api.
:param validated_data:
:return: article object
"""
article_cards = validated_data.pop('article_cards')
article_survey = validated_data.pop('article_survey')
article = Article.objects.create(**validated_data)
for card in article_cards:
card = pop_id(card)
ArticleCard.objects.create(article=article, **card)
for survey in article_survey:
survey = pop_id(survey)
options = survey.pop('survey_options')
survey_obj = ArticleSurvey.objects.create(article=article, points=ARTICLE_SURVEY_POINTS, **survey)
for option in options:
option = pop_id(option)
SurveyOption.objects.create(survey=survey_obj, **option)
return article
def update(self, instance, validated_data):
"""
to update article and related table
:param instance:
:param validated_data:
:return: article object
"""
article_cards = validated_data.pop('article_cards')
article_survey = validated_data.pop('article_survey')
instance.title = validated_data.get('title', instance.title)
instance.description = validated_data.get('description', instance.description)
instance.save()
# Update or create cards
for card_data in article_cards:
card_id = card_data.get('id', None)
if card_id:
card = ArticleCard.objects.get(id=card_id, article=instance)
card.title = card_data.get('title', card.title)
card.description = card_data.get('description', card.description)
card.image = card_data.get('image', card.image)
card.save()
else:
card_data = pop_id(card_data)
ArticleCard.objects.create(article=instance, **card_data)
# Update or create survey sections
for survey_data in article_survey:
survey_id = survey_data.get('id', None)
options_data = survey_data.pop('survey_options')
if survey_id:
survey = ArticleSurvey.objects.get(id=survey_id, article=instance)
survey.question = survey_data.get('question', survey.question)
survey.save()
else:
survey_data = pop_id(survey_data)
survey = ArticleSurvey.objects.create(article=instance, **survey_data)
# Update or create survey options
for option_data in options_data:
option_id = option_data.get('id', None)
if option_id:
option = SurveyOption.objects.get(id=option_id, survey=survey)
option.option = option_data.get('option', option.option)
option.is_answer = option_data.get('is_answer', option.is_answer)
option.save()
else:
option_data = pop_id(option_data)
SurveyOption.objects.create(survey=survey, **option_data)
return instance

View File

View File

@ -0,0 +1,53 @@
"""
web_admin analytics serializer file
"""
from rest_framework import serializers
from junior.models import JuniorPoints, Junior
from web_admin.serializers.user_management_serializer import JuniorSerializer
class JuniorLeaderboardSerializer(serializers.ModelSerializer):
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')
@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):
junior = JuniorLeaderboardSerializer()
rank = serializers.IntegerField()
class Meta:
model = JuniorPoints
fields = ('total_points', 'rank', 'junior')

View File

@ -0,0 +1,333 @@
"""
web_admin serializers file
"""
# django imports
from rest_framework import serializers
from django.contrib.auth import get_user_model
from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE_SURVEY, MAX_ARTICLE_SURVEY, NUMBER,
USER_TYPE, ARTICLE_CARD_IMAGE_FOLDER)
# local imports
from base.messages import ERROR_CODE
from guardian.utils import upload_image_to_alibaba
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage
from web_admin.utils import pop_id, get_image_url
from junior.models import JuniorArticlePoints, JuniorArticle
USER = get_user_model()
class ArticleCardSerializer(serializers.ModelSerializer):
"""
Article Card serializer
"""
id = serializers.IntegerField(required=False)
image_name = serializers.CharField(required=False)
image_url = serializers.CharField(required=False)
class Meta:
"""
meta class
"""
model = ArticleCard
fields = ('id', 'title', 'description', 'image_name', 'image_url')
def create(self, validated_data):
validated_data['image_url'] = get_image_url(validated_data)
article = Article.objects.all().first()
article_card = ArticleCard.objects.create(article=article, **validated_data)
return article_card
class SurveyOptionSerializer(serializers.ModelSerializer):
"""
survey option serializer
"""
id = serializers.IntegerField(required=False)
class Meta:
"""
meta class
"""
model = SurveyOption
fields = ('id', 'option', 'is_answer')
class ArticleSurveySerializer(serializers.ModelSerializer):
"""
article survey serializer
"""
id = serializers.IntegerField(required=False)
options = SurveyOptionSerializer(many=True)
class Meta:
"""
meta class
"""
model = ArticleSurvey
fields = ('id', 'question', 'options')
class ArticleSerializer(serializers.ModelSerializer):
"""
serializer for article API
"""
article_cards = ArticleCardSerializer(many=True)
article_survey = ArticleSurveySerializer(many=True)
class Meta:
"""
meta class
"""
model = Article
fields = ('id', 'title', 'description', 'article_cards', 'article_survey')
def validate(self, attrs):
"""
to validate request data
:return: validated attrs
"""
article_cards = attrs.get('article_cards', None)
article_survey = attrs.get('article_survey', None)
if article_cards is None or len(article_cards) > int(MAX_ARTICLE_CARD):
raise serializers.ValidationError({'details': ERROR_CODE['2039']})
if article_survey is None or len(article_survey) < int(MIN_ARTICLE_SURVEY) or int(
MAX_ARTICLE_SURVEY) < len(article_survey):
raise serializers.ValidationError({'details': ERROR_CODE['2040']})
return attrs
def create(self, validated_data):
"""
to create article.
ID in post data dict is for update api.
:return: article object
"""
article_cards = validated_data.pop('article_cards')
article_survey = validated_data.pop('article_survey')
article = Article.objects.create(**validated_data)
for card in article_cards:
card = pop_id(card)
card['image_url'] = get_image_url(card)
ArticleCard.objects.create(article=article, **card)
for survey in article_survey:
survey = pop_id(survey)
options = survey.pop('options')
survey_obj = ArticleSurvey.objects.create(article=article, points=ARTICLE_SURVEY_POINTS, **survey)
for option in options:
option = pop_id(option)
SurveyOption.objects.create(survey=survey_obj, **option)
return article
def update(self, instance, validated_data):
"""
to update article and related table
:param instance: article object,
:return: article object
"""
article_cards = validated_data.pop('article_cards')
article_survey = validated_data.pop('article_survey')
instance.title = validated_data.get('title', instance.title)
instance.description = validated_data.get('description', instance.description)
instance.save()
prev_card = list(ArticleCard.objects.filter(article=instance).values_list('id', flat=True))
# Update or create cards
for card_data in article_cards:
card_id = card_data.get('id', None)
if card_id:
prev_card.remove(card_id)
card = ArticleCard.objects.get(id=card_id, article=instance)
card.title = card_data.get('title', card.title)
card.description = card_data.get('description', card.description)
card.image_url = get_image_url(card_data)
card.save()
else:
card_data = pop_id(card_data)
card_data['image_url'] = get_image_url(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
for survey_data in article_survey:
survey_id = survey_data.get('id', None)
options_data = survey_data.pop('options')
if survey_id:
prev_survey.remove(survey_id)
survey = ArticleSurvey.objects.get(id=survey_id, article=instance)
survey.question = survey_data.get('question', survey.question)
survey.save()
else:
survey_data = pop_id(survey_data)
survey = ArticleSurvey.objects.create(article=instance, points=ARTICLE_SURVEY_POINTS, **survey_data)
# Update or create survey options
for option_data in options_data:
option_id = option_data.get('id', None)
if option_id:
option = SurveyOption.objects.get(id=option_id, survey=survey)
option.option = option_data.get('option', option.option)
option.is_answer = option_data.get('is_answer', option.is_answer)
option.save()
else:
option_data = pop_id(option_data)
SurveyOption.objects.create(survey=survey, **option_data)
ArticleSurvey.objects.filter(id__in=prev_survey, article=instance).delete()
return instance
class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
"""
Article Card serializer
"""
image = serializers.FileField(required=False)
image_url = serializers.URLField(required=False)
class Meta:
"""
meta class
"""
model = DefaultArticleCardImage
fields = ('image_name', 'image', 'image_url')
def validate(self, attrs):
"""
to validate data
:return: validated data
"""
if 'image' not in attrs and attrs.get('image') is None:
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
def create(self, validated_data):
"""
to create and upload image
:return: card_image object
"""
validated_data['image_url'] = get_image_url(validated_data)
card_image = DefaultArticleCardImage.objects.create(**validated_data)
return card_image
class ArticleListSerializer(serializers.ModelSerializer):
"""
serializer for article API
"""
article_cards = ArticleCardSerializer(many=True)
article_survey = ArticleSurveySerializer(many=True)
total_points = serializers.SerializerMethodField('get_total_points')
is_completed = serializers.SerializerMethodField('get_is_completed')
class Meta:
"""
meta class
"""
model = Article
fields = ('id', 'title', 'description', 'article_cards', 'article_survey', 'total_points', 'is_completed')
def get_total_points(self, obj):
"""total points of article"""
total_question = ArticleSurvey.objects.filter(article=obj).count()
return total_question * NUMBER['five']
def get_is_completed(self, obj):
"""complete all question"""
junior_article = JuniorArticle.objects.filter(article=obj).last()
if junior_article:
junior_article.is_completed
return False
class ArticleQuestionSerializer(serializers.ModelSerializer):
"""
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:
"""
meta class
"""
model = ArticleSurvey
fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer')
class StartAssessmentSerializer(serializers.ModelSerializer):
"""
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:
"""
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:
"""
meta class
"""
model = ArticleCard
fields = ('id', 'title', 'description', 'image_name', 'image_url', 'current_page')

View File

@ -0,0 +1,134 @@
"""
web_admin auth serializers file
"""
# python imports
from datetime import datetime
# django imports
from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.utils import timezone
# local imports
from account.models import UserEmailOtp
from base.constants import USER_TYPE
from base.messages import ERROR_CODE
from guardian.tasks import generate_otp
from base.tasks import send_email_otp
USER = get_user_model()
class AdminOTPSerializer(serializers.ModelSerializer):
"""
admin forgot password serializer
"""
email = serializers.EmailField()
class Meta:
"""
meta class
"""
model = USER
fields = ('email',)
def validate(self, attrs):
""" used to validate the incoming data """
user = USER.objects.filter(email=attrs.get('email')).first()
if not user:
raise serializers.ValidationError({'details': ERROR_CODE['2004']})
elif not user.is_superuser:
raise serializers.ValidationError({'details': ERROR_CODE['2063']})
attrs.update({'user': user})
return attrs
def create(self, validated_data):
"""
to send otp
:return: user_data
"""
email = validated_data['email']
verification_code = generate_otp()
# Send the verification code to the user's email
send_email_otp.delay(email, verification_code)
expiry = timezone.now() + timezone.timedelta(days=1)
user_data, created = UserEmailOtp.objects.update_or_create(email=email,
defaults={
"otp": verification_code,
"expired_at": expiry,
"user_type": dict(USER_TYPE).get('3'),
})
return user_data
class AdminVerifyOTPSerializer(serializers.Serializer):
"""
admin verify otp serializer
"""
email = serializers.EmailField()
otp = serializers.CharField(max_length=6, min_length=6)
class Meta:
""" meta class """
fields = ('email', 'otp',)
def validate(self, attrs):
"""
to validate data
:return: validated data
"""
email = attrs.get('email')
otp = attrs.get('otp')
# fetch email otp object of the user
user_otp_details = UserEmailOtp.objects.filter(email=email, otp=otp).last()
if not user_otp_details:
raise serializers.ValidationError({'details': ERROR_CODE['2008']})
if user_otp_details.user_type != dict(USER_TYPE).get('3'):
raise serializers.ValidationError({'details': ERROR_CODE['2008']})
if user_otp_details.expired_at.replace(tzinfo=None) < datetime.utcnow():
raise serializers.ValidationError({'details': ERROR_CODE['2029']})
user_otp_details.is_verified = True
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

View File

@ -0,0 +1,298 @@
"""
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
# 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')
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')
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()
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: 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')
@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_associated_users(obj):
"""
:param obj: user object
:return: associated user
"""
if profile := obj.guardian_profile.all().first():
if profile.guardian_code:
junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code], is_verified=True)
serializer = JuniorSerializer(junior, many=True)
return serializer.data
elif profile := obj.junior_profile.all().first():
if profile.guardian_code:
guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code, is_verified=True)
serializer = GuardianSerializer(guardian, many=True)
return serializer.data
else:
return None

View File

@ -6,12 +6,25 @@ from django.urls import path, include
from rest_framework import routers
# local imports
from web_admin.views import ArticleViewSet
from web_admin.views.analytics import AnalyticsViewSet
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
router = routers.SimpleRouter()
router.register('article', ArticleViewSet, basename='article')
router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images')
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
router.register('admin', ForgotAndResetPasswordViewSet, basename='admin')
urlpatterns = [
path('api/v1/', include(router.urls)),

View File

@ -1,6 +1,10 @@
"""
web_utils file
"""
import base64
from base.constants import ARTICLE_CARD_IMAGE_FOLDER
from guardian.utils import upload_image_to_alibaba
def pop_id(data):
@ -11,3 +15,28 @@ def pop_id(data):
"""
data.pop('id') if 'id' in data else 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 = f"{data['title']} {data.pop('image_name')}" if 'image_name' in data else data['title']
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image_name}"
# upload image on ali baba
image_url = upload_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

View File

View File

@ -0,0 +1,137 @@
"""
web_admin analytics view file
"""
# python imports
import datetime
# 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, OuterRef, Subquery, Sum
from django.db.models.functions import TruncDate
from django.db.models import F, Window
from django.db.models.functions.window import Rank
# local imports
from account.utils import custom_response
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, EXPIRED
from guardian.models import JuniorTask
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
USER = get_user_model()
class AnalyticsViewSet(GenericViewSet):
"""
analytics api view
"""
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: query params {start_date and end_date}, date format (yyyy-mm-dd)
:return:
"""
end_date = datetime.date.today()
start_date = end_date - datetime.timedelta(days=6)
if request.query_params.get('start_date') and request.query_params.get('end_date'):
start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d')
end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d')
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: query params {start_date and end_date}, date format (yyyy-mm-dd)
:return:
"""
end_date = datetime.date.today()
start_date = end_date - datetime.timedelta(days=6)
if request.query_params.get('start_date') and request.query_params.get('end_date'):
start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d')
end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d')
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
:param request: query params {start_date and end_date}, date format (yyyy-mm-dd)
:return:
"""
end_date = datetime.date.today()
start_date = end_date - datetime.timedelta(days=6)
if request.query_params.get('start_date') and request.query_params.get('end_date'):
start_date = datetime.datetime.strptime(request.query_params.get('start_date'), '%Y-%m-%d')
end_date = datetime.datetime.strptime(request.query_params.get('end_date'), '%Y-%m-%d')
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):
"""
:param request:
:return:
"""
# queryset = JuniorPoints.objects.all().order_by('-total_points', 'junior__created_at')
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)

View File

@ -3,18 +3,23 @@ web_admin views file
"""
# django imports
from rest_framework.viewsets import GenericViewSet, mixins
from rest_framework.response import Response
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.permissions import IsAuthenticated
from rest_framework.permissions import IsAuthenticated, AllowAny
from django.contrib.auth import get_user_model
# 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 web_admin.models import Article, ArticleCard, ArticleSurvey
from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticleCardImage
from web_admin.permission import AdminPermission
from web_admin.serializers import ArticleSerializer, ArticleCardSerializer
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer,
DefaultArticleCardImageSerializer, ArticleListSerializer,
ArticleCardlistSerializer)
USER = get_user_model()
class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModelMixin,
@ -25,13 +30,13 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticated, AdminPermission]
queryset = Article
filter_backends = (OrderingFilter, SearchFilter,)
filter_backends = (SearchFilter,)
search_fields = ['title']
http_method_names = ['get', 'post', 'put', 'delete']
def get_queryset(self):
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')
queryset = self.filter_queryset(article)
return queryset
@ -72,10 +77,11 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
:return: list of article
"""
queryset = self.get_queryset()
count = queryset.count()
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)
return custom_response(None, data=serializer.data, count=count)
def retrieve(self, request, *args, **kwargs):
"""
@ -104,8 +110,8 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
return custom_response(SUCCESS_CODE["3029"])
return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST)
@action(methods=['get'], url_name='remove_card', url_path='remove_card',
detail=True, serializer_class=None)
@action(methods=['get'], url_name='remove-card', url_path='remove-card',
detail=True)
def remove_article_card(self, request, *args, **kwargs):
"""
article card remove (delete) api method
@ -120,8 +126,8 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
except AttributeError:
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',
detail=True, serializer_class=None)
@action(methods=['get'], url_name='remove-survey', url_path='remove-survey',
detail=True)
def remove_article_survey(self, request, *args, **kwargs):
"""
article survey remove (delete) api method
@ -137,7 +143,7 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
return custom_error_response(ERROR_CODE["2043"], response_status=status.HTTP_400_BAD_REQUEST)
@action(methods=['post'], url_name='test-add-card', url_path='test-add-card',
detail=False, serializer_class=ArticleCardSerializer)
detail=False, serializer_class=ArticleCardSerializer, permission_classes=[AllowAny])
def add_card(self, request):
"""
:param request:
@ -147,3 +153,96 @@ class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModel
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE["3000"])
@action(methods=['get'], url_name='test-list-card', url_path='test-list-card',
detail=False, serializer_class=ArticleCardSerializer, permission_classes=[AllowAny])
def list_card(self, request):
"""
:param request:
:return:
"""
queryset = ArticleCard.objects.all()
serializer = self.serializer_class(queryset, many=True)
return custom_response(None, serializer.data)
class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.ListModelMixin):
"""
api to upload and list default article card images
"""
serializer_class = DefaultArticleCardImageSerializer
permission_classes = [IsAuthenticated, AdminPermission]
queryset = DefaultArticleCardImage.objects.all()
def create(self, request, *args, **kwargs):
"""
api method to upload default article card images
:param request:
:return: success message
"""
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE["3000"])
def list(self, request, *args, **kwargs):
"""
api method to list default article card images
:param request:
:return: default article card images
"""
queryset = self.queryset
serializer = self.serializer_class(queryset, many=True)
return custom_response(None, data=serializer.data)
class ArticleListViewSet(GenericViewSet, mixins.ListModelMixin):
"""
article api
"""
serializer_class = ArticleListSerializer
permission_classes = [IsAuthenticated,]
queryset = Article
http_method_names = ['get',]
def get_queryset(self):
article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
'article_cards', 'article_survey', 'article_survey__options'
).order_by('-created_at')
queryset = self.filter_queryset(article)
return queryset
def list(self, request, *args, **kwargs):
"""
article list api method
:param request:
:param args:
:param kwargs:
:return: list of article
"""
queryset = self.get_queryset()
count = queryset.count()
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)
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)

63
web_admin/views/auth.py Normal file
View File

@ -0,0 +1,63 @@
"""
web_admin auth views file
"""
# django imports
from rest_framework.viewsets import GenericViewSet
from rest_framework.decorators import action
from rest_framework import status
from django.contrib.auth import get_user_model
# local imports
from account.utils import custom_response, custom_error_response
from base.messages import SUCCESS_CODE, ERROR_CODE
from web_admin.serializers.auth_serializer import (AdminOTPSerializer, AdminVerifyOTPSerializer,
AdminCreatePasswordSerializer)
USER = get_user_model()
class ForgotAndResetPasswordViewSet(GenericViewSet):
"""
to reset admin password
"""
queryset = None
@action(methods=['post'], url_name='otp', url_path='otp',
detail=False, serializer_class=AdminOTPSerializer)
def admin_otp(self, request):
"""
api method to send otp
:return: success message
"""
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
serializer.save()
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',
detail=False, serializer_class=AdminVerifyOTPSerializer)
def admin_verify_otp(self, request):
"""
api method to verify otp
:return: success message
"""
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
return custom_response(SUCCESS_CODE['3011'])
return custom_error_response(ERROR_CODE['2008'], status.HTTP_400_BAD_REQUEST)
@action(methods=['post'], url_name='create-password', url_path='create-password',
detail=False, serializer_class=AdminCreatePasswordSerializer)
def admin_create_password(self, request):
"""
api method to create new password
:return: success message
"""
serializer = self.serializer_class(data=request.data)
if serializer.is_valid():
user = USER.objects.filter(email=serializer.validated_data.get('email')).first()
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)

View File

@ -0,0 +1,126 @@
"""
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 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
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
queryset = queryset.filter(guardian_profile__user__id=kwargs['pk'])
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
queryset = queryset.filter(junior_profile__auth__id=kwargs['pk'])
serializer = UserManagementDetailSerializer(queryset, many=True)
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)
queryset = self.queryset
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
user_obj = queryset.filter(guardian_profile__user__id=kwargs['pk']).first()
serializer = GuardianSerializer(user_obj.guardian_profile.all().first(),
request.data, context={'user_id': kwargs['pk']})
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
user_obj = queryset.filter(junior_profile__auth__id=kwargs['pk']).first()
serializer = JuniorSerializer(user_obj.junior_profile.all().first(),
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(guardian_profile__user__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(junior_profile__auth__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'])

View File

@ -32,3 +32,14 @@ app.autodiscover_tasks()
def debug_task(self):
""" celery debug task """
print(f'Request: {self.request!r}')
"""cron task"""
app.conf.beat_schedule = {
"expired_task": {
"task": "guardian.utils.update_expired_task_status",
"schedule": crontab(minute=0, hour=0),
},
}

View File

@ -61,6 +61,7 @@ INSTALLED_APPS = [
'django.contrib.postgres',
'rest_framework',
'fcm_django',
'django_celery_beat',
# Add your custom apps here.
'django_ses',
'account',
@ -119,6 +120,7 @@ REST_FRAMEWORK = {
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(hours=2, minutes=59, seconds=59, microseconds=999999),
'REFRESH_TOKEN_LIFETIME': timedelta(hours=71, minutes=59, seconds=59, microseconds=999999),
}
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
@ -175,6 +177,31 @@ AUTH_PASSWORD_VALIDATORS = [
},
]
LOGGING = {
"version": 1,
"filters": {
"require_debug_true": {
"()": "django.utils.log.RequireDebugTrue"
}
},
"handlers": {
"console": {
"level": "DEBUG",
"filters": [
"require_debug_true"
],
"class": "logging.StreamHandler"
}
},
"loggers": {
"django.db.backends": {
"level": "DEBUG",
"handlers": [
"console"
]
}
}
}
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
@ -257,6 +284,7 @@ DEFAULT_ADDRESS = os.getenv("DEFAULT_ADDRESS")
# ali baba cloud settings
ALIYUN_OSS_ACCESS_KEY_ID = os.getenv('ALIYUN_OSS_ACCESS_KEY_ID')
ALIYUN_OSS_ACCESS_KEY_SECRET = os.getenv('ALIYUN_OSS_ACCESS_KEY_SECRET')
ALIYUN_OSS_BUCKET_NAME = os.getenv('ALIYUN_OSS_BUCKET_NAME')