Compare commits

..
Author SHA1 Message Date
jain 04ed9c668c remove print statement for expiry date 2023-08-17 13:24:20 +05:30
jain 36427b50c1 print statement for expiry date 2023-08-17 12:24:33 +05:30
dilipshrivastwa-kiwi 5da07002e0 Merge pull request #217 from KiwiTechLLC/ZBKBCK-47
notification modification added more payload
2023-08-16 16:48:53 +05:30
abutalib-kiwi 3017b0ece0 changes in notification method 2023-08-16 16:40:34 +05:30
abutalib-kiwi e73113fcca Merge branch 'dev' into ZBKBCK-47 2023-08-16 16:39:36 +05:30
dilipshrivastwa-kiwi c278670c39 Merge pull request #214 from KiwiTechLLC/sprint5
email verification and user notification bug
2023-08-16 16:28:58 +05:30
jain 60af098e1e email verfication and user notification bug 2023-08-16 16:16:05 +05:30
Ankitajain-kiwi d29a60558f Merge pull request #211 from KiwiTechLLC/sprint5
user type
2023-08-16 11:38:12 +05:30
jain 451c3bdae7 user type 2023-08-16 11:37:21 +05:30
jain 7c809776b6 guardian code status 2023-08-14 18:22:50 +05:30
abutalib-kiwi e46d649fdc notification modification added more payload 2023-08-14 16:03:36 +05:30
dilipshrivastwa-kiwi adb827f5a0 Merge pull request #206 from KiwiTechLLC/sprint4
Sprint4
2023-08-14 15:26:27 +05:30
jain a02dfd4e31 login api with user type 2023-08-14 15:23:41 +05:30
jain d3b0be953e login api with user type 2023-08-14 15:16:16 +05:30
jain 11b9f00285 Merge branch 'login_api_reduced_queries' of github.com:KiwiTechLLC/ZODBank-Backend into login_api_reduced_queries 2023-08-14 15:01:43 +05:30
dilipshrivastwa-kiwi 08dc9f8538 Merge pull request #205 from KiwiTechLLC/sprint4
sonar fixes
2023-08-14 12:20:48 +05:30
jain 6373d1e02c sonar fixes 2023-08-14 12:13:14 +05:30
Ankitajain-kiwi a1d959299a Merge pull request #202 from KiwiTechLLC/sprint4
is complete key
2023-08-14 11:45:37 +05:30
jain 070637bf1d is complete key 2023-08-14 11:43:59 +05:30
Ankitajain-kiwi fd9a4902ae Merge pull request #199 from KiwiTechLLC/sprint4
guardian code chnages
2023-08-11 16:55:21 +05:30
jain 082f93ff9d guardian code chnages 2023-08-11 16:54:27 +05:30
dilipshrivastwa-kiwi 25eaee31a4 Merge pull request #198 from KiwiTechLLC/ZBKADM-71
added pagination in leaderboard admin
2023-08-11 16:02:13 +05:30
abutalib-kiwi d5c30f2029 added pagination in leaderboard admin 2023-08-11 15:56:22 +05:30
Ankitajain-kiwi cae28e0b54 Merge pull request #197 from KiwiTechLLC/sprint4
is complete article key
2023-08-11 15:41:35 +05:30
jain 54200dba52 is complete article key 2023-08-11 15:39:25 +05:30
dilipshrivastwa-kiwi 846a9d42d4 Merge pull request #196 from KiwiTechLLC/ZBKADM-72-sonar-issues
sonar issues
2023-08-11 14:44:25 +05:30
abutalib-kiwi 69c19cf097 sonar issues 2023-08-11 14:40:49 +05:30
dilipshrivastwa-kiwi 18cb885410 Merge pull request #193 from KiwiTechLLC/ZBKADM-71
leaderboard ranking
2023-08-11 14:13:05 +05:30
Ankitajain-kiwi 4d7209df19 Merge pull request #194 from KiwiTechLLC/sprint4
remove deleted user from leaderboard
2023-08-11 13:40:48 +05:30
jain 94c76b7f2e remove deleted user from leaderboard 2023-08-11 13:39:25 +05:30
abutalib-kiwi af8ea39200 leaderboard ranking 2023-08-11 13:37:44 +05:30
Ankitajain-kiwi 6e9304b2cc Merge pull request #192 from KiwiTechLLC/sprint4
is complete article key
2023-08-11 13:04:54 +05:30
jain 5eab8f4907 is complete article key 2023-08-11 13:04:05 +05:30
dilipshrivastwa-kiwi f750487b88 Merge pull request #190 from KiwiTechLLC/sprint4
add correct and attempt answer
2023-08-11 12:21:47 +05:30
jain 8b0032f6d2 junior point table 2023-08-11 12:18:35 +05:30
jain bb65f81200 add correct and attempt answer 2023-08-11 11:48:02 +05:30
dilipshrivastwa-kiwi 04db40e100 Merge pull request #189 from KiwiTechLLC/sprint4
create article
2023-08-10 21:24:15 +05:30
jain 1e162255d7 create article 2023-08-10 20:17:24 +05:30
dilipshrivastwa-kiwi a479f3cb62 Merge pull request #187 from KiwiTechLLC/sprint4
task points
2023-08-10 19:06:32 +05:30
jain fbd6f9ece5 task points 2023-08-10 19:05:33 +05:30
jain 11c84208b7 task points 2023-08-10 19:00:37 +05:30
Ruman Siddiqui 4f02cef0f9 Optimised login api 2023-08-10 18:43:03 +05:30
dilipshrivastwa-kiwi 80dffb7942 Merge pull request #184 from KiwiTechLLC/ZBKADM-71
image name fix, remove '|' character
2023-08-10 18:21:10 +05:30
abutalib-kiwi 0a8c2cf56d image name fix, remove '|' character 2023-08-10 18:17:49 +05:30
dilipshrivastwa-kiwi 03fb4f4176 Merge pull request #182 from KiwiTechLLC/sprint4
current page update in article api
2023-08-10 15:51:09 +05:30
jain 22afe7e555 current page update in article api 2023-08-10 15:43:31 +05:30
jain 69723b362f current page update in article api 2023-08-10 15:30:46 +05:30
Ankitajain-kiwi 9e612c7171 Merge pull request #181 from KiwiTechLLC/sprint4
junior points
2023-08-10 12:20:34 +05:30
jain 7a9be0326a junior points 2023-08-10 12:19:08 +05:30
Ankitajain-kiwi aaa1c55227 Merge pull request #180 from KiwiTechLLC/sprint4
guardian code
2023-08-10 12:07:49 +05:30
jain 656f0da89a guardian code 2023-08-10 12:06:52 +05:30
Ankitajain-kiwi 28e71e132c Merge pull request #178 from KiwiTechLLC/sprint4
Sprint4
2023-08-10 11:56:53 +05:30
jain b30643299f junior points 2023-08-10 11:56:19 +05:30
jain b6b70af13f remove guardian code request 2023-08-10 11:02:32 +05:30
jain 22dd7fc10b remove guardian code request 2023-08-10 10:55:33 +05:30
Ankitajain-kiwi 924bc20193 Merge pull request #176 from KiwiTechLLC/sprint4
points
2023-08-09 19:21:50 +05:30
jain af7582b9e2 points 2023-08-09 19:20:56 +05:30
Ankitajain-kiwi 856d27d803 Merge pull request #175 from KiwiTechLLC/sprint4
points remove from serializer
2023-08-09 18:54:12 +05:30
jain 19f56280e4 points remove from serializer 2023-08-09 18:53:18 +05:30
Ankitajain-kiwi d3800dbc85 Merge pull request #173 from KiwiTechLLC/sprint4
guardian code update
2023-08-09 18:10:18 +05:30
dilipshrivastwa-kiwi 9373dc4697 Merge pull request #172 from KiwiTechLLC/ZBKADM-71
analytics section api, users counts, new signup count, task report
2023-08-09 17:20:53 +05:30
jain 32eaa6c3f2 guardian code update 2023-08-09 17:18:07 +05:30
abutalib-kiwi 11ff8fc700 conflict resolved 2023-08-09 17:03:42 +05:30
abutalib-kiwi 9c75cb1615 analytics section api, users counts, new signup count, task report 2023-08-09 16:57:19 +05:30
dilipshrivastwa-kiwi 3f8b0e2eb7 Merge pull request #171 from KiwiTechLLC/sprint4
financial learning section api
2023-08-09 16:44:27 +05:30
jain 69ce6857e8 financial learning section api 2023-08-09 16:36:33 +05:30
Ankitajain-kiwi 3efa31efe4 Merge pull request #169 from KiwiTechLLC/sprint4
Sprint4
2023-08-09 14:39:01 +05:30
jain 275b09d2ea Merge branch 'sprint4' of github.com:KiwiTechLLC/ZODBank-Backend into sprint4 2023-08-09 14:38:09 +05:30
jain 3cb0e3ed8b guardian code add in junior list api 2023-08-09 14:37:38 +05:30
dilipshrivastwa-kiwi 823c5ea4c4 Merge pull request #168 from KiwiTechLLC/ZBKADM-69
otp, verify otp and login bug resolved
2023-08-09 14:21:58 +05:30
abutalib-kiwi c9ee482512 otp, verify otp and login bug resolved 2023-08-09 14:16:20 +05:30
dilipshrivastwa-kiwi 08750813ce Merge pull request #167 from KiwiTechLLC/sprint4
Sprint4
2023-08-09 13:54:08 +05:30
Ankitajain-kiwi 75f4f66285 Merge branch 'dev' into sprint4 2023-08-09 13:49:39 +05:30
jain 881bda739b article card api, check answer api 2023-08-09 13:48:02 +05:30
jain d86f082e58 article card api, check answer api 2023-08-09 13:37:06 +05:30
Ankitajain-kiwi b12c5071fe Merge pull request #164 from KiwiTechLLC/sprint4
changes in add junior api
2023-08-08 16:37:08 +05:30
jain f75201b3dd changes in add junior api 2023-08-08 16:36:03 +05:30
Ankitajain-kiwi 45ea7013f8 Merge pull request #162 from KiwiTechLLC/sprint4
junior list api
2023-08-08 16:06:52 +05:30
jain 7d428f5eb5 junior list api 2023-08-08 16:05:56 +05:30
Ankitajain-kiwi 9ff64e64ef Merge pull request #160 from KiwiTechLLC/sprint4
top junior api
2023-08-08 15:04:03 +05:30
jain 4b23394569 top junior api 2023-08-08 15:03:24 +05:30
dilipshrivastwa-kiwi 81893c8841 Merge pull request #159 from KiwiTechLLC/ZBKADM-69
change in admin login
2023-08-08 14:38:43 +05:30
abutalib-kiwi 10a1ea9b76 change in admin login 2023-08-08 14:29:57 +05:30
dilipshrivastwa-kiwi 7fca493d0d Merge pull request #157 from KiwiTechLLC/sprint4
junior position changes and decrease api response time
2023-08-08 14:02:27 +05:30
jain 85e4ae8761 junior position changes and decrease api response time 2023-08-08 13:54:04 +05:30
dilipshrivastwa-kiwi 6ebd5ae410 Merge pull request #156 from KiwiTechLLC/sprint4
guardian list api changes
2023-08-08 12:35:43 +05:30
jain f57b111555 guardian list api changes 2023-08-08 11:48:53 +05:30
jain 6596414675 guardian list api changes 2023-08-08 11:46:27 +05:30
dilipshrivastwa-kiwi ed7fd3c15d Merge pull request #155 from KiwiTechLLC/ZBKADM-69
notification list modified and mark as read method changed
2023-08-07 20:48:37 +05:30
dilipshrivastwa-kiwi 8cf7148114 Merge pull request #154 from KiwiTechLLC/ZBKADM-69
change method to get real time
2023-08-07 19:19:00 +05:30
Ankitajain-kiwi b98443479f Merge pull request #153 from KiwiTechLLC/sprint4
junior article points table
2023-08-07 19:12:55 +05:30
jain f917244265 junior article points table 2023-08-07 19:12:14 +05:30
dilipshrivastwa-kiwi ceb5bc13c3 Merge pull request #152 from KiwiTechLLC/ZBKADM-69
task list api modified
2023-08-07 18:22:20 +05:30
jain ae0fc4fe8d article list api 2023-08-07 17:06:31 +05:30
dilipshrivastwa-kiwi 0a1b9c7e70 Merge pull request #151 from KiwiTechLLC/ZBKADM-69
changes in edit api, included first name, last name and username
2023-08-07 15:14:46 +05:30
abutalib-kiwi f2cf1488e9 changes in edit api, included first name, last name and username 2023-08-07 15:12:54 +05:30
dilipshrivastwa-kiwi 0426974539 Merge pull request #150 from KiwiTechLLC/sprint4
guardian list api
2023-08-07 13:44:20 +05:30
Ankitajain-kiwi b0e26f41b9 Merge branch 'dev' into sprint4 2023-08-07 13:43:49 +05:30
jain c20249730a guardian list api 2023-08-07 13:42:47 +05:30
jain b3b499e661 guardian list api 2023-08-07 13:41:33 +05:30
jain f377e283fd guardian list api 2023-08-07 13:25:01 +05:30
dilipshrivastwa-kiwi 648a2ec4d5 Merge pull request #149 from KiwiTechLLC/ZBKADM-69
admin login api modified
2023-08-07 12:13:51 +05:30
abutalib-kiwi 83ec922584 admin login api modified 2023-08-07 12:10:36 +05:30
jain 88221ec77a notifications API 2023-08-07 11:28:34 +05:30
jain e853346910 notifications API 2023-08-07 11:26:23 +05:30
dilipshrivastwa-kiwi a088764b7b Merge pull request #148 from KiwiTechLLC/ZBKADM-69
edit user validations, active-inactive api
2023-08-07 11:04:20 +05:30
abutalib-kiwi 4a2f36cde8 edit user validations, active-inactive api 2023-08-07 11:01:37 +05:30
dilipshrivastwa-kiwi 401ee1ddf8 Merge pull request #147 from KiwiTechLLC/ZBKADM-69
Zbkadm 69
2023-08-04 16:46:05 +05:30
abutalib-kiwi baacb1a18f user edit api 2023-08-04 16:37:46 +05:30
abutalib-kiwi 5f1c645e3a user list api 2023-08-04 16:27:46 +05:30
dilipshrivastwa-kiwi 79648637aa Merge pull request #145 from KiwiTechLLC/ZBKADM-69
user detail view api, searching user list, count
2023-08-04 13:21:47 +05:30
abutalib-kiwi 756bea0471 user detail view api, searching user list, count 2023-08-04 13:15:32 +05:30
dilipshrivastwa-kiwi 465632f519 Merge pull request #144 from KiwiTechLLC/ZBKADM-69
user detail api, changed image upload method
2023-08-03 18:38:53 +05:30
abutalib-kiwi b9e2d9bc8a user detail api, changed image upload method 2023-08-03 18:35:19 +05:30
dilipshrivastwa-kiwi d82c8cd4ae Merge pull request #143 from KiwiTechLLC/ZBKADM-69
user detail api, changed image upload method
2023-08-03 18:24:56 +05:30
abutalib-kiwi dd0f2b027a task list api modified 2023-08-03 18:17:01 +05:30
abutalib-kiwi 3806d1f3a6 user detail api, changed image upload method 2023-08-03 18:14:54 +05:30
abutalib-kiwi f3e2ab9a34 notification list modified and mark as read method changed 2023-08-02 20:45:46 +05:30
abutalib-kiwi 685f627707 change method to get real time 2023-08-02 19:16:29 +05:30
Ankitajain-kiwi b2d172eae5 Merge pull request #141 from KiwiTechLLC/sprint4
migration file
2023-08-02 16:58:24 +05:30
jain 404825dc85 migration file 2023-08-02 16:57:40 +05:30
Ankitajain-kiwi 6e6f0a55d0 Merge pull request #140 from KiwiTechLLC/sprint4
changes in remove guardian code api
2023-08-02 16:26:56 +05:30
jain 75d0b12008 changes in remove guardian code api 2023-08-02 16:26:20 +05:30
Ankitajain-kiwi 2a4011ca5d Merge pull request #139 from KiwiTechLLC/sprint4
sprint4 reassign task after expired
2023-08-02 15:21:38 +05:30
jain ebb468166e sprint4 reassign task after expired 2023-08-02 15:20:43 +05:30
Ankitajain-kiwi 06176912ee Merge pull request #137 from KiwiTechLLC/sprint4
change junior task list api response
2023-08-02 14:24:38 +05:30
jain ed8fc156ac change junior task list api response 2023-08-02 14:23:36 +05:30
dilipshrivastwa-kiwi bff97f59b2 Merge pull request #135 from KiwiTechLLC/ZBKADM-67
changes in article update api and get image url method
2023-08-02 11:19:21 +05:30
abutalib-kiwi af121f5a53 changes in article update api and get image url method 2023-08-02 11:16:12 +05:30
Ankitajain-kiwi 4c3aa03e13 Merge pull request #134 from KiwiTechLLC/sprint4
api response time
2023-08-02 11:00:42 +05:30
jain f3a8b52617 api response time 2023-08-02 11:00:02 +05:30
dilipshrivastwa-kiwi 0c3a77cd11 Merge pull request #133 from KiwiTechLLC/ZBKADM-67
change in update api, added method to upload and  get image url
2023-08-01 20:19:09 +05:30
abutalib-kiwi d3564efbb9 change in update api, added method to upload and get image url 2023-08-01 20:15:01 +05:30
dilipshrivastwa-kiwi 329df77790 Merge pull request #132 from KiwiTechLLC/ZBKADM-67
article list api changes, changed related name for survey_options to …
2023-08-01 13:16:37 +05:30
abutalib-kiwi ee92c98f34 article list api changes, changed related name for survey_options to options 2023-08-01 13:14:22 +05:30
dilipshrivastwa-kiwi fbce04b97e Merge pull request #129 from KiwiTechLLC/ZBKADM-67
fixed add junior api
2023-07-28 19:30:39 +05:30
abutalib-kiwi afaed69eec fixed add junior api 2023-07-28 19:19:59 +05:30
dilipshrivastwa-kiwi 3c0eaf676e Merge pull request #128 from KiwiTechLLC/ZBKADM-67
fixed add junior api
2023-07-28 19:00:41 +05:30
abutalib-kiwi 6ffaa66a4d fixed add junior api 2023-07-28 18:57:11 +05:30
dilipshrivastwa-kiwi 4b8e7189c1 Merge pull request #127 from KiwiTechLLC/ZBKADM-67
changes in approve junior and add juinior api, added search in junior…
2023-07-28 18:11:54 +05:30
abutalib-kiwi d68f24543d changes in approve junior and add juinior api, added search in junior list api 2023-07-28 17:32:02 +05:30
dilipshrivastwa-kiwi 3534561697 Merge pull request #126 from KiwiTechLLC/ZBKADM-67
some changes in artcle api
2023-07-28 16:50:26 +05:30
abutalib-kiwi de774111c0 some changes in artcle api 2023-07-28 16:08:40 +05:30
dilipshrivastwa-kiwi bab50e4492 Merge pull request #123 from KiwiTechLLC/ZBKADM-67
some changes in admin password reset related api
2023-07-27 19:36:01 +05:30
abutalib-kiwi 151a177e76 some changes in admin password reset related api 2023-07-27 19:29:12 +05:30
dilipshrivastwa-kiwi 73a62186a7 Merge pull request #122 from KiwiTechLLC/ZBKADM-67
added request status in expired task cronjob
2023-07-27 15:32:17 +05:30
abutalib-kiwi 83c66ab3b6 added request status in expired task cronjob 2023-07-27 15:31:25 +05:30
dilipshrivastwa-kiwi 94e53e1dcd Merge pull request #121 from KiwiTechLLC/ZBKADM-67
added task to send otp on email
2023-07-27 13:12:36 +05:30
abutalib-kiwi 68b77ef766 added task to send otp on email 2023-07-27 12:59:41 +05:30
dilipshrivastwa-kiwi a2000459d3 Merge pull request #119 from KiwiTechLLC/ZBKADM-67
some changes in forgot password api
2023-07-27 11:06:09 +05:30
abutalib-kiwi c079f3ceca some changes in forgot password api 2023-07-27 10:59:10 +05:30
dilipshrivastwa-kiwi 671cdfc27b Merge pull request #118 from KiwiTechLLC/ZBKADM-67
change password api
2023-07-26 17:51:32 +05:30
abutalib-kiwi a83e27b12a Merge branch 'dev' into ZBKADM-67 2023-07-26 17:45:22 +05:30
abutalib-kiwi 87aa1af02b change password api 2023-07-26 17:16:36 +05:30
dilipshrivastwa-kiwi 5f3026f08f Merge pull request #116 from KiwiTechLLC/sprint3
jira-28 notification
2023-07-26 16:51:46 +05:30
jain a323ad7df7 jira-28 notification 2023-07-26 16:42:26 +05:30
jain 2b6e943d8c jira-28 notification 2023-07-26 16:38:36 +05:30
Ankitajain-kiwi bca0d8d49b Merge pull request #114 from KiwiTechLLC/sprint3
requirement .txt file add
2023-07-26 11:08:02 +05:30
jain b860d937f5 requirement .txt file add 2023-07-26 11:06:58 +05:30
dilipshrivastwa-kiwi 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
abutalib-kiwi d0ef4391cc conflict resolved 2023-07-25 19:51:27 +05:30
dilipshrivastwa-kiwi 5a1b419863 Merge pull request #111 from KiwiTechLLC/sprint3
jira-290 cron job for expired task
2023-07-25 19:49:24 +05:30
abutalib-kiwi 7bf0e62604 added api for forgot password, verify otp and resend otp for admin 2023-07-25 19:43:14 +05:30
jain 80843502a6 jira-290 cron job for expired task 2023-07-25 19:13:25 +05:30
dilipshrivastwa-kiwi 3b31d08996 Merge pull request #109 from KiwiTechLLC/sprint3
Sprint3
2023-07-25 17:34:44 +05:30
jain 3cdd685ea8 sonar issues 2023-07-25 17:21:18 +05:30
jain c4152c0773 Merge branch 'sprint3' of github.com:KiwiTechLLC/ZODBank-Backend into sprint3 2023-07-25 17:15:35 +05:30
jain 8a454c3f97 sonar issues 2023-07-25 17:15:16 +05:30
Ankitajain-kiwi 71405780f8 Merge branch 'dev' into sprint3 2023-07-25 17:07:58 +05:30
jain a8558b5e6e sonar issues 2023-07-25 17:06:26 +05:30
jain 7c4f9b2506 jira-291 start task api 2023-07-25 16:54:48 +05:30
dilipshrivastwa-kiwi cc3490b059 Merge pull request #108 from KiwiTechLLC/ZBKADM-67
article card folder name change
2023-07-25 15:52:22 +05:30
abutalib-kiwi 32476149bc article card folder name change 2023-07-25 15:50:43 +05:30
Ankitajain-kiwi c57224ad0e Merge pull request #107 from KiwiTechLLC/sprint3
jira-28 invite guardian
2023-07-25 15:29:36 +05:30
jain c8a8e1149b jira-28 invite guardian 2023-07-25 15:27:29 +05:30
dilipshrivastwa-kiwi 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
abutalib-kiwi 6dcf045131 Merge branch 'dev' into ZBKADM-67 2023-07-25 14:22:37 +05:30
abutalib-kiwi 6e84814117 modified yml file, default article card image upload api, 2023-07-25 14:09:51 +05:30
dilipshrivastwa-kiwi c587ba322e Merge pull request #105 from KiwiTechLLC/sprint3
start task api
2023-07-25 13:29:32 +05:30
jain 2db0e47e3b resolve conflict 2023-07-25 13:21:20 +05:30
jain 52cdd4b0ba resolve conflict 2023-07-25 13:19:53 +05:30
jain 6be0682913 junior guardian relation table 2023-07-25 13:17:27 +05:30
Ankitajain-kiwi bfeeb5e41f Merge branch 'dev' into sprint3 2023-07-25 12:56:48 +05:30
jain bb56fc7a33 try and except in api 2023-07-25 12:53:56 +05:30
jain e6d45f3bf2 start task api 2023-07-25 12:02:59 +05:30
dilipshrivastwa-kiwi 49b648e86b Merge pull request #103 from KiwiTechLLC/sprint3
Sprint3
2023-07-25 11:33:08 +05:30
jain 79e85fa968 jira-27 invite guardian api 2023-07-25 11:17:57 +05:30
jain 096961976d sonar issues 2023-07-24 15:28:54 +05:30
dilipshrivastwa-kiwi 4d104bab1c Merge pull request #101 from KiwiTechLLC/sprint3
bugs and push notification for create task
2023-07-24 15:19:40 +05:30
jain ed28117a7f bugs and push notification for create task 2023-07-24 15:10:59 +05:30
Ankitajain-kiwi 0a7c081f63 Merge branch 'qa' into dev 2023-07-24 11:03:59 +05:30
jain a2b4f3b758 update position of the junior 2023-07-24 11:00:22 +05:30
jain d86f7d3436 guardian code 2023-07-21 18:38:16 +05:30
jain a5a309cf5b comment top junior call api 2023-07-21 18:33:18 +05:30
jain 0a5eaa233f add qa top -junior url 2023-07-21 17:33:21 +05:30
jain b32d4def2d add qa top -junior url 2023-07-21 16:25:21 +05:30
Ankitajain-kiwi 410282036e Merge pull request #99 from KiwiTechLLC/sprint3
add dev url for top junior api
2023-07-21 16:23:07 +05:30
jain 2a225ea79d add dev url for top junior api 2023-07-21 16:19:30 +05:30
Ankitajain-kiwi 65e012dd60 Merge pull request #98 from KiwiTechLLC/sprint3
add print in junior list
2023-07-21 14:49:52 +05:30
jain 8013767d71 add print in junior list 2023-07-21 14:49:38 +05:30
jain fdd0b88b11 add print in junior list 2023-07-21 14:48:18 +05:30
dilipshrivastwa-kiwi bb1351c309 Merge pull request #96 from KiwiTechLLC/ZBKBCK-001
article api changes
2023-07-21 12:32:32 +05:30
Ankitajain-kiwi c15bd2339a Merge pull request #97 from KiwiTechLLC/dev
Dev
2023-07-21 12:07:26 +05:30
abutalib-kiwi 5ee01af825 article api changes 2023-07-21 11:35:58 +05:30
dilipshrivastwa-kiwi b8382e225c Merge pull request #95 from KiwiTechLLC/ZBKBCK-001
added article point
2023-07-20 18:25:23 +05:30
abutalib-kiwi 532b56c687 added article point 2023-07-20 18:01:53 +05:30
dilipshrivastwa-kiwi 6fe29f0070 Merge pull request #94 from KiwiTechLLC/sprint3
real time changes
2023-07-20 17:13:31 +05:30
jain 96d71d951e real time changes 2023-07-20 17:09:49 +05:30
Ankitajain-kiwi effad5cddc Merge pull request #93 from KiwiTechLLC/dev
Dev
2023-07-20 13:25:53 +05:30
Ankitajain-kiwi 8c0da25814 Merge branch 'qa' into dev 2023-07-20 13:25:42 +05:30
Ankitajain-kiwi b032300ab7 Merge pull request #92 from KiwiTechLLC/sprint3
admin login
2023-07-20 12:45:39 +05:30
jain 4b09ff41cb admin login 2023-07-20 12:44:48 +05:30
dilipshrivastwa-kiwi 8762c00dba Merge pull request #90 from KiwiTechLLC/ZDBBCK-001
sign up issue resolved
2023-07-20 12:31:36 +05:30
abutalib-kiwi 4410712b11 sign up issue resolved 2023-07-20 12:29:41 +05:30
Ankitajain-kiwi e2f737c5b4 Merge pull request #91 from KiwiTechLLC/sprint3
Sprint3
2023-07-20 12:29:02 +05:30
jain 0f5dfc2e9a google login chnages 2023-07-20 12:27:22 +05:30
jain b488b08f3f google login chnages 2023-07-20 12:25:41 +05:30
dilipshrivastwa-kiwi 7102c44055 Merge pull request #89 from KiwiTechLLC/sprint3
Sprint3
2023-07-19 18:05:07 +05:30
dilipshrivastwa-kiwi 8e2d9c44b9 Merge pull request #88 from KiwiTechLLC/ZDBBCK-001
celery settings
2023-07-19 17:59:15 +05:30
Ankitajain-kiwi fa96364901 Merge branch 'dev' into sprint3 2023-07-19 17:51:58 +05:30
jain 49abf12ce8 jira-34 referral code validation API 2023-07-19 17:50:10 +05:30
abutalib-kiwi 4002d1e561 celery settings 2023-07-19 17:48:27 +05:30
jain 9f9926da14 jira-34 referral code validation API 2023-07-19 17:45:20 +05:30
dilipshrivastwa-kiwi 555d8231e7 Merge pull request #87 from KiwiTechLLC/ZDBBCK-001
notification set up and celery configuration
2023-07-19 17:04:06 +05:30
abutalib-kiwi 3223a2b050 notification set up and celery configuration 2023-07-19 17:00:58 +05:30
dilipshrivastwa-kiwi 2e7d36485c Merge pull request #86 from KiwiTechLLC/sprint3
jira-34 allocated points
2023-07-19 15:04:41 +05:30
jain 2e3870dc53 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint3 2023-07-19 15:04:04 +05:30
jain 86e2c75afc jira-34 allocated points 2023-07-19 15:01:18 +05:30
dilipshrivastwa-kiwi e238c85674 Merge pull request #85 from KiwiTechLLC/ZDBBCK-001
cors error settings
2023-07-19 15:00:12 +05:30
abutalib-kiwi 5429140a5c cors error settings 2023-07-19 14:58:14 +05:30
dilipshrivastwa-kiwi 6e6ba881f6 Merge pull request #84 from KiwiTechLLC/ZDBBCK-001
checking email settings
2023-07-19 13:15:40 +05:30
abutalib-kiwi 74fc656bd1 checking email settings 2023-07-19 13:11:36 +05:30
dilipshrivastwa-kiwi e9b0e480e6 Merge pull request #83 from KiwiTechLLC/ZDBBCK-001
checking email settings
2023-07-18 19:39:27 +05:30
abutalib-kiwi 49fc8d9dca checking email settings 2023-07-18 19:37:30 +05:30
dilipshrivastwa-kiwi 39271282e2 Merge pull request #82 from KiwiTechLLC/sprint3
jira-34 points earned
2023-07-18 19:27:13 +05:30
jain 9b84d176af jira-34 points earned 2023-07-18 19:18:38 +05:30
dilipshrivastwa-kiwi 49336e2e18 Merge pull request #81 from KiwiTechLLC/ZDBBCK-001
checking email settings
2023-07-18 18:30:13 +05:30
abutalib-kiwi 0df748ccbf checking email settings 2023-07-18 18:27:27 +05:30
dilipshrivastwa-kiwi 9d600a1ea8 Merge pull request #80 from KiwiTechLLC/sprint3
email setting changes
2023-07-18 17:34:31 +05:30
jain 06d841f444 email setting changes 2023-07-18 17:32:34 +05:30
dilipshrivastwa-kiwi e3a5d52a1d Merge pull request #79 from KiwiTechLLC/sprint3
sonar fixes
2023-07-18 16:58:20 +05:30
jain 130dcd83e7 sonar fixes 2023-07-18 16:56:24 +05:30
dilipshrivastwa-kiwi 19a2a3d1db Merge pull request #78 from KiwiTechLLC/ZDBBCK-001
remove fcm token
2023-07-18 16:32:51 +05:30
dilipshrivastwa-kiwi 02f01b8a16 Merge pull request #77 from KiwiTechLLC/sprint3
Sprint3
2023-07-18 16:31:20 +05:30
abutalib-kiwi 3d84c03163 remove fcm token 2023-07-18 16:29:11 +05:30
jain 5cc43aa2b8 jira-33 mark as complete the task 2023-07-18 15:59:26 +05:30
jain 4c0cac7cb0 jira-32 list of all task of junior API, changes in Search task API 2023-07-18 15:19:50 +05:30
dilipshrivastwa-kiwi 6aa198d1ad Merge pull request #76 from KiwiTechLLC/ZDBBCK-001
[ZDBBCK-001]:- notification app, api for device registration and fcm token
2023-07-18 15:03:18 +05:30
abutalib-kiwi ba4d5933de notification app, api for device registration and fcm token 2023-07-18 14:13:30 +05:30
jain 4d04f16cee Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint3 2023-07-18 11:02:03 +05:30
jain 032186bf43 task list 2023-07-18 11:01:16 +05:30
dilipshrivastwa-kiwi 04342133ef Merge pull request #75 from KiwiTechLLC/ZDBBCK-001
[ZDBBCK-001]:- web admin article api
2023-07-17 19:18:11 +05:30
abutalib-kiwi 751af642b8 added admin permission 2023-07-17 18:58:09 +05:30
abutalib-kiwi e90326fcb6 Merge branch 'dev' into ZDBBCK-001 2023-07-17 18:50:43 +05:30
Ankitajain-kiwi 0cec3a5cf1 Merge pull request #74 from KiwiTechLLC/sprint3
Sprint3
2023-07-17 18:28:06 +05:30
jain ae7250b34a changes in env 2023-07-17 18:17:00 +05:30
jain 8f1f49de45 jira-21 approve task API 2023-07-17 18:00:19 +05:30
abutalib-kiwi de5d3b19d0 conflict resolved 2023-07-17 17:53:11 +05:30
dilipshrivastwa-kiwi 2b0fc56190 Merge pull request #73 from KiwiTechLLC/sprint3
sonar issues
2023-07-17 17:13:42 +05:30
jain 4779749b0c success msg 2023-07-17 17:05:45 +05:30
jain 6d6d21137f sonar issues 2023-07-17 17:00:33 +05:30
dilipshrivastwa-kiwi cd944b10f5 Merge pull request #72 from KiwiTechLLC/sprint3
sonar issues
2023-07-17 16:34:19 +05:30
jain 1399b585e8 sonar issues 2023-07-17 16:07:06 +05:30
dilipshrivastwa-kiwi 2d3100286d Merge pull request #71 from KiwiTechLLC/sprint2
sonar issues fixed
2023-07-17 12:21:13 +05:30
jain cfc36735d6 sonar issues fixed 2023-07-17 11:58:04 +05:30
Ankitajain-kiwi 761e723cd3 Merge pull request #70 from KiwiTechLLC/sprint2
sonar issue
2023-07-15 23:08:38 +05:30
jain 32e52e17f4 sonar issue 2023-07-15 23:07:12 +05:30
dilipshrivastwa-kiwi 71e7601d6b Merge pull request #69 from KiwiTechLLC/sprint2
Sprint2
2023-07-14 21:31:27 +05:30
jain 5bd865a685 login in only single device at a time 2023-07-14 19:38:58 +05:30
abutalib-kiwi 45410fa0ca list, view, edit api for article 2023-07-14 18:47:07 +05:30
jain 485dc83911 authenticate the api 2023-07-14 16:04:43 +05:30
dilipshrivastwa-kiwi ac00543312 Merge pull request #68 from KiwiTechLLC/sprint2
Sprint2
2023-07-14 15:22:40 +05:30
jain a0e1dc11fa migrations files 2023-07-14 15:07:54 +05:30
jain c798357952 migrations files 2023-07-14 15:05:18 +05:30
jain d163f43c85 jira-25 access token API 2023-07-14 15:02:53 +05:30
jain b3f35d271b change access token duration 2023-07-14 12:45:08 +05:30
jain 7b9b5a2c6f sonar issues 2023-07-14 12:05:28 +05:30
abutalib-kiwi a4d9997580 web_admin module added, api for article created 2023-07-13 19:51:17 +05:30
dilipshrivastwa-kiwi c43db90219 Merge pull request #65 from KiwiTechLLC/sprint2
jira-18 approval API
2023-07-13 19:30:32 +05:30
jain 5d528b885a jira-18 approval API 2023-07-13 19:24:33 +05:30
Ankitajain-kiwi d3b7b6cd07 Merge pull request #64 from KiwiTechLLC/dev
Dev
2023-07-13 17:33:54 +05:30
Ankitajain-kiwi 1ea4720ab4 Merge pull request #63 from KiwiTechLLC/sprint2
referral code
2023-07-13 17:33:12 +05:30
jain 52a0f4b590 referral code 2023-07-13 17:31:05 +05:30
Ankitajain-kiwi f7cfd53594 Merge pull request #62 from KiwiTechLLC/dev
Dev
2023-07-13 16:42:28 +05:30
dilipshrivastwa-kiwi dae6ffbed1 Merge pull request #61 from KiwiTechLLC/sprint2
changes in image size
2023-07-13 16:41:33 +05:30
jain a16fecb1c9 changes in image size 2023-07-13 16:37:42 +05:30
Ankitajain-kiwi 75bc387bec Merge pull request #60 from KiwiTechLLC/dev
Dev
2023-07-13 15:43:21 +05:30
dilipshrivastwa-kiwi aadced6a13 Merge pull request #59 from KiwiTechLLC/sprint2
check size of the image
2023-07-13 13:56:30 +05:30
jain 1ac6c7c01d check size of the image 2023-07-13 13:50:40 +05:30
dilipshrivastwa-kiwi c3f7750349 Merge pull request #58 from KiwiTechLLC/sprint2
Sprint2
2023-07-13 13:06:56 +05:30
jain ab06962b79 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into sprint2 2023-07-13 12:29:33 +05:30
jain 324a023d83 changes in complete profile field 2023-07-13 12:28:35 +05:30
Ankitajain-kiwi 0c760a58f3 Merge pull request #57 from KiwiTechLLC/dev
Dev
2023-07-13 11:29:55 +05:30
dilipshrivastwa-kiwi d0430d7aad Merge pull request #56 from KiwiTechLLC/sprint2
Sprint2
2023-07-12 19:42:50 +05:30
Ankitajain-kiwi 701961234d Merge branch 'dev' into sprint2 2023-07-12 19:36:07 +05:30
jain 1fc02d17e6 jira-16 filter, invite and remove API 2023-07-12 19:32:10 +05:30
dilipshrivastwa-kiwi b156e62dc8 Merge pull request #55 from KiwiTechLLC/dev
Dev
2023-07-12 16:53:30 +05:30
jain 81e314878e passcode add 2023-07-12 16:43:16 +05:30
jain b11137bfad changes in files 2023-07-12 16:27:52 +05:30
jain a041028840 jira-17 add junior and send invite mail 2023-07-12 16:24:30 +05:30
dilipshrivastwa-kiwi 8720de4834 Merge pull request #54 from KiwiTechLLC/sprint2
logout api and sonar
2023-07-11 22:57:00 +05:30
jain 33101811ff logout api and sonar 2023-07-11 22:35:59 +05:30
jain 18cb975d94 logout api and sonar 2023-07-11 22:34:59 +05:30
dilipshrivastwa-kiwi 2e7ef5e69f Merge pull request #53 from KiwiTechLLC/sprint2
jira-25 setting API
2023-07-11 18:30:58 +05:30
jain 7e7367e3a4 jira-25 setting API 2023-07-11 18:23:40 +05:30
dilipshrivastwa-kiwi 809f89bd42 Merge pull request #52 from KiwiTechLLC/sprint2
Support email
2023-07-11 16:43:31 +05:30
jain 68ce67d387 Support email 2023-07-11 16:34:33 +05:30
jain efa121640f Support email 2023-07-11 16:33:03 +05:30
Ankitajain-kiwi 23fb2a0695 Merge pull request #51 from KiwiTechLLC/dev
Dev
2023-07-11 13:15:46 +05:30
Ankitajain-kiwi 7d6dfb15a2 Merge pull request #50 from KiwiTechLLC/sprint2
jira-26 changes in notification API
2023-07-11 13:15:15 +05:30
jain 8b4db3e2e9 jira-26 changes in notification API 2023-07-11 13:14:20 +05:30
Ankitajain-kiwi fe8ddd02a5 Merge pull request #49 from KiwiTechLLC/dev
Dev
2023-07-11 12:06:29 +05:30
dilipshrivastwa-kiwi c26e6140a7 Merge pull request #48 from KiwiTechLLC/sprint2
sonar issue
2023-07-11 12:01:20 +05:30
jain 41761f22e8 sonar issue 2023-07-11 11:52:07 +05:30
dilipshrivastwa-kiwi cd0f1c72a1 Merge pull request #47 from KiwiTechLLC/sprint2
swagger points
2023-07-10 21:27:11 +05:30
jain c0f20f732d swagger points 2023-07-10 21:18:22 +05:30
dilipshrivastwa-kiwi 628dbbf926 Merge pull request #46 from KiwiTechLLC/sprint2
Sprint2
2023-07-10 19:24:26 +05:30
jain 1ae81d4134 jira-25 setting API 2023-07-10 19:16:45 +05:30
jain 03fb774a7b jira-25 setting API 2023-07-10 18:01:14 +05:30
jain 4fb7a2fc6c jira-14 dashboard API 2023-07-10 17:51:30 +05:30
jain 0d4c9aae78 delete user account API 2023-07-10 16:50:50 +05:30
jain c8925aad8e jira-15 leader board API and profile API 2023-07-10 14:53:43 +05:30
dilipshrivastwa-kiwi 51153a09cc Merge pull request #45 from KiwiTechLLC/sprint2
sonar fixes
2023-07-10 12:20:55 +05:30
jain 026bfb6da7 sonar fixes 2023-07-10 12:10:15 +05:30
Ankitajain-kiwi 3dc16ce79c Merge pull request #44 from KiwiTechLLC/dev
Dev
2023-07-09 19:27:19 +05:30
Ankitajain-kiwi 65a00ef090 Merge pull request #43 from KiwiTechLLC/sprint2
jira-15 top leadership API
2023-07-09 19:26:43 +05:30
jain 3098de36b8 jira-15 top leadership API 2023-07-09 19:25:59 +05:30
Ankitajain-kiwi 15cc3eca51 Merge pull request #42 from KiwiTechLLC/sprint2
Sprint2
2023-07-09 19:16:13 +05:30
jain 1b60d4344e jira-15 top leadership API 2023-07-09 19:13:05 +05:30
jain 3723e46be1 jira-15 changes in profile API 2023-07-09 17:56:47 +05:30
Ankitajain-kiwi b75756c43a Merge pull request #41 from KiwiTechLLC/dev
Dev
2023-07-07 18:06:40 +05:30
dilipshrivastwa-kiwi 8bf4ad8352 Merge pull request #40 from KiwiTechLLC/sprint2
jira-15 country_name
2023-07-07 18:03:37 +05:30
jain 413434496c jira-15 country_name 2023-07-07 17:57:29 +05:30
dilipshrivastwa-kiwi c7415c7760 Merge pull request #39 from KiwiTechLLC/sprint2
swagger
2023-07-07 17:04:59 +05:30
jain 9e8cde3516 swagger 2023-07-07 17:02:48 +05:30
dilipshrivastwa-kiwi af5f0cf200 Merge pull request #38 from KiwiTechLLC/sprint2
jira-15 dashboard api profile, default image, top leaderboard API
2023-07-07 16:39:08 +05:30
jain ab435ef384 jira-15 dashboard api profile, default image, top leaderboard API 2023-07-07 16:33:51 +05:30
Ankitajain-kiwi c01c2b4e23 Merge pull request #37 from KiwiTechLLC/dev
Dev
2023-07-06 20:18:01 +05:30
dilipshrivastwa-kiwi 3f0e73771c Merge pull request #36 from KiwiTechLLC/sprint2
Sprint2
2023-07-06 20:02:24 +05:30
jain c6ffb1f039 update profile 2023-07-06 19:59:03 +05:30
jain 2b783512a5 update profile 2023-07-06 19:53:20 +05:30
dilipshrivastwa-kiwi bea4cbe376 Merge pull request #35 from KiwiTechLLC/sprint2
Sprint2
2023-07-06 16:23:00 +05:30
jain 34034dcdb8 jira-15 remove commented code 2023-07-06 16:20:07 +05:30
jain c7a5bf68a0 jira-15 remove commented code 2023-07-06 16:17:56 +05:30
jain 3b642fcc5f changes in requirement file 2023-07-06 16:08:43 +05:30
jain 2edd8c4e00 jira-15 and email otp validation 2023-07-06 15:52:30 +05:30
jain cf966a5250 jira-15 dashboard API 2023-07-06 12:21:54 +05:30
jain a80f603614 jira-15 top junior API and default images field 2023-07-06 07:57:53 +05:30
jain 0d143a39ac jira-20 create task 2023-07-04 19:25:51 +05:30
jain 5e5161a77e list of the task and task table 2023-07-04 18:26:00 +05:30
Ankitajain-kiwi 156dc496bd Merge pull request #34 from KiwiTechLLC/sprint2
changes in setting
2023-07-03 18:12:12 +05:30
jain 376bb89f20 changes in setting 2023-07-03 18:10:53 +05:30
jain 1c7d6bac85 changes in settings and env file 2023-07-03 17:17:58 +05:30
Ankitajain-kiwi abbe09731c Merge pull request #33 from KiwiTechLLC/login_code
requirement.txt file
2023-07-03 16:00:51 +05:30
jain 22f1d01b94 requirement.txt file 2023-07-03 15:59:37 +05:30
dilipshrivastwa-kiwi 69c06691c7 Merge pull request #32 from KiwiTechLLC/login_code
Login code
2023-07-03 15:59:33 +05:30
jain a27c486c43 remove unused code 2023-07-03 15:53:25 +05:30
jain 28cbdf9cfa jira-5 social login 2023-07-03 14:57:07 +05:30
jain ddfc4e946c Merge branch 'qa' of github.com:KiwiTechLLC/ZODBank-Backend into qa 2023-07-03 12:58:35 +05:30
jain 014b5fe183 jira-5 apple login with first and last name 2023-07-03 12:58:06 +05:30
jain d2498f82ad jira-5 google login and apple login 2023-06-30 21:25:43 +05:30
Ankitajain-kiwi f2533d9a08 Merge pull request #30 from KiwiTechLLC/dev
textual changes
2023-06-30 17:22:16 +05:30
jain 36cedf8c69 textual changes 2023-06-30 17:20:47 +05:30
Ankitajain-kiwi 4e22dd9871 Merge pull request #28 from KiwiTechLLC/dev
jira-6 change country name length
2023-06-30 16:25:39 +05:30
jain 66bbe06686 jira-6 change country name length 2023-06-30 16:24:45 +05:30
jain 79ac140ddd jira-3 google login 2023-06-30 16:22:41 +05:30
jain 4a34dab570 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into login_code 2023-06-30 16:10:09 +05:30
Ankitajain-kiwi 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
jain 245162b913 jira-274 user can use same number multiple time 2023-06-30 15:51:12 +05:30
jain 6cbe824d9e jira-13 alibaba bucket name change 2023-06-30 15:24:49 +05:30
jain 7376fc555b jira-5 google login 2023-06-30 15:21:00 +05:30
Ankitajain-kiwi df438241ce Merge pull request #24 from KiwiTechLLC/dev
Dev
2023-06-30 11:18:19 +05:30
Ankitajain-kiwi ed2d7895ab Merge pull request #23 from KiwiTechLLC/login_code
reset password msg
2023-06-30 11:17:54 +05:30
jain c57968b7c0 reset password msg 2023-06-30 11:17:18 +05:30
Ankitajain-kiwi bb06238623 Merge pull request #21 from KiwiTechLLC/dev
Dev
2023-06-29 21:48:53 +05:30
dilipshrivastwa-kiwi e42d8af65b Merge pull request #20 from KiwiTechLLC/login_code
email changes
2023-06-29 21:44:33 +05:30
jain d254c5e5fa email changes 2023-06-29 21:39:36 +05:30
Ankitajain-kiwi ada987413b Merge pull request #18 from KiwiTechLLC/dev
Dev
2023-06-29 21:25:18 +05:30
dilipshrivastwa-kiwi 118d73776b Merge pull request #17 from KiwiTechLLC/login_code
jira-7 email
2023-06-29 20:29:51 +05:30
jain c2713b8214 jira-7 email 2023-06-29 20:26:35 +05:30
dilipshrivastwa-kiwi 4dd95e74fb Merge pull request #16 from KiwiTechLLC/login_code
Login code
2023-06-29 18:22:14 +05:30
jain 70c136dde4 jira-9 changes in setting 2023-06-29 18:21:15 +05:30
jain 88a9e925ed jira-9 changes in setting 2023-06-29 18:20:07 +05:30
jain 3d3ccfb146 jira-9 changes in setting 2023-06-29 18:16:11 +05:30
jain be6725c66d jira-9 changes in setting 2023-06-29 18:14:49 +05:30
dilipshrivastwa-kiwi f3c41043e0 Merge pull request #15 from KiwiTechLLC/login_code
jira-4 login after email verified
2023-06-29 18:09:05 +05:30
jain ea2bd635ca jira-4 login after email verified 2023-06-29 18:03:27 +05:30
Ankitajain-kiwi eb4cde397f Merge pull request #14 from KiwiTechLLC/GoogleLogin
add requirement file
2023-06-29 13:05:17 +05:30
jain 4864d3b499 add requirement file 2023-06-29 13:03:40 +05:30
dilipshrivastwa-kiwi ef1f7ebeac Merge pull request #13 from KiwiTechLLC/GoogleLogin
Google login
2023-06-29 12:19:05 +05:30
jain c1ae88c32f jira-13 image upload changes 2023-06-29 11:49:40 +05:30
jain 9d7f265f40 jira-9 and jira-12 sendgrid and upload images in alibaba bucket 2023-06-29 11:40:04 +05:30
dilipshrivastwa-kiwi 323168b7d0 Merge pull request #11 from KiwiTechLLC/dev
Dev
2023-06-28 11:56:12 +05:30
jain f0a2a4bd4b jira-13 update profile 2023-06-27 21:34:40 +05:30
jain 4a982f1baf changes in setting.py file 2023-06-27 20:44:42 +05:30
jain d849153bec changes in setting.py file 2023-06-27 20:43:22 +05:30
Ankitajain-kiwi 947119d4c2 Merge pull request #10 from KiwiTechLLC/26june_sprint1
changes in setting.py file
2023-06-27 20:18:12 +05:30
Ankitajain-kiwi a41e3a6f14 Merge branch 'dev' into 26june_sprint1 2023-06-27 20:18:04 +05:30
jain 7b02b7dfbc changes in setting.py file 2023-06-27 20:16:35 +05:30
Ankitajain-kiwi 3852e9bf03 Merge pull request #9 from KiwiTechLLC/26june_sprint1
26june sprint1
2023-06-27 19:34:27 +05:30
jain d208e5252c jira-13 update country name 2023-06-27 19:32:57 +05:30
jain 7b6d34f334 setting changes 2023-06-27 17:37:12 +05:30
jain 0006208cf8 Merge branch 'dev' of github.com:KiwiTechLLC/ZODBank-Backend into 26june_sprint1 2023-06-27 17:31:00 +05:30
Ankitajain-kiwi 352de6d630 Merge pull request #8 from KiwiTechLLC/server_demo
changes
2023-06-27 17:28:51 +05:30
jain ad582d77d7 changes 2023-06-27 17:27:19 +05:30
jain 019d028cb6 jira-5 google login 2023-06-27 17:22:59 +05:30
Ankitajain-kiwi e1e5b1838a Merge pull request #7 from KiwiTechLLC/server_demo
changes docker file position
2023-06-27 16:54:39 +05:30
jain 8ff142ce2f changes docker file position 2023-06-27 16:53:38 +05:30
jain 4537f1f4fa changes 2023-06-27 16:45:42 +05:30
Ankitajain-kiwi 2086dcb472 Merge pull request #6 from KiwiTechLLC/26june_sprint1
auth token changes
2023-06-27 15:18:43 +05:30
jain d4eaaada74 auth token changes 2023-06-27 15:17:46 +05:30
dilipshrivastwa-kiwi 396b4a32c3 Merge pull request #5 from KiwiTechLLC/26june_sprint1
26june sprint1
2023-06-27 15:07:19 +05:30
jain 232f082c77 jira-7 changes in requirment.txt file 2023-06-27 14:59:00 +05:30
jain aaa1730636 Jira-13 migrate file 2023-06-27 11:46:20 +05:30
jain 44b25dde3e Jira-13 sonar fixes 2023-06-27 11:44:00 +05:30
dilipshrivastwa-kiwi b52819c033 Merge pull request #4 from KiwiTechLLC/26june_sprint1
jira-9 changes in phone verification
2023-06-26 16:51:15 +05:30
jain 9bd31f9e86 jira-9 changes in phone verification 2023-06-26 16:44:32 +05:30
Ankitajain-kiwi 6374ea0a90 Merge pull request #3 from KiwiTechLLC/demo-dev1
Demo dev1
2023-06-26 12:44:05 +05:30
jain 9db620d818 forgot, reset and change password 2023-06-26 10:38:49 +05:30
jain 89982f851f forgot password and email verification 2023-06-24 18:17:32 +05:30
dilipshrivastwa-kiwi c115873972 Merge pull request #2 from KiwiTechLLC/demo-dev1
First Commit
2023-06-24 14:30:17 +05:30
jain 8bc3a307c0 Initial Commit 2023-06-23 19:13:49 +05:30
dilipshrivastwa-kiwi 501deaac0b Merge pull request #1 from KiwiTechLLC/dks_sprint1
add git ignore file
2023-06-15 15:12:38 +05:30
dilipshrivastwa-kiwi 5d386322e4 add git ignore file 2023-06-15 15:11:18 +05:30
dilipshrivastwa-kiwi ecb058209d First Commit 2023-06-15 15:01:10 +05:30
566 changed files with 9142 additions and 36681 deletions
-40
View File
@@ -1,40 +0,0 @@
NODE_ENV=dev
PORT=5000
DB_HOST=
DB_PASS=
DB_USER=postgres
DB_PORT=5432
DB_NAME=
MIGRATIONS_RUN=true
SWAGGER_API_DOCS_PATH="/api-docs"
JWT_ACCESS_TOKEN_SECRET=your_access_token_secret
JWT_ACCESS_TOKEN_EXPIRY=1d
JWT_REFRESH_TOKEN_SECRET=your_refresh_token_secret
JWT_REFRESH_TOKEN_EXPIRY=1d
USE_MOCK=true
OCI_TENANCY_ID=
OCI_USER_ID=
OCI_FINGERPRINT=
OCI_PRIVATE_KEY=
OCI_PASSPHRASE=""
OCI_NAMESPACE=
OCI_REGION=
MAIL_HOST=smtp.gmail.com
MAIL_USER=[email protected]
MAIL_PASSWORD=
MAIL_PORT=587
MAIL_FROM=UBA
BRANCH_IO_URL=https://api2.branch.io/v1/url
BRANCH_IO_KEY=
ZOD_BASE_URL=http://localhost:5001
ANDROID_PACKAGE_NAME=com.zod
IOS_PACKAGE_NAME=com.zod
ANDRIOD_JUNIOR_DEEPLINK_PATH=zodbank://juniors/qr-code/validate
IOS_JUNIOR_DEEPLINK_PATH=zodbank://juniors/qr-code/validate
-47
View File
@@ -1,47 +0,0 @@
/** @type {import('eslint').Linter.Config} */
module.exports = {
parser: '@typescript-eslint/parser',
parserOptions: {
project: 'tsconfig.json',
tsconfigRootDir: __dirname,
sourceType: 'module',
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: ['plugin:@typescript-eslint/recommended', 'plugin:prettier/recommended', 'plugin:security/recommended'],
root: true,
env: {
node: true,
jest: true,
},
ignorePatterns: ['.eslintrc.js', 'node_modules', 'dist', 'coverage', 'jest*\\.ts', 'typeorm*\\.ts'],
rules: {
'@typescript-eslint/interface-name-prefix': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{ argsIgnorePattern: '^_', varsIgnorePattern: '^_', caughtErrorsIgnorePattern: '^_' },
],
'@typescript-eslint/no-inferrable-types': 'off',
'func-names': ['error', 'as-needed'],
'no-underscore-dangle': ['error'],
'require-await': ['error'],
'no-console': ['error'],
'no-multi-assign': ['error'],
'no-magic-numbers': ['error', { ignoreArrayIndexes: true}],
'no-multiple-empty-lines': ['error', { max: 1, maxEOF: 1, maxBOF: 0 }],
'max-len': [
'error',
{ code: 120, tabWidth: 2, ignoreUrls: true, ignoreStrings: true, ignoreTemplateLiterals: true },
],
},
overrides: [
{
files: ['*spec.ts'],
rules: {
'no-magic-numbers': ['off'],
},
},
],
};
-85
View File
@@ -1,85 +0,0 @@
name: CI Zod
on:
pull_request:
branches:
- '*'
push:
branches:
- dev
- main
env:
NODE_ENV: development
TAG: dev
jobs:
install_dependencies:
name: Install Dependencies
runs-on: ubuntu-latest
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 22.4.1
- name: Install Dependencies
run: npm install
- name: Cache Node Modules
uses: actions/cache@v3
with:
path: node_modules
key: ${{ runner.os }}-node_modules-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-node_modules-
run_tests:
name: Run Tests
runs-on: ubuntu-latest
needs: install_dependencies
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 22.4.1
- name: Install Dependencies
run: npm install
- name: Run Lint
run: npm run lint
- name: Run Tests with Coverage
run: npm run test:cov
build_app:
name: Build Application
runs-on: ubuntu-latest
needs: run_tests
steps:
- name: Checkout Code
uses: actions/checkout@v3
- name: Set up Node.js
uses: actions/setup-node@v3
with:
node-version: 22.4.1
- name: Install Dependencies
run: npm install
- name: Build Application
run: npm run build
- name: Upload Build Artifacts
uses: actions/upload-artifact@v3
with:
name: dist
path: dist/
+22 -55
View File
@@ -1,57 +1,24 @@
# compiled output
/dist
/node_modules
/build
# Logs
logs
/static
/media
.idea/
*.pyc
media/
*.name
*.iml
*.log
npm-debug.log*
pnpm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# OS
*.xml
*.pyo
.DS_Store
# Tests
/coverage
/.nyc_output
# IDEs and editors
/.idea
.project
.classpath
.c9/
*.launch
.settings/
*.sublime-workspace
# IDE - VSCode
.vscode/*
!.vscode/settings.json
# dotenv environment variable files
.env
.env.development.local
.env.test.local
.env.production.local
.env.local
# temp directory
.temp
.tmp
# Runtime data
pids
*.pid
*.seed
*.pid.lock
# Diagnostic reports (https://nodejs.org/api/report.html)
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
zod-certs
.idea
venv/*
static/*
*.pem
*.sqlite3
/migrations/__pycache__/
/__pycache__/
/*.pyc
*/__pycache__/*.pyc
__pycache__/
*.env
ve/*
celerybeat-schedule
-6
View File
@@ -1,6 +0,0 @@
{
"singleQuote": true,
"trailingComma": "all",
"tabWidth": 2,
"printWidth": 120
}
-6
View File
@@ -1,6 +0,0 @@
{
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.organizeImports": "explicit"
}
}
+11
View File
@@ -0,0 +1,11 @@
FROM python:3.9
ENV PYTHONUNBUFFERED 1
RUN mkdir /usr/src/app
WORKDIR /usr/src/app
COPY . .
RUN apt-get update
RUN apt-get install wkhtmltopdf -y
RUN apt install -y gdal-bin python3-gdal
RUN pip install -r requirements.txt
WORKDIR /usr/src/app
-33
View File
@@ -1,33 +0,0 @@
# Base image
FROM node:22.4.1-alpine
RUN apk update && \
apk upgrade && \
apk add ca-certificates curl && \
update-ca-certificates
#saudi timezone
ENV TZ=Asia/Riyadh
# Create app directory
RUN mkdir /app && chown node:node /app
WORKDIR /app
# finally switch user to node
USER node
# A wildcard is used to ensure both package.json, package-lock.json and package-lock.json are copied
COPY --chown=node:node package*.json ./
# Install app dependencies
RUN npm install --frozen-lockfile
# Bundle app source
COPY --chown=node:node . .
# Creates a "dist" folder with the production build
RUN npm run build
# Start the server using the production build
CMD [ "node", "dist/main" ]
+1 -99
View File
@@ -1,99 +1 @@
<p align="center">
<a href="http://nestjs.com/" target="blank"><img src="https://nestjs.com/img/logo-small.svg" width="120" alt="Nest Logo" /></a>
</p>
[circleci-image]: https://img.shields.io/circleci/build/github/nestjs/nest/master?token=abc123def456
[circleci-url]: https://circleci.com/gh/nestjs/nest
<p align="center">A progressive <a href="http://nodejs.org" target="_blank">Node.js</a> framework for building efficient and scalable server-side applications.</p>
<p align="center">
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/v/@nestjs/core.svg" alt="NPM Version" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/l/@nestjs/core.svg" alt="Package License" /></a>
<a href="https://www.npmjs.com/~nestjscore" target="_blank"><img src="https://img.shields.io/npm/dm/@nestjs/common.svg" alt="NPM Downloads" /></a>
<a href="https://circleci.com/gh/nestjs/nest" target="_blank"><img src="https://img.shields.io/circleci/build/github/nestjs/nest/master" alt="CircleCI" /></a>
<a href="https://coveralls.io/github/nestjs/nest?branch=master" target="_blank"><img src="https://coveralls.io/repos/github/nestjs/nest/badge.svg?branch=master#9" alt="Coverage" /></a>
<a href="https://discord.gg/G7Qnnhy" target="_blank"><img src="https://img.shields.io/badge/discord-online-brightgreen.svg" alt="Discord"/></a>
<a href="https://opencollective.com/nest#backer" target="_blank"><img src="https://opencollective.com/nest/backers/badge.svg" alt="Backers on Open Collective" /></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://opencollective.com/nest/sponsors/badge.svg" alt="Sponsors on Open Collective" /></a>
<a href="https://paypal.me/kamilmysliwiec" target="_blank"><img src="https://img.shields.io/badge/Donate-PayPal-ff3f59.svg" alt="Donate us"/></a>
<a href="https://opencollective.com/nest#sponsor" target="_blank"><img src="https://img.shields.io/badge/Support%20us-Open%20Collective-41B883.svg" alt="Support us"></a>
<a href="https://twitter.com/nestframework" target="_blank"><img src="https://img.shields.io/twitter/follow/nestframework.svg?style=social&label=Follow" alt="Follow us on Twitter"></a>
</p>
<!--[![Backers on Open Collective](https://opencollective.com/nest/backers/badge.svg)](https://opencollective.com/nest#backer)
[![Sponsors on Open Collective](https://opencollective.com/nest/sponsors/badge.svg)](https://opencollective.com/nest#sponsor)-->
## Description
[Nest](https://github.com/nestjs/nest) framework TypeScript starter repository.
## Project setup
```bash
$ npm install
```
## Compile and run the project
```bash
# development
$ npm run start
# watch mode
$ npm run start:dev
# production mode
$ npm run start:prod
```
## Run tests
```bash
# unit tests
$ npm run test
# e2e tests
$ npm run test:e2e
# test coverage
$ npm run test:cov
```
## Deployment
When you're ready to deploy your NestJS application to production, there are some key steps you can take to ensure it runs as efficiently as possible. Check out the [deployment documentation](https://docs.nestjs.com/deployment) for more information.
If you are looking for a cloud-based platform to deploy your NestJS application, check out [Mau](https://mau.nestjs.com), our official platform for deploying NestJS applications on AWS. Mau makes deployment straightforward and fast, requiring just a few simple steps:
```bash
$ npm install -g mau
$ mau deploy
```
With Mau, you can deploy your application in just a few clicks, allowing you to focus on building features rather than managing infrastructure.
## Resources
Check out a few resources that may come in handy when working with NestJS:
- Visit the [NestJS Documentation](https://docs.nestjs.com) to learn more about the framework.
- For questions and support, please visit our [Discord channel](https://discord.gg/G7Qnnhy).
- To dive deeper and get more hands-on experience, check out our official video [courses](https://courses.nestjs.com/).
- Deploy your application to AWS with the help of [NestJS Mau](https://mau.nestjs.com) in just a few clicks.
- Visualize your application graph and interact with the NestJS application in real-time using [NestJS Devtools](https://devtools.nestjs.com).
- Need help with your project (part-time to full-time)? Check out our official [enterprise support](https://enterprise.nestjs.com).
- To stay in the loop and get updates, follow us on [X](https://x.com/nestframework) and [LinkedIn](https://linkedin.com/company/nestjs).
- Looking for a job, or have a job to offer? Check out our official [Jobs board](https://jobs.nestjs.com).
## Support
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
## Stay in touch
- Author - [Kamil Myśliwiec](https://twitter.com/kammysliwiec)
- Website - [https://nestjs.com](https://nestjs.com/)
- Twitter - [@nestframework](https://twitter.com/nestframework)
## License
Nest is [MIT licensed](https://github.com/nestjs/nest/blob/master/LICENSE).
# ZODBank-Backend
+49
View File
@@ -0,0 +1,49 @@
"""Account admin"""
from django.contrib import admin
"""Import django app"""
from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete, UserDeviceDetails
# Register your models here.
@admin.register(UserDelete)
class UserDeleteAdmin(admin.ModelAdmin):
"""User profile admin"""
list_display = ['user', 'old_email', 'd_email']
def __str__(self):
"""Return delete user"""
return self.user
@admin.register(UserNotification)
class UserNotificationAdmin(admin.ModelAdmin):
"""User profile admin"""
list_display = ['user', 'push_notification', 'email_notification', 'sms_notification']
def __str__(self):
"""Return image url"""
return self.image_url
@admin.register(DefaultTaskImages)
class DefaultTaskImagesAdmin(admin.ModelAdmin):
"""User profile admin"""
list_display = ['task_name', 'image_url']
def __str__(self):
"""Return image url"""
return self.image_url
@admin.register(UserEmailOtp)
class UserEmailOtpAdmin(admin.ModelAdmin):
"""User Email otp admin"""
list_display = ['email']
def __str__(self):
"""Return object in email and otp format"""
return self.email + '-' + self.otp
@admin.register(UserDeviceDetails)
class UserDeviceDetailsAdmin(admin.ModelAdmin):
"""User profile admin"""
list_display = ['user', 'device_id']
def __str__(self):
"""Return user email"""
return self.user.email
+9
View File
@@ -0,0 +1,9 @@
"""Account app file"""
"""Import Django"""
from django.apps import AppConfig
class AccountConfig(AppConfig):
"""default configurations"""
default_auto_field = 'django.db.models.BigAutoField'
name = 'account'
+40
View File
@@ -0,0 +1,40 @@
"""middleware file"""
"""Django import"""
from rest_framework import status
from rest_framework.response import Response
from rest_framework.renderers import JSONRenderer
"""App django"""
from account.utils import custom_error_response
from account.models import UserDeviceDetails
from base.messages import ERROR_CODE, SUCCESS_CODE
# Custom middleware
# when user login with
# multiple device simultaneously
# It restricted login in
# multiple devices only
# user can login in single
# device at a time"""
class CustomMiddleware(object):
"""Custom middleware"""
def __init__(self, get_response):
"""response"""
self.get_response = get_response
def __call__(self, request):
# Code to be executed before the view is called
response = self.get_response(request)
# Code to be executed after the view is called
device_id = request.META.get('HTTP_DEVICE_ID')
if request.user.is_authenticated:
"""device details"""
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
if device_id and not device_details:
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND)
# Set content type header to "application/json"
response['Content-Type'] = 'application/json'
# Render the response as JSON
renderer = JSONRenderer()
response.content = renderer.render(response.data)
return response
+65
View File
@@ -0,0 +1,65 @@
# Generated by Django 4.2.2 on 2023-06-23 12:05
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='UserEmailOtp',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('email', models.EmailField(max_length=254)),
('otp', models.CharField(max_length=10)),
('is_verified', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('expired_at', models.DateTimeField(blank=True, null=True)),
('is_active', models.BooleanField(default=True)),
],
options={
'db_table': 'user_email_otp',
},
),
migrations.CreateModel(
name='UserPhoneOtp',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('country_code', models.IntegerField()),
('phone', models.CharField(max_length=17)),
('otp', models.CharField(max_length=10)),
('is_verified', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('expired_at', models.DateTimeField(blank=True, null=True)),
('is_active', models.BooleanField(default=True)),
],
options={
'db_table': 'user_phone_otp',
},
),
migrations.CreateModel(
name='UserProfile',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('user_type', models.CharField(blank=True, choices=[('1', 'junior'), ('2', 'guardian'), ('3', 'superuser')], default=None, max_length=15, null=True)),
('is_verified', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('is_active', models.BooleanField(default=False)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='user_profile', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_profile',
},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 12:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='useremailotp',
name='user_type',
field=models.CharField(blank=True, choices=[('1', 'junior'), ('2', 'guardian'), ('3', 'superuser')], default=None, max_length=15, null=True),
),
]
@@ -0,0 +1,26 @@
# Generated by Django 4.2.2 on 2023-07-07 10:23
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('account', '0002_useremailotp_user_type'),
]
operations = [
migrations.CreateModel(
name='DefaultTaskImages',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('task_name', models.CharField(max_length=15)),
('image_url', models.URLField(blank=True, default=None, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
options={
'db_table': 'default_task_image',
},
),
]
+31
View File
@@ -0,0 +1,31 @@
# Generated by Django 4.2.2 on 2023-07-10 09:24
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('account', '0003_defaulttaskimages'),
]
operations = [
migrations.CreateModel(
name='UserDelete',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('old_email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Original Email')),
('d_email', models.EmailField(blank=True, max_length=254, null=True, verbose_name='Dummy Email')),
('is_active', models.BooleanField(default=True)),
('reason', models.TextField(blank=True, max_length=500, null=True, verbose_name='Reason for Leaving')),
('created_at', models.DateTimeField(auto_now_add=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='delete_information_set', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_delete_information',
},
),
]
@@ -0,0 +1,31 @@
# Generated by Django 4.2.2 on 2023-07-10 12:40
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('account', '0004_userdelete'),
]
operations = [
migrations.CreateModel(
name='UserNotification',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('push_notification', models.BooleanField(default=True)),
('email_notification', models.BooleanField(default=True)),
('sms_notification', models.BooleanField(default=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user_notification', to=settings.AUTH_USER_MODEL)),
],
options={
'db_table': 'user_notification',
},
),
]
@@ -0,0 +1,21 @@
# Generated by Django 4.2.2 on 2023-07-11 11:26
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('account', '0005_usernotification'),
]
operations = [
migrations.AlterModelOptions(
name='useremailotp',
options={'verbose_name': 'User Email OTP', 'verbose_name_plural': 'User Email OTP'},
),
migrations.AlterModelOptions(
name='usernotification',
options={'verbose_name': 'User Notification', 'verbose_name_plural': 'User Notification'},
),
]
@@ -0,0 +1,21 @@
# Generated by Django 4.2.2 on 2023-07-14 09:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('account', '0006_alter_useremailotp_options_and_more'),
]
operations = [
migrations.AlterModelOptions(
name='defaulttaskimages',
options={'verbose_name': 'Default Task images', 'verbose_name_plural': 'Default Task images'},
),
migrations.AlterModelOptions(
name='userdelete',
options={'verbose_name': 'Deleted User', 'verbose_name_plural': 'Deleted User'},
),
]
@@ -0,0 +1,31 @@
# Generated by Django 4.2.2 on 2023-07-14 11:08
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('account', '0007_alter_defaulttaskimages_options_and_more'),
]
operations = [
migrations.CreateModel(
name='UserDeviceDetails',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('device_id', models.CharField(max_length=500)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='user_device_details', to=settings.AUTH_USER_MODEL)),
],
options={
'verbose_name': 'User Device Details',
'verbose_name_plural': 'User Device Details',
'db_table': 'user_device_details',
},
),
]
@@ -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),
),
]
+167
View File
@@ -0,0 +1,167 @@
"""Account module file"""
"""Django import"""
from django.db import models
from django.contrib.auth.models import User
"""App import"""
from base.constants import USER_TYPE
# Create your models here.
class UserProfile(models.Model):
"""
User details
"""
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='user_profile')
user_type = models.CharField(max_length=15, choices=USER_TYPE, null=True, blank=True, default=None)
is_verified = models.BooleanField(default=False)
# OTP validity
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_active = models.BooleanField(default=False)
class Meta(object):
""" Meta information """
db_table = 'user_profile'
def __str__(self):
"""return phone as an object"""
return f'{self.user}'
class UserPhoneOtp(models.Model):
"""
This class is used to verify user email and their contact no.
"""
"""user details"""
country_code = models.IntegerField()
phone = models.CharField(max_length=17)
"""otp details"""
otp = models.CharField(max_length=10)
is_verified = models.BooleanField(default=False)
# OTP validity
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
expired_at = models.DateTimeField(blank=True, null=True)
is_active = models.BooleanField(default=True)
class Meta(object):
""" Meta information """
db_table = 'user_phone_otp'
def __str__(self):
"""return phone as an object"""
return self.phone
class UserEmailOtp(models.Model):
"""
This class is used to verify user email and their contact no.
"""
"""user details"""
email = models.EmailField()
"""otp details"""
otp = models.CharField(max_length=10)
is_verified = models.BooleanField(default=False)
user_type = models.CharField(max_length=15, choices=USER_TYPE, null=True, blank=True, default=None)
# OTP validity
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
expired_at = models.DateTimeField(blank=True, null=True)
is_active = models.BooleanField(default=True)
class Meta(object):
""" Meta information """
db_table = 'user_email_otp'
verbose_name = 'User Email OTP'
verbose_name_plural = 'User Email OTP'
def __str__(self):
"""return phone as an object"""
return self.email
class DefaultTaskImages(models.Model):
"""Default images upload in oss bucket"""
task_name = models.CharField(max_length=15)
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)
class Meta(object):
""" Meta information """
db_table = 'default_task_image'
verbose_name = 'Default Task images'
verbose_name_plural = 'Default Task images'
def __str__(self):
"""return phone as an object"""
return self.task_name
class UserDelete(models.Model):
"""
User delete information
"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='delete_information_set')
"""Old email"""
old_email = models.EmailField(blank=True, null=True, verbose_name='Original Email')
"""Dummy email"""
d_email = models.EmailField(blank=True, null=True, verbose_name='Dummy Email')
is_active = models.BooleanField(default=True)
"""reason for leaving"""
reason = models.TextField(max_length=500, blank=True, null=True, verbose_name='Reason for Leaving')
created_at = models.DateTimeField(auto_now_add=True)
class Meta(object):
""" Meta information """
db_table = 'user_delete_information'
verbose_name = 'Deleted User'
verbose_name_plural = 'Deleted User'
def __str__(self):
return self.user.email
class UserNotification(models.Model):
"""
User notification details
"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_notification')
"""Push Notification"""
push_notification = models.BooleanField(default=True)
"""Email Notification"""
email_notification = models.BooleanField(default=True)
"""SMS Notification"""
sms_notification = models.BooleanField(default=True)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta information """
db_table = 'user_notification'
verbose_name = 'User Notification'
verbose_name_plural = 'User Notification'
def __str__(self):
return self.user.email
class UserDeviceDetails(models.Model):
"""
User notification details
"""
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_device_details')
"""Device ID"""
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)
class Meta(object):
""" Meta information """
db_table = 'user_device_details'
verbose_name = 'User Device Details'
verbose_name_plural = 'User Device Details'
def __str__(self):
return self.user.email
+378
View File
@@ -0,0 +1,378 @@
"""Account serializer"""
"""Django Import"""
# Import Refresh token of jwt
from rest_framework import serializers
from django.contrib.auth.models import User
from rest_framework_simplejwt.tokens import RefreshToken
import secrets
"""App import"""
# Import guardian's model,
# Import junior's model,
# Import account's model,
# Import constant from
# base package,
# Import messages from
# base package,
# Import some functions
# from utils file"""
from guardian.models import Guardian
from junior.models import Junior
from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp
from base.constants import GUARDIAN, JUNIOR, SUPERUSER, NUMBER
from base.messages import ERROR_CODE, SUCCESS_CODE, STATUS_CODE_ERROR
from .utils import delete_user_account_condition_social, delete_user_account_condition
# In this serializer file
# define google login serializer
# update junior profile,
# update guardian profile,
# super admin serializer,
# reset password,
# forgot password,
# change password,
# basic junior serializer,
# basic guardian serializer,
# user delete account serializer,
# user notification serializer,
# update user notification serializer,
# default task's images serializer,
# upload default task's images serializer,
# email verification serializer,
# phone otp serializer
# create all serializer here
class GoogleLoginSerializer(serializers.Serializer):
"""google login serializer"""
access_token = serializers.CharField(max_length=5000, required=True)
class Meta(object):
"""meta class"""
fields = ('access_token',)
class UpdateJuniorProfileImageSerializer(serializers.ModelSerializer):
"""update junior image"""
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'image']
def update(self, instance, validated_data):
"""update image """
junior_image = validated_data.get('image', instance.image)
instance.image = junior_image
instance.save()
return instance
class UpdateGuardianImageSerializer(serializers.ModelSerializer):
"""update guardian image"""
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id','image']
def update(self, instance, validated_data):
"""update image """
instance.image = validated_data.get('image', instance.image)
instance.save()
return instance
class ResetPasswordSerializer(serializers.Serializer):
"""Reset Password after verification"""
verification_code = serializers.CharField(max_length=10)
password = serializers.CharField(required=True)
class Meta(object):
"""Meta info"""
model = User
def create(self, validated_data):
verification_code = validated_data.pop('verification_code')
password = validated_data.pop('password')
# fetch email otp object of the user
user_opt_details = UserEmailOtp.objects.filter(otp=verification_code, is_verified=True).last()
if user_opt_details:
user_details = User.objects.filter(email=user_opt_details.email).last()
if user_details:
if user_details.check_password(password):
raise serializers.ValidationError({"details":ERROR_CODE['2001'],"code":"400", "status":"failed"})
user_details.set_password(password)
user_details.save()
return {'password':password}
return user_opt_details
return ''
class ChangePasswordSerializer(serializers.Serializer):
"""Update Password after verification"""
current_password = serializers.CharField(max_length=100)
new_password = serializers.CharField(required=True)
class Meta(object):
"""Meta info"""
model = User
def validate_current_password(self, value):
user = self.context
# check old password
if self.context.password not in ('', None) and user.check_password(value):
return value
raise serializers.ValidationError(ERROR_CODE['2015'])
def create(self, validated_data):
new_password = validated_data.pop('new_password')
current_password = validated_data.pop('current_password')
"""Check new password is different from current password"""
if new_password == current_password:
raise serializers.ValidationError({"details": ERROR_CODE['2026']})
user_details = User.objects.filter(email=self.context).last()
if user_details:
user_details.set_password(new_password)
user_details.save()
return {'password':new_password}
return ''
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',
'username', 'is_active', 'is_superuser').first()
if not user or not user.check_password(attrs['password']):
raise serializers.ValidationError({'details': ERROR_CODE['2002']})
self.context.update({'user': user})
return attrs
def create(self, validated_data):
"""
used to return the user object after validation
"""
return self.context['user']
class SuperUserSerializer(serializers.ModelSerializer):
"""Super admin serializer"""
user_type = serializers.SerializerMethodField('get_user_type')
auth_token = serializers.SerializerMethodField('get_auth_token')
refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj):
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)
refresh_token = str(refresh)
return refresh_token
def get_user_type(self, obj):
"""user type"""
return str(NUMBER['three'])
class Meta(object):
"""Meta info"""
model = User
fields = ['id', 'auth_token', 'refresh_token', 'username', 'email', 'first_name',
'last_name', 'is_active', 'user_type']
class GuardianSerializer(serializers.ModelSerializer):
"""guardian serializer"""
user_type = serializers.SerializerMethodField('get_user_type')
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
auth_token = serializers.SerializerMethodField('get_auth_token')
refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj):
refresh = RefreshToken.for_user(obj.user)
access_token = str(refresh.access_token)
return access_token
def get_refresh_token(self, obj):
refresh = RefreshToken.for_user(obj.user)
refresh_token = str(refresh)
return refresh_token
def get_user_type(self, obj):
"""user type"""
if self.context.get('user_type', ''):
return self.context.get('user_type')
# remove the below code once user_type can be passed
# from everywhere from where this serializer is being called
else:
email_verified = UserEmailOtp.objects.filter(
email=obj.user.username
).last()
if email_verified and email_verified.user_type is not None:
return email_verified.user_type
return str(NUMBER['two'])
def get_auth(self, obj):
"""user email address"""
return obj.user.username
def get_first_name(self, obj):
"""user first name"""
return obj.user.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.user.last_name
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active',
'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type', 'country_name']
class JuniorSerializer(serializers.ModelSerializer):
"""junior serializer"""
user_type = serializers.SerializerMethodField('get_user_type')
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
auth_token = serializers.SerializerMethodField('get_auth_token')
refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj):
refresh = RefreshToken.for_user(obj.auth)
access_token = str(refresh.access_token)
return access_token
def get_refresh_token(self, obj):
refresh = RefreshToken.for_user(obj.auth)
refresh_token = str(refresh)
return refresh_token
def get_user_type(self, obj):
email_verified = UserEmailOtp.objects.filter(email=obj.auth.username).last()
if email_verified and email_verified.user_type is not None:
return email_verified.user_type
return str(NUMBER['one'])
def get_auth(self, obj):
return obj.auth.username
def get_first_name(self, obj):
return obj.auth.first_name
def get_last_name(self, obj):
return obj.auth.last_name
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_password_set',
'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited']
class EmailVerificationSerializer(serializers.ModelSerializer):
"""Email verification serializer"""
class Meta(object):
"""Meta info"""
model = UserEmailOtp
fields = '__all__'
class DefaultTaskImagesSerializer(serializers.ModelSerializer):
"""Update Password after verification"""
class Meta(object):
"""Meta info"""
model = DefaultTaskImages
fields = ['id', 'task_name', 'image_url']
def create(self, validated_data):
# create default task object
data = DefaultTaskImages.objects.create(**validated_data)
return data
class DefaultTaskImagesDetailsSerializer(serializers.ModelSerializer):
"""Update Password after verification"""
class Meta(object):
"""Meta info"""
model = DefaultTaskImages
fields = '__all__'
class UserDeleteSerializer(serializers.ModelSerializer):
"""User Delete Serializer"""
class Meta(object):
"""Meta Information"""
model = UserDelete
fields = ['id','reason']
def create(self, validated_data):
user = self.context['user']
user_type = str(self.context['user_type'])
data = validated_data.get('reason')
passwd = self.context['password']
signup_method = self.context['signup_method']
random_num = secrets.randbelow(10001)
user_tb = User.objects.filter(id=user.id).last()
user_type_datas = UserEmailOtp.objects.filter(email=user.email).last()
# check password and sign up method
if user_tb and user_tb.check_password(passwd) and signup_method == str(NUMBER['one']):
user_type_data = user_type_datas.user_type
instance = delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num)
return instance
elif user_tb and passwd is None and signup_method in ['2','3']:
inst = delete_user_account_condition_social(user, user_type, user_tb, data, random_num)
return inst
else:
raise serializers.ValidationError({"details": ERROR_CODE['2031'], "code": "400", "status": "failed"})
class UserNotificationSerializer(serializers.ModelSerializer):
"""User Notification serializer"""
class Meta(object):
"""Meta info"""
model = UserNotification
fields = '__all__'
class UpdateUserNotificationSerializer(serializers.ModelSerializer):
"""Update User Notification serializer"""
class Meta(object):
"""Meta info"""
model = UserNotification
fields = ['push_notification', 'email_notification', 'sms_notification']
def create(self, validated_data):
instance = UserNotification.objects.filter(user=self.context).last()
if instance:
# change notification status
instance.push_notification = validated_data.get('push_notification',instance.push_notification)
instance.email_notification = validated_data.get('email_notification', instance.email_notification)
instance.sms_notification = validated_data.get('sms_notification', instance.sms_notification)
instance.save()
else:
instance = UserNotification.objects.create(user=self.context)
return instance
class UserPhoneOtpSerializer(serializers.ModelSerializer):
"""User Phone serializers"""
class Meta(object):
"""Meta info"""
model = UserPhoneOtp
fields = '__all__'
@@ -0,0 +1,54 @@
<!DOCTYPE HTML>
{% block subject %}DinDin{% endblock %}
{% load static %}
{% block html %}
<html lang="en" style="height: 100%;">
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8">
<meta name="viewport"
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
<title>Zod Bank | OTP</title>
<style type="text/css">
@media all and (max-width: 599px) {
.block {
display: block !important;
width: 100%;
}
.top-space {
padding: 15px 0 0 !important;
}
}
</style>
</head>
<body style="margin: 0; padding: 0; background: #f7f8f9; height: 100%;">
<!-- begin template body -->
<table style="background:#f7f8f9; border: 0; border-collapse: separate; border-spacing: 0px; margin: auto; width: 100%; font-family: Arial, Helvetica, sans-serif; height: 100%;" aria-describedby="email-data-wrapper">
<tr>
<td style="padding: 0;">
<table style="background-color: white; border-collapse: separate; border-spacing: 0px; border: 1px solid #e4e8eb; width: 100%; max-width: 600px; margin-right:auto; margin-left: auto;
font-family: Arial, Helvetica, sans-serif;" aria-describedby="email-data-wrapper">
<tr>
<td style="padding: 41px 30px 40px; height: 39px; background: url({% static 'images/backgrounds/email_template.png' %}) left center no-repeat; background-size: cover;">
</td>
</tr>
{% block plain %}
{% endblock %}
<tr>
<td style="padding: 0 27px;">
<p style="margin: 0; font-size: 14px; line-height: 20px; color: #505050; font-weight: 400;">-</p>
<p style="margin: 0; font-size: 14px; line-height: 20px; color: #505050; font-weight: 400;">Cheers!</p>
<p style="margin: 0 0 30px; font-size: 14px; line-height: 20px; color: #505050; font-weight: 700;">Zod Bank Team</p>
</td>
</tr>
</table>
</td>
</tr>
</tbody>
</table>
<!-- end template body -->
</body>
</html>
{% endblock %}
@@ -0,0 +1,23 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
OTP Verification
{% endblock %}
{% block plain %}
<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 User,
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
You are receiving this email for email verification. Please use <b>{{ otp }} </b>as the verification code for your email address & username.
</p>
</td>
</tr>
{% endblock %}
@@ -0,0 +1,23 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
Password Reset Verification Code
{% endblock %}
{% block plain %}
<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 User,
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
You are receiving this email for reset password verification. Please use <b>{{ verification_code }} </b>as the verification code.
</p>
</td>
</tr>
{% endblock %}
@@ -0,0 +1,23 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
Approval Email
{% endblock %}
{% block plain %}
<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;">
Hello,
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
Please approve {{full_name}} for accessing the Zod bank platform as a junior.
</p>
</td>
</tr>
{% endblock %}
@@ -0,0 +1,24 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
Invitation Email
{% endblock %}
{% block plain %}
<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}},
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
You are receiving this email for joining the ZOD bank platform. Please use <b>{{ url }} </b> link to join the platform.
<br> Your credentials are:- username = <b>{{email}}</b> and password <b>{{password}}</b><br> <br>Below are the steps to complete the account and how to use this platform.
</p>
</td>
</tr>
{% endblock %}
@@ -0,0 +1,22 @@
{% extends "templated_email/email_base.email" %}
{% block subject %}
{{subject}}
{% endblock %}
{% block plain %}
<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 {{name}},
</p>
</td>
</tr>
<tr>
<td style="padding: 0 27px 22px;">
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
<b>{{name}}</b> have some queries and need some support. Please support them by using their email address <b> {{sender}}</b>. <br> <br> <b>Queries are:- </b> <br> {{ message }}
</p>
</td>
</tr>
{% endblock %}
+5
View File
@@ -0,0 +1,5 @@
"""Test cases file of account"""
"""Django import"""
from django.test import TestCase
# Create your tests here.
+71
View File
@@ -0,0 +1,71 @@
""" Urls files"""
"""Django import"""
from django.urls import path, include
"""Third party import"""
from rest_framework import routers
# Import view functions
# UserLogin views,
# SendPhoneOtp views,
# UserPhoneVerification views,
# UserEmailVerification views,
# ReSendEmailOtp views,
# ForgotPasswordAPIView views,
# ResetPasswordAPIView views,
# ChangePasswordAPIView views,
# UpdateProfileImage views,
# GoogleLoginViewSet views,
# SigninWithApple views,
# ProfileAPIViewSet views,
# UploadImageAPIViewSet views,
# DefaultImageAPIViewSet views,
# DeleteUserProfileAPIViewSet views,
# UserNotificationAPIViewSet views,
# UpdateUserNotificationAPIViewSet views,
# SendSupportEmail views,
# LogoutAPIView views,
# AccessTokenAPIView views"""
from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVerification, ReSendEmailOtp,
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
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', AdminLoginViewSet, basename='admin')
"""google login end point"""
router.register('google-login', GoogleLoginViewSet, basename='admin')
router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp')
router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification')
"""email verification end point"""
router.register('user-email-verification', UserEmailVerification, basename='user-email-verification')
"""Resend email otp end point"""
router.register('resend-email-otp', ReSendEmailOtp, basename='resend-email-otp')
"""Profile end point"""
router.register('profile', ProfileAPIViewSet, basename='profile')
"""Upload default task image end point"""
router.register('upload-default-task-image', UploadImageAPIViewSet, basename='upload-default-task-image')
"""Fetch default task image end point"""
router.register('default-task-image', DefaultImageAPIViewSet, basename='default-task-image')
"""Delete user account"""
router.register('delete', DeleteUserProfileAPIViewSet, basename='delete')
"""user account notification"""
router.register('user-notification', UserNotificationAPIViewSet, basename='user-notification')
"""update user account notification"""
router.register('update-user-notification', UpdateUserNotificationAPIViewSet, basename='update-user-notification')
"""Define url pattern"""
urlpatterns = [
path('api/v1/', include(router.urls)),
path('api/v1/forgot-password/', ForgotPasswordAPIView.as_view()),
path('api/v1/reset-password/', ResetPasswordAPIView.as_view()),
path('api/v1/change-password/', ChangePasswordAPIView.as_view()),
path('api/v1/update-profile-image/', UpdateProfileImage.as_view()),
path('api/v1/apple-login/', SigninWithApple.as_view(), name='signup_with_apple'),
path('api/v1/send-support-email/', SendSupportEmail.as_view(), name='send-support-email'),
path('api/v1/logout/', LogoutAPIView.as_view(), name='logout'),
path('api/v1/generate-token/', AccessTokenAPIView.as_view(), name='generate-token')
]
+259
View File
@@ -0,0 +1,259 @@
"""Account utils"""
from celery import shared_task
"""Import django"""
from django.conf import settings
from rest_framework import viewsets, status
from rest_framework.response import Response
"""Third party Django app"""
from templated_email import send_templated_mail
import jwt
import string
from datetime import datetime
from calendar import timegm
from uuid import uuid4
# Import secrets module for generating random number
import secrets
from rest_framework import serializers
# Django App Import
# Import models from junior App,
# Import models from guardian App,
# Import models from account App,
# Import messages from base package"""
from junior.models import Junior
from guardian.models import Guardian
from account.models import UserDelete, UserDeviceDetails
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
# user account
# condition for social
# login account,
# Update junior account,
# Update guardian account,
# Define custom email for otp verification,
# Define support email for user's query,
# Define custom success response,
# Define custom error response,
# Generate access token,
# refresh token by using jwt,
# Define function for generating
# guardian code, junior code,
# referral code,
# Define function for generating
# alphanumeric code
# otp expiry
def delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num):
"""delete user account"""
if user_type == '1' and user_type_data == '1':
junior_account_update(user_tb)
elif user_type == '2' and user_type_data == '2':
guardian_account_update(user_tb)
else:
raise serializers.ValidationError({"details": ERROR_CODE['2030'], "code": "400", "status": "failed"})
user_tb.email = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower()
user_tb.username = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower()
d_email = user_tb.email
o_mail = user.email
# update user email with dummy email
user_tb.save()
"""create object in user delete model"""
instance = UserDelete.objects.create(user=user_tb, d_email=d_email, old_email=o_mail,
is_active=True, reason=data)
return instance
def delete_user_account_condition_social(user, user_type,user_tb, data, random_num):
"""delete user account"""
if user_type == '1':
junior_account_update(user_tb)
elif user_type == '2':
guardian_account_update(user_tb)
else:
raise serializers.ValidationError({"details": ERROR_CODE['2030'], "code": "400", "status": "failed"})
user_tb.email = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower()
user_tb.username = str(random_num) + str('@D_') + '{}'.format(user_tb.username).lower()
dummy_email = user_tb.email
old_mail = user.email
# update user email with dummy email
user_tb.save()
"""create object in user delete model"""
instance_data = UserDelete.objects.create(user=user_tb, d_email=dummy_email, old_email=old_mail,
is_active=True, reason=data)
return instance_data
def junior_account_update(user_tb):
"""junior account delete"""
junior_data = Junior.objects.filter(auth__email=user_tb.email).first()
if junior_data:
# Update junior account
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"""
guardian_data = Guardian.objects.filter(user__email=user_tb.email).first()
if guardian_data:
# Update guardian account
guardian_data.is_active = False
guardian_data.is_verified = False
guardian_data.save()
jun_data = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code))
"""Disassociate relation between guardian and junior"""
for data in jun_data:
data.guardian_code.remove(guardian_data.guardian_code)
data.save()
@shared_task()
def send_otp_email(recipient_email, otp):
"""Send otp on email with template"""
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [recipient_email]
"""Send otp on email"""
send_templated_mail(
template_name='email_otp_verification.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'otp': otp
}
)
return otp
@shared_task
def user_device_details(user, device_id):
"""
Used to store the device id of the user
user: user object
device_id: string
return
"""
device_details, created = UserDeviceDetails.objects.get_or_create(user=user)
if device_details:
device_details.device_id = device_id
device_details.save()
def send_support_email(name, sender, subject, message):
"""Send otp on email with template"""
to_email = [settings.EMAIL_FROM_ADDRESS]
from_email = settings.DEFAULT_ADDRESS
"""Send support email to zod bank support team"""
send_templated_mail(
template_name='support_mail.email',
from_email=from_email,
recipient_list=to_email,
context={
'name': name.title(),
'sender': sender,
'subject': subject,
'message': message
}
)
return name
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, "count": count})
def custom_error_response(detail, response_status):
"""
function is used for getting same global error response for all
:param detail: error message .
:param response_status: http status.
:return: Json response
"""
if not detail:
"""when details is empty"""
detail = {}
return Response({"error": detail, "status": "failed", "code": response_status})
def get_user_data(attrs):
"""
used to decode token
"""
user_data = jwt.decode(jwt=attrs['token'], options={'verify_signature': False},
algorithms=['RS256'])
return user_data
def generate_jwt_token(token_type: str, now_time: int, data: dict = dict):
"""
used to generate jwt token
"""
if type(data) == type:
data = {}
"""Update data dictionary"""
data.update({
'token_type': token_type,
'iss': 'your_site_url',
'iat': timegm(datetime.utcnow().utctimetuple()),
'jti': uuid4().hex
})
"""Access and Refresh token"""
TOKEN_TYPE = ["access", "refresh"]
if token_type == TOKEN_TYPE[1]:
"""Refresh token"""
exp = now_time + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME']
else:
"""access token"""
exp = now_time + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME']
data.update({
"exp": timegm(exp.utctimetuple())
})
signing_key = secrets.token_hex(32)
return jwt.encode(payload=data, key=signing_key,
algorithm='HS256')
def get_token(data: dict = dict):
""" create access and refresh token """
now_time = datetime.utcnow()
"""generate access token"""
access = generate_jwt_token('access', now_time, data)
"""generate refresh token"""
refresh = generate_jwt_token('refresh', now_time, data)
return {
'access': access,
'refresh': refresh
}
def generate_alphanumeric_code(length):
"""Generate alphanumeric code"""
alphabet = string.ascii_letters + string.digits
code = ''.join(secrets.choice(alphabet) for _ in range(length))
return code
def generate_code(value, user_id):
"""generate referral, junior and guardian code"""
code = value + str(user_id).zfill(3)
return code
OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1)
def get_user_full_name(user_obj):
"""
to get user's full name
"""
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.last_name else user_obj.first_name
+603
View File
@@ -0,0 +1,603 @@
"""Account view """
import threading
from notifications.utils import remove_fcm_token
# django imports
from datetime import datetime, timedelta
from rest_framework import viewsets, status, views
from rest_framework.decorators import action
import logging
from django.utils import timezone
import jwt
from django.contrib.auth import logout
from django.contrib.auth import authenticate, login
from rest_framework.permissions import IsAuthenticated
from templated_email import send_templated_mail
import google.oauth2.credentials
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
# local imports
from guardian.models import Guardian
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
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer,
DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer,
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, USER_TYPE_FLAG
from guardian.tasks import generate_otp
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
generate_code, OTP_EXPIRY, user_device_details)
from junior.serializers import JuniorProfileSerializer
from guardian.serializers import GuardianProfileSerializer
class GoogleLoginMixin(object):
"""google login mixin"""
@staticmethod
def google_login(request):
"""google login function"""
access_token = request.data.get('access_token')
user_type = request.data.get('user_type')
if not access_token:
return Response({'error': 'Access token is required.'}, status=status.HTTP_400_BAD_REQUEST)
try:
# Validate the access token and obtain the user's email and name
credentials = google.oauth2.credentials.Credentials.from_authorized_user_info(
info={
'access_token': access_token,
'token_uri': 'https://oauth2.googleapis.com/token',
'client_id': settings.GOOGLE_CLIENT_ID,
'client_secret': settings.GOOGLE_CLIENT_SECRET,
'refresh_token': None,
}
)
user_info_endpoint = f'https://www.googleapis.com/oauth2/v3/userinfo?access_token={access_token}'
headers = {'Authorization': f'Bearer {credentials.token}'}
response = requests.get(user_info_endpoint, headers=headers)
response.raise_for_status()
user_info = response.json()
email = user_info['email']
first_name = user_info['given_name']
last_name = user_info['family_name']
profile_picture = user_info['picture']
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
# Check if the user exists in your database or create a new user
# ...
user_data = User.objects.filter(email__iexact=email)
if user_data.exists():
if str(user_type) == '1':
junior_query = Junior.objects.filter(auth=user_data.last()).last()
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.filter(user=user_data.last()).last()
serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
if not User.objects.filter(email__iexact=email).exists():
user_obj = User.objects.create(username=email, email=email, first_name=first_name, last_name=last_name)
if str(user_type) == '1':
junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True,
image=profile_picture, signup_method='2',
junior_code=generate_code(JUN, user_obj.id),
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',
guardian_code=generate_code(GRD, user_obj.id),
referral_code=generate_code(ZOD, user_obj.id)
)
serializer = GuardianSerializer(guardian_query)
# Return a JSON response with the user's email and name
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
class GoogleLoginViewSet(GoogleLoginMixin, viewsets.GenericViewSet):
"""Google login viewset"""
serializer_class = GoogleLoginSerializer
def create(self, request):
"""create method"""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
return self.google_login(request)
class SigninWithApple(views.APIView):
"""This API is for sign in with Apple for app."""
def post(self, request):
token = request.data.get("access_token")
user_type = request.data.get("user_type")
try:
decoded_data = jwt.decode(token, options={"verify_signature": False})
user_data = {"email": decoded_data.get('email'), "username": decoded_data.get('email'), "is_active": True}
if decoded_data.get("email"):
try:
user = User.objects.get(email=decoded_data.get("email"))
if str(user_type) == '1':
junior_query = Junior.objects.filter(auth=user).last()
serializer = JuniorSerializer(junior_query)
if str(user_type) == '2':
guardian_query = Guardian.objects.filter(user=user).last()
serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
except User.DoesNotExist:
user = User.objects.create(**user_data)
if str(user_type) == '1':
junior_query = Junior.objects.create(auth=user, is_verified=True, is_active=True,
signup_method='3',
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',
guardian_code=generate_code(GRD, user.id),
referral_code=generate_code(ZOD, user.id))
serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK)
except Exception as e:
logging.error(e)
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class UpdateProfileImage(views.APIView):
"""Update profile image"""
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
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)
filename = f"images/{image.name}"
image_url = upload_image_to_alibaba(image, filename)
image_data = image_url
if str(request.data['user_type']) == '1':
junior_query = Junior.objects.filter(auth=request.user).last()
serializer = UpdateJuniorProfileImageSerializer(junior_query,
data={'image':image_data}, partial=True)
elif str(request.data['user_type']) == '2':
guardian_query = Guardian.objects.filter(user=request.user).last()
serializer = UpdateGuardianImageSerializer(guardian_query,
data={'image':image_data}, partial=True)
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3017'], 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:
logging.error(e)
return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST)
class ChangePasswordAPIView(views.APIView):
"""change password"""
serializer_class = ChangePasswordSerializer
permission_classes = [IsAuthenticated]
def post(self, request):
serializer = ChangePasswordSerializer(context=request.user, data=request.data)
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3007'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class ResetPasswordAPIView(views.APIView):
"""Reset password"""
def post(self, request):
serializer = ResetPasswordSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3006'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class ForgotPasswordAPIView(views.APIView):
"""Forgot password"""
def post(self, request):
serializer = ForgotPasswordSerializer(data=request.data)
if serializer.is_valid():
email = serializer.validated_data['email']
try:
User.objects.get(email=email)
except User.DoesNotExist:
return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND)
verification_code = generate_otp()
# Send the verification code to the user's email
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
}
)
expiry = OTP_EXPIRY
user_data, created = UserEmailOtp.objects.get_or_create(email=email)
if created:
user_data.expired_at = expiry
user_data.save()
if user_data:
user_data.otp = verification_code
user_data.expired_at = expiry
user_data.save()
return custom_response(SUCCESS_CODE['3015'],
response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class SendPhoneOtp(viewsets.ModelViewSet):
"""Send otp on phone"""
serializer_class = UserPhoneOtpSerializer
def create(self, request, *args, **kwargs):
otp = generate_otp()
phone_number = self.request.data['phone']
if phone_number.isdigit() and len(phone_number) == 10:
phone_otp, created = UserPhoneOtp.objects.get_or_create(country_code=self.request.data['country_code'],
phone=self.request.data['phone'])
if phone_otp:
phone_otp.otp = otp
phone_otp.save()
return custom_response(None, {'phone_otp':otp}, response_status=status.HTTP_200_OK)
return custom_error_response(ERROR_CODE['2020'], response_status=status.HTTP_400_BAD_REQUEST)
class UserPhoneVerification(viewsets.ModelViewSet):
"""Send otp on phone"""
serializer_class = UserPhoneOtpSerializer
def list(self, request, *args, **kwargs):
try:
phone_data = UserPhoneOtp.objects.filter(phone=self.request.GET.get('phone'),
otp=self.request.GET.get('otp')).last()
if phone_data:
phone_data.is_verified = True
phone_data.save()
return custom_response(SUCCESS_CODE['3012'], response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
except Exception:
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
class UserLogin(viewsets.ViewSet):
"""User login"""
@action(methods=['post'], detail=False)
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user_type = request.data.get('user_type')
device_id = request.META.get('HTTP_DEVICE_ID')
user = authenticate(request, username=username, password=password)
try:
if user is not None:
login(request, user)
if str(user_type) == USER_TYPE_FLAG["TWO"]:
guardian_data = Guardian.objects.filter(user__username=username, is_verified=True).last()
if guardian_data:
serializer = GuardianSerializer(
guardian_data, context={'user_type': user_type}
).data
else:
return custom_error_response(
ERROR_CODE["2070"],
response_status=status.HTTP_401_UNAUTHORIZED
)
elif str(user_type) == USER_TYPE_FLAG["FIRST"]:
junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last()
if junior_data:
serializer = JuniorSerializer(
junior_data, context={'user_type': user_type}
).data
else:
return custom_error_response(
ERROR_CODE["2071"],
response_status=status.HTTP_401_UNAUTHORIZED
)
else:
return custom_error_response(
ERROR_CODE["2069"],
response_status=status.HTTP_401_UNAUTHORIZED
)
# storing device id in using thread so the time would be reduced
threading.Thread(target=user_device_details, args=(user, device_id))
return custom_response(SUCCESS_CODE['3003'], serializer, 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)
email_verified = UserEmailOtp.objects.filter(email=username).last()
refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
data = {
"auth_token":access_token,
"refresh_token":refresh_token,
"is_profile_complete": False,
"user_type": user_type,
}
is_verified = False
if email_verified:
is_verified = email_verified.is_verified
if not is_verified:
otp = generate_otp()
email_verified.otp = otp
email_verified.save()
data.update({"email_otp":otp})
send_otp_email.delay(username, otp)
return custom_response(
ERROR_CODE['2024'],
{"email_otp": otp, "is_email_verified": is_verified},
response_status=status.HTTP_200_OK
)
data.update({"is_email_verified": is_verified})
return custom_response(
SUCCESS_CODE['3003'],
data,
response_status=status.HTTP_200_OK
)
@action(methods=['post'], detail=False)
def admin_login(self, request):
email = request.data.get('email')
password = request.data.get('password')
user = User.objects.filter(email__iexact=email, is_superuser=True
).only('id', 'first_name', 'last_name', 'email',
'username', 'is_active', 'is_superuser').first()
if not user or not user.check_password(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
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
try:
user_obj = User.objects.filter(username=self.request.data.get('email')).last()
email_data = UserEmailOtp.objects.filter(email=self.request.data.get('email'),
otp=self.request.data.get('otp')).last()
if email_data:
input_datetime_str = str(email_data.expired_at)
input_format = "%Y-%m-%d %H:%M:%S.%f%z"
output_format = "%Y-%m-%d %H:%M:%S.%f"
input_datetime = datetime.strptime(input_datetime_str, input_format)
output_datetime_str = input_datetime.strftime(output_format)
format_str = "%Y-%m-%d %H:%M:%S.%f"
datetime_obj = datetime.strptime(output_datetime_str, format_str)
if datetime.today() > datetime_obj:
return custom_error_response(ERROR_CODE["2029"], response_status=status.HTTP_400_BAD_REQUEST)
email_data.is_verified = True
email_data.save()
if email_data.user_type == '1':
junior_data = Junior.objects.filter(auth__email=self.request.data.get('email')).last()
if junior_data:
junior_data.is_verified = True
junior_data.save()
else:
guardian_data = Guardian.objects.filter(user__email=self.request.data.get('email')).last()
if guardian_data:
guardian_data.is_verified = True
guardian_data.save()
refresh = RefreshToken.for_user(user_obj)
access_token = str(refresh.access_token)
refresh_token = str(refresh)
return custom_response(SUCCESS_CODE['3011'], {"auth_token":access_token,
"refresh_token":refresh_token},
response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
logging.error(e)
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
class ReSendEmailOtp(viewsets.ModelViewSet):
"""Send otp on phone"""
serializer_class = EmailVerificationSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
otp = generate_otp()
if User.objects.filter(email=request.data['email']):
expiry = OTP_EXPIRY
email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email'])
if created:
email_data.expired_at = expiry
email_data.save()
if email_data:
email_data.otp = otp
email_data.expired_at = expiry
email_data.save()
send_otp_email(request.data['email'], otp)
return custom_response(SUCCESS_CODE['3016'], response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2023"], response_status=status.HTTP_400_BAD_REQUEST)
class ProfileAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset"""
serializer_class = JuniorProfileSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""profile view"""
if str(self.request.GET.get('user_type')) == '1':
junior_data = Junior.objects.filter(auth=self.request.user).last()
if junior_data:
serializer = JuniorProfileSerializer(junior_data)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
elif str(self.request.GET.get('user_type')) == '2':
guardian_data = Guardian.objects.filter(user=self.request.user).last()
if guardian_data:
serializer = GuardianProfileSerializer(guardian_data)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class UploadImageAPIViewSet(viewsets.ModelViewSet):
"""upload task image"""
serializer_class = DefaultTaskImagesSerializer
def create(self, request, *args, **kwargs):
"""upload images"""
image_data = request.data['image_url']
filename = f"default_task_images/{image_data.name}"
if image_data.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
image = upload_image_to_alibaba(image_data, filename)
image_data = image
request.data['image_url'] = image_data
serializer = DefaultTaskImagesSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
class DefaultImageAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset"""
serializer_class = DefaultTaskImagesDetailsSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""profile view"""
queryset = DefaultTaskImages.objects.all()
serializer = DefaultTaskImagesSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
""" Delete user API view set """
@action(detail=False, methods=['POST'], url_path='user-account',serializer_class=UserDeleteSerializer,
permission_classes=[IsAuthenticated])
def account(self, request):
user_type = str(request.data['user_type'])
password = request.data.get('password')
signup_method = str(request.data.get('signup_method'))
serializer = self.get_serializer(data=request.data, context={'request': request, 'user': request.user,
'user_type': user_type,
'password': password,
'signup_method':signup_method})
if serializer.is_valid():
serializer.save()
return custom_response(SUCCESS_CODE['3005'], response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class UserNotificationAPIViewSet(viewsets.ModelViewSet):
"""notification viewset"""
serializer_class = UserNotificationSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""profile view"""
queryset = UserNotification.objects.filter(user=request.user)
serializer = UserNotificationSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
"""Update notification viewset"""
serializer_class = UpdateUserNotificationSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
"""profile view"""
serializer = UpdateUserNotificationSerializer(data=request.data,
context=request.user)
if serializer.is_valid():
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
class SendSupportEmail(views.APIView):
"""support email api"""
permission_classes = (IsAuthenticated,)
def post(self, request):
name = request.data.get('name')
sender = request.data.get('email')
subject = request.data.get('subject')
message = request.data.get('message')
if name and sender and subject and message:
try:
send_support_email(name, sender, subject, message)
return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
else:
return custom_error_response(ERROR_CODE['2033'], response_status=status.HTTP_400_BAD_REQUEST)
class LogoutAPIView(views.APIView):
"""Log out API"""
permission_classes = (IsAuthenticated,)
def post(self, request):
remove_fcm_token(
request.auth.payload['user_id'],
request.META['HTTP_AUTHORIZATION'].split(" ")[1],
request.data.get('registration_id', ""))
logout(request)
request.session.flush()
return custom_response(SUCCESS_CODE['3020'], response_status=status.HTTP_200_OK)
class AccessTokenAPIView(views.APIView):
"""generate access token API"""
def post(self, request):
# Assuming you have a refresh_token string
refresh_token = request.data['refresh_token']
# Create a RefreshToken instance from the refresh token string
refresh = RefreshToken(refresh_token)
# Generate a new access token
access_token = str(refresh.access_token)
data = {"auth_token": access_token}
return custom_response(None, data, response_status=status.HTTP_200_OK)
+3
View File
@@ -0,0 +1,3 @@
"""
This is init module of the Project Zod Bank
"""
+30
View File
@@ -0,0 +1,30 @@
"""
Common send_mail function
"""
import logging
from django.core.mail import EmailMultiAlternatives
def send_mail(subject, message, from_email, recipient_list, html_message=None, cc=None,
fail_silently=False):
"""
Send Email
:param subject:
:param message:
:param from_email:
:param recipient_list:
:param html_message:
:param cc:
:param fail_silently:
:return:
"""
try:
mail = EmailMultiAlternatives(subject, message, from_email, recipient_list, cc)
if html_message:
mail.attach_alternative(html_message, 'text/html')
return mail.send(fail_silently)
except Exception as e:
logging.error(e)
return False
+133
View File
@@ -0,0 +1,133 @@
"""
This module contains constants used throughout the project
"""
import os
# GOOGLE_URL used for interact with google server to verify user existence.
#GOOGLE_URL = "https://www.googleapis.com/plus/v1/"
# Define Code prefix word
# for guardian code,
# junior code,
# referral code"""
ZOD = 'ZOD'
JUN = 'JUN'
GRD = 'GRD'
# Define number variable
# from zero to
# twenty and
# some standard
# number"""
NUMBER = {
'point_zero': 0.0, 'zero': 0, 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6, 'seven': 7,
'eight': 8, 'nine': 9, 'ten': 10, 'eleven': 11, 'twelve': 12, 'thirteen': 13, 'fourteen': 14, 'fifteen': 15,
'sixteen': 16, 'seventeen': 17, 'eighteen': 18, 'nineteen': 19, 'twenty': 20,
'twenty_one': 21, 'twenty_two': 22,'twenty_three': 23, 'twenty_four': 24, 'twenty_five': 25,
'thirty': 30, 'forty': 40, 'fifty': 50, 'sixty': 60, 'seventy': 70, 'eighty': 80, 'ninty': 90,
'ninety_nine': 99, 'hundred': 100, 'thirty_six_hundred': 3600
}
# Super Admin string constant for 'role'
SUPER_ADMIN = "Super Admin"
# Define jwt_token_expiration time in minutes for now token will expire after 3 days
JWT_TOKEN_EXPIRATION = 3 * 24 * 60
# Define common file extention
FILE_EXTENSION = ("gif", "jpeg", "jpg", "png", "svg")
# Define file size in bytes(5MB = 5 * 1024 * 1024)
FILE_SIZE = 5 * 1024 * 1024
# String constant for configurable date for allocation lock period
ALLOCATION_LOCK_DATE = 1
"""user type"""
USER_TYPE = (
('1', 'junior'),
('2', 'guardian'),
('3', 'superuser')
)
USER_TYPE_FLAG = {
"FIRST" : "1",
"TWO" : "2",
"THREE": "3"
}
"""gender"""
GENDERS = (
('1', 'Male'),
('2', 'Female')
)
# Task status"""
TASK_STATUS = (
('1', 'pending'),
('2', 'in-progress'),
('3', 'rejected'),
('4', 'requested'),
('5', 'completed'),
('6', 'expired')
)
# sign up method
SIGNUP_METHODS = (
('1', 'manual'),
('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'),
('2', 'legal_guardian')
)
"""
Define task status
in a number"""
PENDING = 1
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'
# define user type constant
GUARDIAN = 'guardian'
JUNIOR = 'junior'
SUPERUSER = 'superuser'
# numbers used as a constant
# Define the byte into kb
BYTE_IMAGE_SIZE = 1024
# validate file size
MAX_FILE_SIZE = 1024 * 1024 * 5
ARTICLE_SURVEY_POINTS = 5
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'
DATE_FORMAT = '%Y-%m-%d'
+16
View File
@@ -0,0 +1,16 @@
"""
This module contains constants used throughout the project
"""
from zod_bank.settings import BUCKET_NAME
# Define S3 folder url
S3_FOLDER_DIR = {
'user_image': 'user_image/',
}
# S3 bucket url
S3_URL = "https://"+BUCKET_NAME+".s3.amazonaws.com/"
S3_FOLDER_URL = {
'user_image_file': S3_URL+S3_FOLDER_DIR['user_image'],
}
+182
View File
@@ -0,0 +1,182 @@
"""
This module contains all the messages used all across the project
"""
ERROR_CODE_REQUIRED = {
# Error code for email address
"1000": ["Required email address not found."],
# Error code for password
"1001": ["Required password not found."],
# Error code for Required Post parameters
"1002": ["Required POST parameters not found."],
# Error code for Required Get parameters
"1003": ["Required GET parameters not found."],
# Error code for Required Headers
"1004": ["Required headers were not found."],
# Error code for Required Put parameters
"1005": ["Required PUT parameters not found."],
# Error code for Required query parameters
"1006": ["Required query parameters is not valid."],
# Error code for Required Head parameters
"1008": ["Required HEAD parameters not found."]
}
# Error code
ERROR_CODE = {
"2000": "Invalid email address. Please enter a registered email.",
"2001": "This is your existing password. Please choose other one",
"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": "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.",
"2012": "Phone number already used",
"2013": "Invalid token.",
"2014": "Your old password doesn't match.",
"2015": "Invalid old password.",
"2016": "Invalid search.",
"2017": "{model} object with {pk} does not exist",
"2018": "Attached File not found",
"2019": "Invalid Referral code",
"2020": "Enter valid mobile number",
"2021": "Already register",
"2022": "Invalid Guardian code",
"2023": "Invalid user",
# email not verified
"2024": "Email not verified",
"2025": "Invalid input. Expected a list of strings.",
# check old and new password
"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 has expired. Please request a new one.",
"2030": "Use correct user type and token",
# invalid password
"2031": "Invalid password",
"2032": "Failed to send email",
"2033": "Missing required fields",
"2034": "Junior is not associated",
# image size
"2035": "Image should not be 0 kb",
"2036": "Choose valid user",
# log in multiple device msg
"2037": "You are already log in another device",
"2038": "Choose valid action for task",
# card length limit
"2039": "Add at least one article card or maximum 6",
"2040": "Add at least 5 article survey or maximum 10",
# add article msg
"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",
"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",
"2069": "Invalid user type",
"2070": "You did not find as a guardian",
"2071": "You did not find as a junior"
}
"""Success message code"""
SUCCESS_CODE = {
"3000": "ok",
# Success code for password
"3001": "Sign up successfully",
# Success code for Thank you
"3002": "Thank you for contacting us! Our Consumer Experience Team will reach out to you shortly.",
# Success code for account activation
"3003": "Log in successful",
# Success code for password reset
"3004": "Password reset link has been sent to your email address",
# Success code for link verified
"3005": "Your account is deleted successfully.",
# Success code for password reset
"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
"3008": "You have a valid link.",
# Success code for logged out
"3009": "You have successfully logged out!",
# Success code for check all fields
"3010": "All fields are valid",
"3011": "Email OTP Verified successfully",
"3012": "Phone OTP Verified successfully",
"3013": "Valid Guardian code",
"3014": "Password has been updated 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",
"3020": "Logged out successfully.",
"3021": "Add junior successfully",
"3022": "Remove junior successfully",
"3023": "Junior is approved successfully",
"3024": "Junior request is rejected successfully",
"3025": "Task is approved successfully",
"3026": "Task is rejected successfully",
"3027": "Article has been created successfully.",
"3028": "Article has been updated successfully.",
"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",
"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 = {
# Status code for Invalid Input
"4001": ["Invalid input."],
# Status code for Authentication credentials
"4002": ["Authentication credentials were not provided."],
# Status code for Permission
"4003": ["You do not have permission to perform this action."],
# Status code for not found
"4004": ["Not found."],
# Status code for method not allowed
"4005": ["Method not allowed."]
}
+17
View File
@@ -0,0 +1,17 @@
"""
Custom routers for job sourcing .
"""
# third party imports
from rest_framework.routers import DefaultRouter
class OptionalSlashRouter(DefaultRouter):
"""
optional slash router class
"""
def __init__(self):
"""
explicitly appending '/' in urls if '/' doesn't exists for making common url patterns .
"""
super(OptionalSlashRouter, self).__init__()
self.trailing_slash = '/?'
+29
View File
@@ -0,0 +1,29 @@
"""
web_admin tasks file
"""
# third party imports
from celery import shared_task
from templated_email import send_templated_mail
# django imports
from django.conf import settings
@shared_task
def send_email_otp(email, verification_code):
"""
used to send otp on email
:param email: e-mail
:param verification_code: otp
"""
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [email]
send_templated_mail(
template_name='email_reset_verification.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'verification_code': verification_code
}
)
return True
+177
View File
@@ -0,0 +1,177 @@
"""
This file used for file uploaded
"""
import datetime
# python imports
import logging
import mimetypes
import os
import boto3
from django.core.files.storage import FileSystemStorage
# django imports
from django.utils.crypto import get_random_string
from rest_framework.status import HTTP_400_BAD_REQUEST, HTTP_200_OK
from base import constants
from base.constants import NUMBER
# local import
from zod_bank.settings import base_settings as settings
from zod_bank.settings.base_settings import BASE_DIR
def image_upload(folder, file_name, data):
"""
Function to upload files
:param folder:folder location string
:param file_name:file_name without ext string
:param data:data file obj
:return:Dictionary
"""
status = HTTP_400_BAD_REQUEST
img_name = None
error = None
try:
s3_client = boto3.client('s3',
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_DEFAULT_REGION
)
bucket_name = settings.BUCKET_NAME
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/tmp')
fss = FileSystemStorage()
file = fss.save('tmp/' + str(file_name), data)
fss.url(file)
tmp_file = os.path.join(MEDIA_ROOT, str(file_name))
s3_client.upload_file(
tmp_file, bucket_name, folder + str(file_name),
ExtraArgs={'ACL': 'public-read', 'ContentType': data.content_type}
)
os.unlink(tmp_file)
img_name = file_name
status = HTTP_200_OK
except Exception as e:
error = e
logging.error(e)
return status, error, img_name
def file_delete(folder, file_name):
"""
To delete common file
:param folder: folder name str
:param file_name: file_name string type
"""
status = HTTP_400_BAD_REQUEST
error = None
try:
s3_client = boto3.client('s3',
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_DEFAULT_REGION
)
s3_client.delete_object(Bucket=settings.BUCKET_NAME, Key=str(folder) + str(file_name))
status = HTTP_200_OK
except Exception as e:
error = e
return status, error
def get_aws_obj(folder, file_name):
"""
To get aws file obj
:param folder: folder string type
:param file_name: file_name string type
"""
status = HTTP_400_BAD_REQUEST
obj = None
try:
s3_client = boto3.client('s3',
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_DEFAULT_REGION
)
file_name = folder + file_name
obj = s3_client.get_object(Bucket=settings.BUCKET_NAME, Key=file_name)
status = HTTP_200_OK
except Exception as e:
logging.error(e)
return status, obj
def upload_image(post_data, folder):
"""
:param post_data:
:param folder: string type
:return:
"""
upload_obj = None
# Check Post data
if post_data:
date_now = datetime.datetime.now()
file_extension = os.path.splitext(str(post_data.name))
file_extension = file_extension[constants.NUMBER['one']].split(".")[constants.NUMBER['one']].lower()
rand = get_random_string(NUMBER['twelve'])
image_name = str(rand) + date_now.strftime("%s") + "." + file_extension
upload_obj = image_upload(folder, image_name, post_data)
return upload_obj
def upload_voice_kit_image(post_data, folder, image_dir):
"""
:param post_data:
:param folder: string type
:param image_dir: image_dir
:return:
"""
upload_obj = None
# Check Post data
if post_data:
date_now = datetime.datetime.now()
file_extension = os.path.splitext(str(post_data))
file_extension = file_extension[constants.NUMBER['one']].split(".")[constants.NUMBER['one']].lower()
rand = get_random_string(NUMBER['twelve'])
image_name = str(rand) + date_now.strftime("%s") + "." + file_extension
upload_obj = voice_kit_image_upload(folder, image_name, post_data, image_dir)
return upload_obj
def voice_kit_image_upload(folder, file_name, data, image_dir):
"""
Function to upload files
:param folder:folder location string
:param file_name:file_name without ext string
:param data:data file obj
:return:Dictionary
"""
status = HTTP_400_BAD_REQUEST
img_name = None
error = None
try:
s3_client = boto3.client('s3',
aws_access_key_id=settings.AWS_ACCESS_KEY,
aws_secret_access_key=settings.AWS_SECRET_ACCESS_KEY,
region_name=settings.AWS_DEFAULT_REGION
)
bucket_name = settings.BUCKET_NAME
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/tmp')
fss = FileSystemStorage()
with open(image_dir+data, 'rb') as f:
file = fss.save('tmp/' + str(file_name), f)
fss.url(file)
tmp_file = os.path.join(MEDIA_ROOT, str(file_name))
s3_client.upload_file(
tmp_file, bucket_name, folder + str(file_name),
ExtraArgs={'ACL': 'public-read', 'ContentType': mimetypes.guess_type(file_name)[0]}
)
os.unlink(tmp_file)
img_name = file_name
status = HTTP_200_OK
except Exception as e:
error = e
logging.error(e)
return status, error, img_name
Binary file not shown.
+39
View File
@@ -0,0 +1,39 @@
version: '3'
services:
nginx:
image: nginx:latest
container_name: nginx
restart: always
ports:
- "8000:8000"
volumes:
- ./nginx:/etc/nginx/conf.d
- .:/usr/src/app
depends_on:
- web
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
broker:
image: rabbitmq:3.7
container_name: rabbitmq
volumes:
- .:/usr/src/app
ports:
- 5673:5673
worker:
build: .
image: celery
container_name: dev_celery
restart: "always"
command: bash -c " celery -A zod_bank.celery worker --concurrency=1 -B -l DEBUG -E"
volumes:
- .:/usr/src/app
depends_on:
- broker
+23
View File
@@ -0,0 +1,23 @@
"""Guardian admin"""
"""Third party Django app"""
from django.contrib import admin
"""Import Django app"""
from .models import Guardian, JuniorTask
# Register your models here.
@admin.register(Guardian)
class GuardianAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['user', 'family_name']
def __str__(self):
"""Return email id"""
return self.user__email
@admin.register(JuniorTask)
class TaskAdmin(admin.ModelAdmin):
"""Junior Admin"""
list_display = ['id', 'task_name', 'task_status', 'junior', 'due_date', 'points', 'created_at', 'updated_at']
def __str__(self):
"""Return email id"""
return str(self.task_name) + str(self.points)
+9
View File
@@ -0,0 +1,9 @@
"""Guardian app file"""
"""Third party Django app"""
from django.apps import AppConfig
class CustodianConfig(AppConfig):
"""Guardian config"""
default_auto_field = 'django.db.models.BigAutoField'
name = 'guardian'
+43
View File
@@ -0,0 +1,43 @@
# Generated by Django 4.2.2 on 2023-06-23 12:05
from django.conf import settings
import django.contrib.postgres.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Guardian',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('country_code', models.IntegerField(blank=True, null=True)),
('phone', models.CharField(blank=True, default=None, max_length=31, null=True)),
('family_name', models.CharField(blank=True, default=None, max_length=50, null=True)),
('gender', models.CharField(blank=True, choices=[('1', 'Male'), ('2', 'Female')], default=None, max_length=15, null=True)),
('dob', models.DateField(blank=True, default=None, max_length=15, null=True)),
('guardian_code', models.CharField(blank=True, default=None, max_length=10, null=True)),
('junior_code', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, default=None, max_length=10, null=True), null=True, size=None)),
('referral_code', models.CharField(blank=True, default=None, max_length=10, null=True)),
('referral_code_used', models.CharField(blank=True, default=None, max_length=10, null=True)),
('is_active', models.BooleanField(default=True)),
('is_complete_profile', models.BooleanField(default=False)),
('passcode', models.IntegerField(blank=True, default=None, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guardian_profile', to=settings.AUTH_USER_MODEL, verbose_name='Email')),
],
options={
'verbose_name': 'Guardian',
'db_table': 'guardians',
},
),
]
@@ -0,0 +1,17 @@
# Generated by Django 4.2.2 on 2023-06-27 06:15
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('guardian', '0001_initial'),
]
operations = [
migrations.RemoveField(
model_name='guardian',
name='junior_code',
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-27 13:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0002_remove_guardian_junior_code'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=30, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-28 06:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0003_guardian_country_name'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to='images/'),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 06:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0004_guardian_image'),
]
operations = [
migrations.AlterField(
model_name='guardian',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to=''),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 12:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0005_alter_guardian_image'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='is_verified',
field=models.BooleanField(default=False),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-30 10:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0006_guardian_is_verified'),
]
operations = [
migrations.AlterField(
model_name='guardian',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=100, null=True),
),
]
+36
View File
@@ -0,0 +1,36 @@
# Generated by Django 4.2.2 on 2023-07-04 09:24
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('junior', '0006_alter_junior_country_name'),
('guardian', '0007_alter_guardian_country_name'),
]
operations = [
migrations.CreateModel(
name='JuniorTask',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('task_name', models.CharField(max_length=100)),
('task_description', models.CharField(max_length=500)),
('points', models.IntegerField(default=5)),
('due_date', models.DateField(blank=True, null=True)),
('image', models.ImageField(blank=True, default=None, null=True, upload_to='')),
('task_status', models.CharField(blank=True, choices=[('1', 'pending'), ('2', 'in-progress'), ('3', 'rejected'), ('4', 'requested'), ('5', 'completed')], default='pending', max_length=15, null=True)),
('is_active', models.BooleanField(default=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', to='guardian.guardian', verbose_name='Guardian')),
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior', to='junior.junior', verbose_name='Junior')),
],
options={
'verbose_name': 'Junior Task',
'db_table': 'junior_task',
},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-04 12:56
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0008_juniortask'),
]
operations = [
migrations.AlterField(
model_name='juniortask',
name='image',
field=models.URLField(blank=True, default=None, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-04 13:41
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0009_alter_juniortask_image'),
]
operations = [
migrations.AlterField(
model_name='juniortask',
name='task_status',
field=models.CharField(choices=[('1', 'pending'), ('2', 'in-progress'), ('3', 'rejected'), ('4', 'requested'), ('5', 'completed')], default='pending', max_length=15),
),
]
@@ -0,0 +1,28 @@
# Generated by Django 4.2.2 on 2023-07-05 11:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0010_alter_juniortask_task_status'),
]
operations = [
migrations.AddField(
model_name='juniortask',
name='default_image',
field=models.ImageField(blank=True, default=None, null=True, upload_to=''),
),
migrations.AddField(
model_name='juniortask',
name='is_approved',
field=models.BooleanField(default=False),
),
migrations.AlterField(
model_name='juniortask',
name='task_status',
field=models.CharField(choices=[('1', 'pending'), ('2', 'in-progress'), ('3', 'rejected'), ('4', 'requested'), ('5', 'completed')], default=1, max_length=15),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-06 05:12
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0011_juniortask_default_image_juniortask_is_approved_and_more'),
]
operations = [
migrations.AlterField(
model_name='juniortask',
name='default_image',
field=models.URLField(blank=True, default=None, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-06 06:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0012_alter_juniortask_default_image'),
]
operations = [
migrations.AlterField(
model_name='guardian',
name='image',
field=models.URLField(blank=True, default=None, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-11 11:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0013_alter_guardian_image'),
]
operations = [
migrations.AddField(
model_name='guardian',
name='signup_method',
field=models.CharField(choices=[('1', 'manual'), ('2', 'google'), ('3', 'apple')], default='1', max_length=31),
),
]
@@ -0,0 +1,21 @@
# Generated by Django 4.2.2 on 2023-07-14 09:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('guardian', '0014_guardian_signup_method'),
]
operations = [
migrations.AlterModelOptions(
name='guardian',
options={'verbose_name': 'Guardian', 'verbose_name_plural': 'Guardian'},
),
migrations.AlterModelOptions(
name='juniortask',
options={'verbose_name': 'Junior Task', 'verbose_name_plural': 'Junior Task'},
),
]
@@ -0,0 +1,28 @@
# Generated by Django 4.2.2 on 2023-07-18 07:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('guardian', '0015_alter_guardian_options_alter_juniortask_options'),
]
operations = [
migrations.AddField(
model_name='juniortask',
name='completed_on',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='juniortask',
name='rejected_on',
field=models.DateTimeField(blank=True, null=True),
),
migrations.AddField(
model_name='juniortask',
name='requested_on',
field=models.DateTimeField(blank=True, null=True),
),
]
@@ -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),
),
]
@@ -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',
),
]
@@ -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),
),
]
@@ -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),
),
]
+136
View File
@@ -0,0 +1,136 @@
"""Guardian model file"""
"""Third party Django app"""
from django.db import models
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"""
import junior.models
"""Add user model"""
User = get_user_model()
# Create your models here.
# Define junior model with
# various fields like
# phone, country code,
# country name,
# gender,
# date of birth,
# profile image,
# signup method,
# guardian code,
# referral code,
# referral code that used by the guardian
# is invited guardian
# profile is active or not
# profile is complete or not
# passcode
# guardian is verified or not
"""Define junior task model"""
# define name of the Task
# task description
# points of the task
# default image of the task
# image uploaded by junior
# task status
# task approved or not
# Create your models here.
class Guardian(models.Model):
"""Guardian model"""
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='guardian_profile', verbose_name='Email')
"""Contact details"""
country_code = models.IntegerField(blank=True, null=True)
phone = models.CharField(max_length=31, null=True, blank=True, default=None)
"""country name of the guardian"""
country_name = models.CharField(max_length=100, null=True, blank=True, default=None)
"""Image info"""
image = models.URLField(null=True, blank=True, default=None)
"""Personal info"""
family_name = models.CharField(max_length=50, null=True, blank=True, default=None)
"""gender of the guardian"""
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"""
is_verified = models.BooleanField(default=False)
"""guardian profile is complete or not"""
is_complete_profile = models.BooleanField(default=False)
"""passcode of the guardian profile"""
passcode = models.IntegerField(null=True, blank=True, default=None)
"""Sign up method"""
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
"""Guardian Codes"""
guardian_code = models.CharField(max_length=10, null=True, blank=True, default=None)
"""Referral code"""
referral_code = models.CharField(max_length=10, null=True, blank=True, default=None)
"""Referral code that is used by guardian while signup"""
referral_code_used = models.CharField(max_length=10, null=True, blank=True, default=None)
"""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 = 'guardians'
"""verbose name of the model"""
verbose_name = 'Guardian'
"""change another name"""
verbose_name_plural = 'Guardian'
def __str__(self):
"""Return email id"""
return f'{self.user}'
class JuniorTask(models.Model):
"""Junior Task details model"""
guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE, related_name='guardian', verbose_name='Guardian')
"""task details"""
task_name = models.CharField(max_length=100)
"""task description"""
task_description = models.CharField(max_length=500)
"""points of the task"""
points = models.IntegerField(default=TASK_POINTS)
"""last date of the task"""
due_date = models.DateField(auto_now_add=False, null=True, blank=True)
"""Images of task that is upload by guardian"""
default_image = models.URLField(null=True, blank=True, default=None)
"""image that is uploaded by junior"""
image = models.URLField(null=True, blank=True, default=None)
"""associated junior with the task"""
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"""
is_active = models.BooleanField(default=True)
"""Task is approved or not"""
is_approved = models.BooleanField(default=False)
"""request task on particular date"""
requested_on = models.DateTimeField(auto_now_add=False, null=True, blank=True)
"""reject task on particular date"""
rejected_on = models.DateTimeField(auto_now_add=False, null=True, blank=True)
"""complete task on particular date"""
completed_on = models.DateTimeField(auto_now_add=False, null=True, blank=True)
"""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_task'
"""verbose name of the model"""
verbose_name = 'Junior Task'
verbose_name_plural = 'Junior Task'
def __str__(self):
"""Return email id"""
return f'{self.task_name}'
+466
View File
@@ -0,0 +1,466 @@
"""Serializer of Guardian"""
# third party imports
import logging
from rest_framework import serializers
# Import Refresh token of jwt
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 pytz
from django.utils import timezone
# Import guardian's model,
# Import junior's model,
# Import account's model,
# Import constant from
# base package,
# Import messages from
# base package,
# Import some functions
# 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, 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, send_notification_to_junior
# In this serializer file
# define user serializer,
# create guardian serializer,
# task serializer,
# guardian serializer,
# task details serializer,
# top junior serializer,
# guardian profile serializer,
# approve junior serializer,
# approve task serializer,
class UserSerializer(serializers.ModelSerializer):
"""User serializer"""
auth_token = serializers.SerializerMethodField('get_auth_token')
class Meta(object):
"""Meta info"""
model = User
fields = ['id', 'email', 'password', 'auth_token']
def get_auth_token(self, obj):
"""generate auth token"""
refresh = RefreshToken.for_user(obj)
access_token = str(refresh.access_token)
return access_token
def create(self, validated_data):
"""fetch data"""
email = validated_data.get('email')
user_type = self.context
password = validated_data.get('password')
try:
"""Create user profile"""
user = User.objects.create_user(username=email, email=email, password=password)
UserNotification.objects.get_or_create(user=user)
if user_type == str(NUMBER['one']):
# create junior profile
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),
referral_code=generate_code(ZOD, user.id))
return user
except Exception as e:
"""Error handling"""
logging.error(e)
otp = UserEmailOtp.objects.filter(email=email).last()
otp_verified = False
if otp and otp.is_verified:
otp_verified = True
raise serializers.ValidationError({"details": ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
"code": 400, "status":"failed",
})
# update guardian profile
class CreateGuardianSerializer(serializers.ModelSerializer):
"""Create guardian serializer"""
"""Basic info"""
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
email = serializers.SerializerMethodField('get_email')
"""Contact details"""
phone = serializers.CharField(max_length=20, required=False)
country_code = serializers.IntegerField(required=False)
# basic info
family_name = serializers.CharField(max_length=100, required=False)
dob = serializers.DateField(required=False)
# code info
referral_code = serializers.CharField(max_length=100, required=False)
# image info
image = serializers.URLField(required=False)
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id', 'first_name', 'last_name', 'email', 'phone', 'family_name', 'gender', 'country_code',
'dob', 'referral_code', 'passcode', 'is_complete_profile', 'referral_code_used',
'country_name', 'image']
def get_first_name(self,obj):
"""first name of guardian"""
return obj.user.first_name
def get_last_name(self,obj):
"""last name of guardian"""
return obj.user.last_name
def get_email(self,obj):
"""emailof guardian"""
return obj.user.email
def create(self, validated_data):
"""Create guardian profile"""
user = User.objects.filter(username=self.context['user']).last()
if user:
"""Save first and last name of guardian"""
if self.context.get('first_name') != '' and self.context.get('first_name') is not None:
user.first_name = self.context.get('first_name')
if self.context.get('last_name') != '' and self.context.get('last_name') is not None:
user.last_name = self.context.get('last_name')
user.save()
"""Create guardian data"""
guardian = Guardian.objects.filter(user=self.context['user']).last()
if guardian:
"""update details according to the data get from request"""
guardian.gender = validated_data.get('gender',guardian.gender)
guardian.family_name = validated_data.get('family_name', guardian.family_name)
guardian.dob = validated_data.get('dob',guardian.dob)
"""Update country code and phone number"""
guardian.phone = validated_data.get('phone', guardian.phone)
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.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,
guardian.dob, user.first_name,
guardian.image])
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
def save(self, **kwargs):
"""Save the data into junior table"""
with transaction.atomic():
instance = super().save(**kwargs)
return instance
class TaskSerializer(serializers.ModelSerializer):
"""Task serializer"""
class Meta(object):
"""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()
# update image of the task
images = self.context['image']
validated_data['default_image'] = images
instance = JuniorTask.objects.create(**validated_data)
return instance
class GuardianDetailSerializer(serializers.ModelSerializer):
"""junior serializer"""
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
def get_auth(self, obj):
"""user email address"""
return obj.user.username
def get_first_name(self, obj):
"""user first name"""
return obj.user.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.user.last_name
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'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()
# 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
time_only = convert_timedelta_into_datetime(time_difference)
return str(time_difference.days) + ' days ' + str(time_only)
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
class Meta(object):
"""Meta info"""
model = JuniorTask
fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image',
'requested_on', 'rejected_on', 'completed_on',
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
class TopJuniorSerializer(serializers.ModelSerializer):
"""Top junior serializer"""
junior = JuniorDetailSerializer()
position = serializers.IntegerField()
class Meta(object):
"""Meta info"""
model = JuniorPoints
fields = ['id', 'junior', 'total_points', 'position', 'created_at', 'updated_at']
def to_representation(self, instance):
"""Convert instance to representation"""
representation = super().to_representation(instance)
representation['position'] = instance.position
return representation
class GuardianProfileSerializer(serializers.ModelSerializer):
"""junior serializer"""
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
total_count = serializers.SerializerMethodField('get_total_count')
complete_field_count = serializers.SerializerMethodField('get_complete_field_count')
notification_count = serializers.SerializerMethodField('get_notification_count')
def get_auth(self, obj):
"""user email address"""
return obj.user.username
def get_first_name(self, obj):
"""user first name"""
return obj.user.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.user.last_name
def get_total_count(self, obj):
"""total fields count"""
return NUMBER['five']
def get_complete_field_count(self, obj):
"""total filled fields count"""
total_field_list = [obj.user.first_name, obj.country_name,
obj.gender, obj.dob, obj.image]
# count total complete field
total_complete_field = [data for data in total_field_list if data != '' and data is not None]
return len(total_complete_field)
def get_notification_count(self, obj):
"""total notification count"""
return 0
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id', 'email', 'first_name', 'last_name', 'country_name','country_code', 'phone', 'gender', 'dob',
'guardian_code', 'notification_count', 'total_count', 'complete_field_count', 'referral_code',
'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method',
'updated_at', 'passcode']
class ApproveJuniorSerializer(serializers.ModelSerializer):
"""approve junior serializer"""
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'guardian_code']
def create(self, validated_data):
"""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):
"""Meta info"""
model = JuniorTask
fields = ['id', 'task_status', 'is_approved']
def create(self, validated_data):
"""update task status """
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_to_junior.delay(TASK_POINTS, None, junior_details.auth.id,
{'task_id': instance.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_to_junior.delay(TASK_REJECTED, None, junior_details.auth.id,
{'task_id': instance.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
+7
View File
@@ -0,0 +1,7 @@
"""task files"""
"""Django import"""
import secrets
def generate_otp():
"""generate random otp"""
digits = "0123456789"
return "".join(secrets.choice(digits) for _ in range(6))
+5
View File
@@ -0,0 +1,5 @@
"""Test file of Guardian"""
"""Third party Django app"""
from django.test import TestCase
# Create your tests here.
+45
View File
@@ -0,0 +1,45 @@
""" Urls files"""
"""Django import"""
from django.urls import path, include
from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView,
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView,
GuardianListAPIView)
"""Third party import"""
from rest_framework import routers
"""Define Router"""
router = routers.SimpleRouter()
# API End points with router
# in this file
# we define various api end point
# that is covered in this guardian
# section API:- like
# sign-up, create guardian profile,
# create-task,
# all task list, top junior,
# filter-task"""
"""Sign up API"""
router.register('sign-up', SignupViewset, basename='sign-up')
# Create guardian profile API"""
router.register('create-guardian-profile', UpdateGuardianProfile, basename='update-guardian-profile')
# Create Task API"""
router.register('create-task', CreateTaskAPIView, basename='create-task')
# All Task list API"""
router.register('all-task-list', AllTaskListAPIView, basename='all-task-list')
# Task list bases on the status API"""
router.register('task-list', TaskListAPIView, basename='task-list')
# Leaderboard API"""
router.register('top-junior', TopJuniorListAPIView, basename='top-junior')
# Search Task list on the bases of status, due date, and task title API"""
router.register('filter-task', SearchTaskListAPIView, basename='filter-task')
# Approve junior API"""
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)),
]
+111
View File
@@ -0,0 +1,111 @@
"""Utiles file of guardian"""
"""Django import"""
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 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, send_notification_to_junior
# Define upload image on
# ali baba cloud
# firstly save image
# in temporary file
# then check bucket name
# then upload on ali baba
# 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"""
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"""
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
# Upload the temporary file to Alibaba OSS
bucket.put_object_from_file(filename, temp_file.name)
"""create perfect url for image"""
new_filename = filename.replace(' ', '%20')
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}"
def real_time():
"""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"""
# convert timedelta into datetime format
hours = time_difference.seconds // NUMBER['thirty_six_hundred']
minutes = (time_difference.seconds // NUMBER['sixty']) % NUMBER['sixty']
seconds = time_difference.seconds % NUMBER['sixty']
microseconds = time_difference.microseconds
# 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_to_junior.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))
+345
View File
@@ -0,0 +1,345 @@
"""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,
# Import junior's model,
# Import account's model,
# Import constant from
# base package,
# Import messages from
# base package,
# Import some functions
# 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,
GuardianDetailListSerializer)
from .models import Guardian, JuniorTask
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from account.models import UserEmailOtp, UserNotification
from .tasks import generate_otp
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, GUARDIAN_CODE_STATUS
from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_ASSIGNED, LEADERBOARD_RANKING
from notifications.utils import send_notification_to_junior
""" Define APIs """
# Define Signup API,
# update guardian profile,
# list of all task
# list of task according to the status of the task
# create task API
# search task by name of the task API
# top junior API,
# approve junior API
# approve task API
# Create your views here.
# create approve task API
class SignupViewset(viewsets.ModelViewSet):
"""Signup view set"""
queryset = User.objects.all()
serializer_class = UserSerializer
def create(self, request, *args, **kwargs):
"""Create user profile"""
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()
# 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)
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)
else:
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
class UpdateGuardianProfile(viewsets.ViewSet):
"""Update guardian profile"""
serializer_class = CreateGuardianSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs):
"""Create guardian profile"""
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
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
queryset = JuniorTask.objects.filter(guardian__user=request.user)
# use TaskDetailsSerializer serializer
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]
filter_backends = (SearchFilter,)
search_fields = ['task_name', ]
pagination_class = PageNumberPagination
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')
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 = 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):
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
task = serializer.save()
junior_id = Junior.objects.filter(id=junior).last()
send_notification_to_junior.delay(TASK_ASSIGNED, request.auth.payload['user_id'],
junior_id.auth.id, {'task_id': task.id})
return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK)
return custom_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
def get_queryset(self):
"""Get the queryset for the view"""
title = self.request.GET.get('title')
# fetch junior query
junior_queryset = JuniorTask.objects.filter(guardian__user=self.request.user, task_name__icontains=title)\
.order_by('due_date', 'created_at')
return junior_queryset
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
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]
def get_serializer_context(self):
# context list
context = super().get_serializer_context()
context.update({'view': self})
return context
def list(self, request, *args, **kwargs):
"""Fetch junior list of those who complete their tasks"""
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_to_junior.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):
"""approve junior by guardian"""
serializer_class = ApproveJuniorSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""Get the queryset for the view"""
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
junior_queryset = Junior.objects.filter(id=self.request.data.get('junior_id')).last()
return guardian, junior_queryset
def create(self, request, *args, **kwargs):
""" junior list"""
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"""
serializer_class = ApproveTaskSerializer
permission_classes = [IsAuthenticated]
def get_queryset(self):
"""Get the queryset for the view"""
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# task query
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
guardian=guardian,
junior=self.request.data.get('junior_id')).last()
return guardian, task_queryset
def create(self, request, *args, **kwargs):
""" junior list"""
# action 1 is use for approve and 2 for reject
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)
-35
View File
@@ -1,35 +0,0 @@
import type { Config as JestConfig } from 'jest';
import { pathsToModuleNameMapper } from 'ts-jest';
import { compilerOptions as tsConfigCompilerOptions } from './tsconfig.json';
type ArrayElement<A> = A extends readonly (infer T)[] ? T : never;
type JestProjectConfig = Exclude<ArrayElement<JestConfig['projects']>, string>;
const baseJestConfig: JestConfig = {
// ...
};
const baseJestProjectConfig: JestProjectConfig = {
testEnvironment: 'node',
transform: {
'^.+\\.(t|j)s$': 'ts-jest',
},
moduleNameMapper: pathsToModuleNameMapper(tsConfigCompilerOptions.paths, { prefix: '<rootDir>' }),
globalSetup: '<rootDir>/test/global-setup.ts',
};
const projects: { name?: string; rootDir: string }[] = [{ rootDir: '.' }];
export function buildJestConfig(config?: {
jestConfigs?: JestConfig;
jestProjectConfigs?: JestProjectConfig;
}): JestConfig {
return {
...baseJestConfig,
...config?.jestConfigs,
projects: projects.map(({ name, rootDir }) => ({
...baseJestProjectConfig,
...config?.jestProjectConfigs,
...(name && { displayName: name }),
rootDir,
})),
};
}
-16
View File
@@ -1,16 +0,0 @@
import type { Config } from 'jest';
import { buildJestConfig } from './jest.base.config';
const config: Config = buildJestConfig({
jestProjectConfigs: {
testRegex: '.*\\.spec\\.ts$',
coveragePathIgnorePatterns: ['__testing__', 'entities', '<rootDir>/src/db', 'index.ts'],
},
jestConfigs: {
coverageDirectory: 'coverage',
collectCoverageFrom: ['<rootDir>/src/**/*.(t|j)s'],
coverageReporters: ['clover', 'json', 'lcov', 'text', 'text-summary', 'cobertura'],
},
});
export default config;
View File
+51
View File
@@ -0,0 +1,51 @@
"""Junior admin"""
"""Third party Django app"""
from django.contrib import admin
"""Import Django app"""
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"""
list_display = ['auth', 'guardian_code']
def __str__(self):
"""Return email id"""
return self.auth__email
@admin.register(JuniorPoints)
class JuniorPointsAdmin(admin.ModelAdmin):
"""Junior Points Admin"""
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']
+8
View File
@@ -0,0 +1,8 @@
"""App file"""
"""Import AppConfig"""
from django.apps import AppConfig
class JuniorConfig(AppConfig):
"""Junior config"""
default_auto_field = 'django.db.models.BigAutoField'
name = 'junior'
+42
View File
@@ -0,0 +1,42 @@
# Generated by Django 4.2.2 on 2023-06-23 12:05
from django.conf import settings
import django.contrib.postgres.fields
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Junior',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('phone', models.CharField(blank=True, default=None, max_length=31, null=True)),
('country_code', models.IntegerField(blank=True, null=True)),
('gender', models.CharField(blank=True, choices=[('1', 'Male'), ('2', 'Female')], default=None, max_length=10, null=True)),
('dob', models.DateField(blank=True, default=None, max_length=15, null=True)),
('junior_code', models.CharField(blank=True, default=None, max_length=10, null=True)),
('guardian_code', django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, default=None, max_length=10, null=True), null=True, size=None)),
('referral_code', models.CharField(blank=True, default=None, max_length=10, null=True)),
('referral_code_used', models.CharField(blank=True, default=None, max_length=10, null=True)),
('is_active', models.BooleanField(default=True)),
('is_complete_profile', models.BooleanField(default=False)),
('passcode', models.IntegerField(blank=True, default=None, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('auth', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_profile', to=settings.AUTH_USER_MODEL, verbose_name='Email')),
],
options={
'verbose_name': 'Junior',
'db_table': 'junior',
},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-27 13:59
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='junior',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=30, null=True),
),
]
+18
View File
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-28 10:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0002_junior_country_name'),
]
operations = [
migrations.AddField(
model_name='junior',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to='images/'),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 06:14
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0003_junior_image'),
]
operations = [
migrations.AlterField(
model_name='junior',
name='image',
field=models.ImageField(blank=True, default=None, null=True, upload_to=''),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-29 12:10
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0004_alter_junior_image'),
]
operations = [
migrations.AddField(
model_name='junior',
name='is_verified',
field=models.BooleanField(default=False),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-06-30 10:53
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0005_junior_is_verified'),
]
operations = [
migrations.AlterField(
model_name='junior',
name='country_name',
field=models.CharField(blank=True, default=None, max_length=100, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-06 12:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0006_alter_junior_country_name'),
]
operations = [
migrations.AlterField(
model_name='junior',
name='image',
field=models.URLField(blank=True, default=None, null=True),
),
]
+28
View File
@@ -0,0 +1,28 @@
# Generated by Django 4.2.2 on 2023-07-09 12:40
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('junior', '0007_alter_junior_image'),
]
operations = [
migrations.CreateModel(
name='JuniorPoints',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('total_task_points', models.IntegerField(blank=True, default=0, null=True)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('junior', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='junior_points', to='junior.junior')),
],
options={
'verbose_name': 'Junior Task Points',
'db_table': 'junior_task_points',
},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-10 07:36
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0008_juniorpoints'),
]
operations = [
migrations.AddField(
model_name='juniorpoints',
name='position',
field=models.IntegerField(blank=True, default=99999, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-11 11:26
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0009_juniorpoints_position'),
]
operations = [
migrations.AddField(
model_name='junior',
name='signup_method',
field=models.CharField(choices=[('1', 'manual'), ('2', 'google'), ('3', 'apple')], default='1', max_length=31),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-12 06:52
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0010_junior_signup_method'),
]
operations = [
migrations.AddField(
model_name='junior',
name='relationship',
field=models.CharField(blank=True, choices=[('1', 'parent'), ('2', 'legal_guardian')], default='1', max_length=31, null=True),
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-12 10:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0011_junior_relationship'),
]
operations = [
migrations.AddField(
model_name='junior',
name='is_invited',
field=models.BooleanField(default=False),
),
]
@@ -0,0 +1,21 @@
# Generated by Django 4.2.2 on 2023-07-14 09:34
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('junior', '0012_junior_is_invited'),
]
operations = [
migrations.AlterModelOptions(
name='junior',
options={'verbose_name': 'Junior', 'verbose_name_plural': 'Junior'},
),
migrations.AlterModelOptions(
name='juniorpoints',
options={'verbose_name': 'Junior Task Points', 'verbose_name_plural': 'Junior Task Points'},
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-18 09:28
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0013_alter_junior_options_alter_juniorpoints_options'),
]
operations = [
migrations.AddField(
model_name='junior',
name='is_password_set',
field=models.BooleanField(default=True),
),
]
@@ -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',
),
]
@@ -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),
),
]
@@ -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',
},
),
]
@@ -0,0 +1,22 @@
# Generated by Django 4.2.2 on 2023-08-02 11:27
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('junior', '0017_juniorguardianrelationship'),
]
operations = [
migrations.RemoveField(
model_name='junior',
name='relationship',
),
migrations.AddField(
model_name='junior',
name='guardian_code_approved',
field=models.BooleanField(default=False),
),
]
@@ -0,0 +1,30 @@
# Generated by Django 4.2.2 on 2023-08-07 13:29
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0004_alter_surveyoption_survey'),
('junior', '0018_remove_junior_relationship_and_more'),
]
operations = [
migrations.CreateModel(
name='JuniorArticlePoints',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('earn_points', models.IntegerField(blank=True, default=5, null=True)),
('is_attempt', models.BooleanField(default=False)),
('is_answer_correct', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles', to='web_admin.article')),
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_details', to='junior.junior', verbose_name='Junior')),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='web_admin.articlesurvey')),
('submitted_answer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submitted_answer', to='web_admin.surveyoption')),
],
),
]

Some files were not shown because too many files have changed in this diff Show More