Compare commits

..
211 Commits
Author SHA1 Message Date
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
dilipshrivastwa-kiwi 69c06691c7 Merge pull request #32 from KiwiTechLLC/login_code
Login code
2023-07-03 15:59:33 +05:30
jain ddfc4e946c Merge branch 'qa' of github.com:KiwiTechLLC/ZODBank-Backend into qa 2023-07-03 12:58:35 +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
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 6cbe824d9e jira-13 alibaba bucket name change 2023-06-30 15:24:49 +05:30
Ankitajain-kiwi df438241ce Merge pull request #24 from KiwiTechLLC/dev
Dev
2023-06-30 11:18:19 +05:30
Ankitajain-kiwi bb06238623 Merge pull request #21 from KiwiTechLLC/dev
Dev
2023-06-29 21:48:53 +05:30
Ankitajain-kiwi ada987413b Merge pull request #18 from KiwiTechLLC/dev
Dev
2023-06-29 21:25:18 +05:30
dilipshrivastwa-kiwi 323168b7d0 Merge pull request #11 from KiwiTechLLC/dev
Dev
2023-06-28 11:56:12 +05:30
98 changed files with 4993 additions and 305 deletions
+29 -11
View File
@@ -2,15 +2,33 @@
from django.contrib import admin from django.contrib import admin
"""Import django app""" """Import django app"""
from .models import UserProfile, UserEmailOtp, UserPhoneOtp from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete, UserDeviceDetails
# Register your models here. # Register your models here.
@admin.register(UserProfile)
class UserProfileAdmin(admin.ModelAdmin): @admin.register(UserDelete)
class UserDeleteAdmin(admin.ModelAdmin):
"""User profile admin""" """User profile admin"""
list_display = ['user'] list_display = ['user', 'old_email', 'd_email']
def __str__(self): def __str__(self):
return self.user__email """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) @admin.register(UserEmailOtp)
class UserEmailOtpAdmin(admin.ModelAdmin): class UserEmailOtpAdmin(admin.ModelAdmin):
@@ -21,11 +39,11 @@ class UserEmailOtpAdmin(admin.ModelAdmin):
"""Return object in email and otp format""" """Return object in email and otp format"""
return self.email + '-' + self.otp return self.email + '-' + self.otp
@admin.register(UserPhoneOtp) @admin.register(UserDeviceDetails)
class UserPhoneOtpAdmin(admin.ModelAdmin): class UserDeviceDetailsAdmin(admin.ModelAdmin):
"""User Phone otp admin""" """User profile admin"""
list_display = ['phone'] list_display = ['user', 'device_id']
def __str__(self): def __str__(self):
"""Return object in phone number and otp format""" """Return user email"""
return self.phone + '-' + self.otp return self.user.email
+3
View File
@@ -1,6 +1,9 @@
"""Account app file"""
"""Import Django"""
from django.apps import AppConfig from django.apps import AppConfig
class AccountConfig(AppConfig): class AccountConfig(AppConfig):
"""default configurations"""
default_auto_field = 'django.db.models.BigAutoField' default_auto_field = 'django.db.models.BigAutoField'
name = 'account' 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
@@ -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),
),
]
+92 -1
View File
@@ -1,6 +1,8 @@
"""Account module file"""
"""Django import"""
from django.db import models from django.db import models
import random
from django.contrib.auth.models import User from django.contrib.auth.models import User
"""App import"""
from base.constants import USER_TYPE from base.constants import USER_TYPE
# Create your models here. # Create your models here.
@@ -70,7 +72,96 @@ class UserEmailOtp(models.Model):
class Meta(object): class Meta(object):
""" Meta information """ """ Meta information """
db_table = 'user_email_otp' db_table = 'user_email_otp'
verbose_name = 'User Email OTP'
verbose_name_plural = 'User Email OTP'
def __str__(self): def __str__(self):
"""return phone as an object""" """return phone as an object"""
return self.email 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
+188 -48
View File
@@ -1,34 +1,77 @@
"""Account serializer"""
"""Django Import"""
# Import Refresh token of jwt
from rest_framework import serializers from rest_framework import serializers
from django.contrib.auth.models import User from django.contrib.auth.models import User
from 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 guardian.models import Guardian
from junior.models import Junior from junior.models import Junior
from account.models import UserProfile, UserEmailOtp, UserPhoneOtp from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp
from base.constants import GUARDIAN, JUNIOR, SUPERUSER from base.constants import GUARDIAN, JUNIOR, SUPERUSER, NUMBER
from django.db import transaction from base.messages import ERROR_CODE, SUCCESS_CODE, STATUS_CODE_ERROR
from base.messages import ERROR_CODE_REQUIRED, ERROR_CODE, SUCCESS_CODE, STATUS_CODE_ERROR from .utils import delete_user_account_condition_social, delete_user_account_condition
from django.core.exceptions import ObjectDoesNotExist
from django.contrib.auth import authenticate
from rest_framework import viewsets, status
from rest_framework.decorators import action
from django.contrib.auth import authenticate, login
from rest_framework_simplejwt.tokens import RefreshToken
from guardian.utils import upload_image_to_alibaba
from .utils import get_token
# 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): class GoogleLoginSerializer(serializers.Serializer):
"""google login serializer"""
access_token = serializers.CharField(max_length=5000, required=True) access_token = serializers.CharField(max_length=5000, required=True)
class Meta: class Meta(object):
"""meta class""" """meta class"""
fields = ('access_token',) 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): class UpdateGuardianImageSerializer(serializers.ModelSerializer):
"""Reset Password after verification""" """update guardian image"""
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = Guardian model = Guardian
fields = '__all__' fields = ['id','image']
def update(self, instance, validated_data): def update(self, instance, validated_data):
"""update image """ """update image """
@@ -36,21 +79,7 @@ class UpdateGuardianImageSerializer(serializers.ModelSerializer):
instance.save() instance.save()
return instance return instance
class UpdateJuniorProfileImageSerializer(serializers.ModelSerializer):
"""Reset Password after verification"""
class Meta(object):
"""Meta info"""
model = Junior
fields = '__all__'
def update(self, instance, validated_data):
"""update image """
image = validated_data.get('image', instance.image)
filename = f"images/{image.name}"
image_url = upload_image_to_alibaba(image, filename)
instance.image = image_url
instance.save()
return instance
class ResetPasswordSerializer(serializers.Serializer): class ResetPasswordSerializer(serializers.Serializer):
"""Reset Password after verification""" """Reset Password after verification"""
verification_code = serializers.CharField(max_length=10) verification_code = serializers.CharField(max_length=10)
@@ -62,6 +91,7 @@ class ResetPasswordSerializer(serializers.Serializer):
def create(self, validated_data): def create(self, validated_data):
verification_code = validated_data.pop('verification_code') verification_code = validated_data.pop('verification_code')
password = validated_data.pop('password') 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() user_opt_details = UserEmailOtp.objects.filter(otp=verification_code, is_verified=True).last()
if user_opt_details: if user_opt_details:
user_details = User.objects.filter(email=user_opt_details.email).last() user_details = User.objects.filter(email=user_opt_details.email).last()
@@ -84,13 +114,14 @@ class ChangePasswordSerializer(serializers.Serializer):
def validate_current_password(self, value): def validate_current_password(self, value):
user = self.context user = self.context
if self.context.password not in ('', None): # check old password
if user.check_password(value): if self.context.password not in ('', None) and user.check_password(value):
return value return value
raise serializers.ValidationError({"details":ERROR_CODE['2015']}) raise serializers.ValidationError(ERROR_CODE['2015'])
def create(self, validated_data): def create(self, validated_data):
new_password = validated_data.pop('new_password') new_password = validated_data.pop('new_password')
current_password = validated_data.pop('current_password') current_password = validated_data.pop('current_password')
"""Check new password is different from current password"""
if new_password == current_password: if new_password == current_password:
raise serializers.ValidationError({"details": ERROR_CODE['2026']}) raise serializers.ValidationError({"details": ERROR_CODE['2026']})
user_details = User.objects.filter(email=self.context).last() user_details = User.objects.filter(email=self.context).last()
@@ -107,16 +138,30 @@ class ForgotPasswordSerializer(serializers.Serializer):
email = serializers.EmailField() email = serializers.EmailField()
class SuperUserSerializer(serializers.ModelSerializer): class SuperUserSerializer(serializers.ModelSerializer):
"""Super admin serializer"""
user_type = serializers.SerializerMethodField('get_user_type') 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): def get_user_type(self, obj):
"""user type""" """user type"""
return SUPERUSER return str(NUMBER['three'])
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = User model = User
fields = ['id', 'username', 'email', 'first_name', 'last_name', 'is_active', 'user_type'] fields = ['id', 'auth_token', 'refresh_token', 'username', 'email', 'first_name',
'last_name', 'is_active', 'user_type']
class GuardianSerializer(serializers.ModelSerializer): class GuardianSerializer(serializers.ModelSerializer):
@@ -129,14 +174,21 @@ class GuardianSerializer(serializers.ModelSerializer):
refresh_token = serializers.SerializerMethodField('get_refresh_token') refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj): def get_auth_token(self, obj):
return get_token()['access'] refresh = RefreshToken.for_user(obj.user)
def get_refresh_token(self, obj): access_token = str(refresh.access_token)
return get_token()['refresh'] 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): def get_user_type(self, obj):
"""user type""" """user type"""
return GUARDIAN email_verified = UserEmailOtp.objects.filter(email=obj.user.username).last()
if email_verified and email_verified.user_type is not None:
return email_verified.user_type
return str(NUMBER['two'])
def get_auth(self, obj): def get_auth(self, obj):
"""user email address""" """user email address"""
@@ -153,9 +205,9 @@ class GuardianSerializer(serializers.ModelSerializer):
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = Guardian model = Guardian
fields = ['auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'family_name', 'gender', 'dob', fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
'referral_code', 'is_active', 'is_complete_profile', 'passcode', 'image', 'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active',
'created_at', 'updated_at', 'user_type'] 'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type', 'country_name']
class JuniorSerializer(serializers.ModelSerializer): class JuniorSerializer(serializers.ModelSerializer):
@@ -168,12 +220,20 @@ class JuniorSerializer(serializers.ModelSerializer):
refresh_token = serializers.SerializerMethodField('get_refresh_token') refresh_token = serializers.SerializerMethodField('get_refresh_token')
def get_auth_token(self, obj): def get_auth_token(self, obj):
return get_token()['access'] refresh = RefreshToken.for_user(obj.auth)
access_token = str(refresh.access_token)
return access_token
def get_refresh_token(self, obj): def get_refresh_token(self, obj):
return get_token()['refresh'] refresh = RefreshToken.for_user(obj.auth)
refresh_token = str(refresh)
return refresh_token
def get_user_type(self, obj): def get_user_type(self, obj):
return JUNIOR 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): def get_auth(self, obj):
return obj.auth.username return obj.auth.username
@@ -187,9 +247,9 @@ class JuniorSerializer(serializers.ModelSerializer):
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = Junior model = Junior
fields = ['auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob', fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image', 'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_password_set',
'updated_at', 'user_type'] 'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited']
class EmailVerificationSerializer(serializers.ModelSerializer): class EmailVerificationSerializer(serializers.ModelSerializer):
"""Email verification serializer""" """Email verification serializer"""
@@ -198,3 +258,83 @@ class EmailVerificationSerializer(serializers.ModelSerializer):
model = UserEmailOtp model = UserEmailOtp
fields = '__all__' 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,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 %}
+2
View File
@@ -1,3 +1,5 @@
"""Test cases file of account"""
"""Django import"""
from django.test import TestCase from django.test import TestCase
# Create your tests here. # Create your tests here.
+44 -2
View File
@@ -1,23 +1,62 @@
""" Urls files""" """ Urls files"""
"""Django import""" """Django import"""
from django.urls import path, include from django.urls import path, include
from rest_framework.decorators import api_view
"""Third party import""" """Third party import"""
from rest_framework import routers 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, from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVerification, ReSendEmailOtp,
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage, ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
GoogleLoginViewSet, SigninWithApple) GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView)
"""Router""" """Router"""
router = routers.SimpleRouter() router = routers.SimpleRouter()
"""API End points with router""" """API End points with router"""
router.register('user', UserLogin, basename='user') router.register('user', UserLogin, basename='user')
"""super admin login"""
router.register('admin', UserLogin, basename='admin') router.register('admin', UserLogin, basename='admin')
"""google login end point"""
router.register('google-login', GoogleLoginViewSet, basename='admin') router.register('google-login', GoogleLoginViewSet, basename='admin')
router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp') router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp')
router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification') router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification')
"""email verification end point"""
router.register('user-email-verification', UserEmailVerification, basename='user-email-verification') 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') 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 = [ urlpatterns = [
path('api/v1/', include(router.urls)), path('api/v1/', include(router.urls)),
path('api/v1/forgot-password/', ForgotPasswordAPIView.as_view()), path('api/v1/forgot-password/', ForgotPasswordAPIView.as_view()),
@@ -25,4 +64,7 @@ urlpatterns = [
path('api/v1/change-password/', ChangePasswordAPIView.as_view()), path('api/v1/change-password/', ChangePasswordAPIView.as_view()),
path('api/v1/update-profile-image/', UpdateProfileImage.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/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')
] ]
+140 -2
View File
@@ -1,18 +1,116 @@
"""Account utils""" """Account utils"""
"""Third party Django app""" """Import django"""
from django.conf import settings from django.conf import settings
from rest_framework import viewsets, status from rest_framework import viewsets, status
from rest_framework.response import Response from rest_framework.response import Response
"""Third party Django app"""
from templated_email import send_templated_mail from templated_email import send_templated_mail
import jwt import jwt
import string
from datetime import datetime from datetime import datetime
from calendar import timegm from calendar import timegm
from uuid import uuid4 from uuid import uuid4
# Import secrets module for generating random number
import secrets 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
from base.messages import ERROR_CODE
from django.utils import timezone
# 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
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.save()
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()
def send_otp_email(recipient_email, otp): def send_otp_email(recipient_email, otp):
"""Send otp on email with template"""
from_email = settings.EMAIL_FROM_ADDRESS from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [recipient_email] recipient_list = [recipient_email]
"""Send otp on email"""
send_templated_mail( send_templated_mail(
template_name='email_otp_verification.email', template_name='email_otp_verification.email',
from_email=from_email, from_email=from_email,
@@ -23,11 +121,28 @@ def send_otp_email(recipient_email, otp):
) )
return otp return otp
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): def custom_response(detail, data=None, response_status=status.HTTP_200_OK):
"""Custom response code""" """Custom response code"""
if not data: if not data:
"""when data is none"""
data = None data = None
return Response({"data": data, "message": detail, "status": "success", "code": response_status}) return Response({"data": data, "message": detail, "status": "success", "code": response_status})
@@ -39,6 +154,7 @@ def custom_error_response(detail, response_status):
:return: Json response :return: Json response
""" """
if not detail: if not detail:
"""when details is empty"""
detail = {} detail = {}
return Response({"error": detail, "status": "failed", "code": response_status}) return Response({"error": detail, "status": "failed", "code": response_status})
@@ -58,16 +174,20 @@ def generate_jwt_token(token_type: str, now_time: int, data: dict = dict):
""" """
if type(data) == type: if type(data) == type:
data = {} data = {}
"""Update data dictionary"""
data.update({ data.update({
'token_type': token_type, 'token_type': token_type,
'iss': 'your_site_url', 'iss': 'your_site_url',
'iat': timegm(datetime.utcnow().utctimetuple()), 'iat': timegm(datetime.utcnow().utctimetuple()),
'jti': uuid4().hex 'jti': uuid4().hex
}) })
"""Access and Refresh token"""
TOKEN_TYPE = ["access", "refresh"] TOKEN_TYPE = ["access", "refresh"]
if token_type == TOKEN_TYPE[1]: if token_type == TOKEN_TYPE[1]:
"""Refresh token"""
exp = now_time + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'] exp = now_time + settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME']
else: else:
"""access token"""
exp = now_time + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'] exp = now_time + settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME']
data.update({ data.update({
@@ -84,10 +204,28 @@ def generate_jwt_token(token_type: str, now_time: int, data: dict = dict):
def get_token(data: dict = dict): def get_token(data: dict = dict):
""" create access and refresh token """ """ create access and refresh token """
now_time = datetime.utcnow() now_time = datetime.utcnow()
"""generate access token"""
access = generate_jwt_token('access', now_time, data) access = generate_jwt_token('access', now_time, data)
"""generate refresh token"""
refresh = generate_jwt_token('refresh', now_time, data) refresh = generate_jwt_token('refresh', now_time, data)
return { return {
'access': access, 'access': access,
'refresh': refresh '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)
+267 -38
View File
@@ -1,34 +1,50 @@
"""Account view """
from notifications.utils import remove_fcm_token
"""Django import"""
from datetime import datetime, timedelta
from rest_framework import viewsets, status, views from rest_framework import viewsets, status, views
from rest_framework.decorators import action from rest_framework.decorators import action
import random import random
import logging import logging
from django.utils import timezone
import jwt import jwt
from django.contrib.auth import logout
from django.contrib.auth import authenticate, login from django.contrib.auth import authenticate, login
from guardian.models import Guardian
from junior.models import Junior
from account.models import UserProfile, UserPhoneOtp, UserEmailOtp
from django.contrib.auth.models import User
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer)
from rest_framework_simplejwt.tokens import RefreshToken
from base.messages import ERROR_CODE, SUCCESS_CODE
from guardian.tasks import generate_otp
from account.utils import send_otp_email
from account.utils import custom_response, custom_error_response
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from templated_email import send_templated_mail from templated_email import send_templated_mail
import google.oauth2.credentials import google.oauth2.credentials
import google.auth.transport.requests import google.auth.transport.requests
from rest_framework import status from rest_framework import status
from rest_framework.response import Response
import requests import requests
from rest_framework.response import Response
from django.conf import settings from django.conf import settings
from .utils import get_token """App Import"""
from guardian.models import Guardian
from junior.models import Junior
from guardian.utils import upload_image_to_alibaba
from account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification
from django.contrib.auth.models import User
"""Account serializer"""
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer,
DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer,
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer)
from rest_framework_simplejwt.tokens import RefreshToken
from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER, ZOD, JUN, GRD
from guardian.tasks import generate_otp
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
generate_code, OTP_EXPIRY)
from junior.serializers import JuniorProfileSerializer
from guardian.serializers import GuardianProfileSerializer
class GoogleLoginMixin(object):
class GoogleLoginMixin: """google login mixin"""
def google_login(self, request): @staticmethod
def google_login(request):
"""google login function"""
access_token = request.data.get('access_token') access_token = request.data.get('access_token')
user_type = request.data.get('user_type') user_type = request.data.get('user_type')
if not access_token: if not access_token:
@@ -74,11 +90,17 @@ class GoogleLoginMixin:
user_obj = User.objects.create(username=email, email=email, first_name=first_name, last_name=last_name) user_obj = User.objects.create(username=email, email=email, first_name=first_name, last_name=last_name)
if str(user_type) == '1': if str(user_type) == '1':
junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True, junior_query = Junior.objects.create(auth=user_obj, is_verified=True, is_active=True,
image=profile_picture) 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) serializer = JuniorSerializer(junior_query)
if str(user_type) == '2': if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True, guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
image=profile_picture) 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) serializer = GuardianSerializer(guardian_query)
# Return a JSON response with the user's email and name # Return a JSON response with the user's email and name
return custom_response(SUCCESS_CODE['3003'], serializer.data, return custom_response(SUCCESS_CODE['3003'], serializer.data,
@@ -86,9 +108,11 @@ class GoogleLoginMixin:
class GoogleLoginViewSet(GoogleLoginMixin, viewsets.GenericViewSet): class GoogleLoginViewSet(GoogleLoginMixin, viewsets.GenericViewSet):
"""Google login viewset"""
serializer_class = GoogleLoginSerializer serializer_class = GoogleLoginSerializer
def create(self, request): def create(self, request):
"""create method"""
serializer = self.get_serializer(data=request.data) serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True) serializer.is_valid(raise_exception=True)
return self.google_login(request) return self.google_login(request)
@@ -98,8 +122,6 @@ class SigninWithApple(views.APIView):
def post(self, request): def post(self, request):
token = request.data.get("access_token") token = request.data.get("access_token")
user_type = request.data.get("user_type") user_type = request.data.get("user_type")
if not token:
return custom_error_response(ERROR_CODE['2027'], response_status=status.HTTP_400_BAD_REQUEST)
try: try:
decoded_data = jwt.decode(token, options={"verify_signature": False}) decoded_data = jwt.decode(token, options={"verify_signature": False})
user_data = {"email": decoded_data.get('email'), "username": decoded_data.get('email'), "is_active": True} user_data = {"email": decoded_data.get('email'), "username": decoded_data.get('email'), "is_active": True}
@@ -118,10 +140,16 @@ class SigninWithApple(views.APIView):
except User.DoesNotExist: except User.DoesNotExist:
user = User.objects.create(**user_data) user = User.objects.create(**user_data)
if str(user_type) == '1': if str(user_type) == '1':
junior_query = Junior.objects.create(auth=user, is_verified=True, is_active=True) 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) serializer = JuniorSerializer(junior_query)
if str(user_type) == '2': if str(user_type) == '2':
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True) guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
signup_method='3',
guardian_code=generate_code(GRD, user.id),
referral_code=generate_code(ZOD, user.id))
serializer = GuardianSerializer(guardian_query) serializer = GuardianSerializer(guardian_query)
return custom_response(SUCCESS_CODE['3003'], serializer.data, return custom_response(SUCCESS_CODE['3003'], serializer.data,
response_status=status.HTTP_200_OK) response_status=status.HTTP_200_OK)
@@ -131,20 +159,35 @@ class SigninWithApple(views.APIView):
class UpdateProfileImage(views.APIView): class UpdateProfileImage(views.APIView):
"""Update profile image"""
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def put(self, request, format=None): def put(self, request, format=None):
if request.data['user_type'] == '1': try:
junior_query = Junior.objects.filter(auth=request.user).last() image = request.data['image']
serializer = UpdateJuniorProfileImageSerializer(junior_query, data=request.data, partial=True) if image and image.size == NUMBER['zero']:
else: return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
guardian_query = Guardian.objects.filter(user=request.user).last() filename = f"images/{image.name}"
serializer = UpdateGuardianImageSerializer(guardian_query, data=request.data, partial=True) image_url = upload_image_to_alibaba(image, filename)
if serializer.is_valid(): image_data = image_url
serializer.save() if str(request.data['user_type']) == '1':
return custom_response(SUCCESS_CODE['3017'], serializer.data, response_status=status.HTTP_200_OK) junior_query = Junior.objects.filter(auth=request.user).last()
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) 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): class ChangePasswordAPIView(views.APIView):
"""change password"""
serializer_class = ChangePasswordSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def post(self, request): def post(self, request):
serializer = ChangePasswordSerializer(context=request.user, data=request.data) serializer = ChangePasswordSerializer(context=request.user, data=request.data)
@@ -154,6 +197,7 @@ class ChangePasswordAPIView(views.APIView):
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class ResetPasswordAPIView(views.APIView): class ResetPasswordAPIView(views.APIView):
"""Reset password"""
def post(self, request): def post(self, request):
serializer = ResetPasswordSerializer(data=request.data) serializer = ResetPasswordSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
@@ -162,6 +206,8 @@ class ResetPasswordAPIView(views.APIView):
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
class ForgotPasswordAPIView(views.APIView): class ForgotPasswordAPIView(views.APIView):
"""Forgot password"""
def post(self, request): def post(self, request):
serializer = ForgotPasswordSerializer(data=request.data) serializer = ForgotPasswordSerializer(data=request.data)
if serializer.is_valid(): if serializer.is_valid():
@@ -170,7 +216,7 @@ class ForgotPasswordAPIView(views.APIView):
User.objects.get(email=email) User.objects.get(email=email)
except User.DoesNotExist: except User.DoesNotExist:
return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND) return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND)
verification_code = ''.join([str(random.randrange(9)) for _ in range(6)]) verification_code = generate_otp()
# Send the verification code to the user's email # Send the verification code to the user's email
from_email = settings.EMAIL_FROM_ADDRESS from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [email] recipient_list = [email]
@@ -182,9 +228,14 @@ class ForgotPasswordAPIView(views.APIView):
'verification_code': verification_code 'verification_code': verification_code
} }
) )
expiry = OTP_EXPIRY
user_data, created = UserEmailOtp.objects.get_or_create(email=email) user_data, created = UserEmailOtp.objects.get_or_create(email=email)
if created:
user_data.expired_at = expiry
user_data.save()
if user_data: if user_data:
user_data.otp = verification_code user_data.otp = verification_code
user_data.expired_at = expiry
user_data.save() user_data.save()
return custom_response(SUCCESS_CODE['3015'], return custom_response(SUCCESS_CODE['3015'],
response_status=status.HTTP_200_OK) response_status=status.HTTP_200_OK)
@@ -192,6 +243,8 @@ class ForgotPasswordAPIView(views.APIView):
class SendPhoneOtp(viewsets.ModelViewSet): class SendPhoneOtp(viewsets.ModelViewSet):
"""Send otp on phone""" """Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
otp = generate_otp() otp = generate_otp()
phone_number = self.request.data['phone'] phone_number = self.request.data['phone']
@@ -207,6 +260,8 @@ class SendPhoneOtp(viewsets.ModelViewSet):
class UserPhoneVerification(viewsets.ModelViewSet): class UserPhoneVerification(viewsets.ModelViewSet):
"""Send otp on phone""" """Send otp on phone"""
queryset = UserPhoneOtp.objects.all()
serializer_class = UserPhoneOtpSerializer
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
try: try:
phone_data = UserPhoneOtp.objects.filter(phone=self.request.GET.get('phone'), phone_data = UserPhoneOtp.objects.filter(phone=self.request.GET.get('phone'),
@@ -223,10 +278,12 @@ class UserPhoneVerification(viewsets.ModelViewSet):
class UserLogin(viewsets.ViewSet): class UserLogin(viewsets.ViewSet):
"""User login"""
@action(methods=['post'], detail=False) @action(methods=['post'], detail=False)
def login(self, request): def login(self, request):
username = request.data.get('username') username = request.data.get('username')
password = request.data.get('password') password = request.data.get('password')
device_id = request.META.get('HTTP_DEVICE_ID')
user = authenticate(request, username=username, password=password) user = authenticate(request, username=username, password=password)
try: try:
@@ -238,6 +295,10 @@ class UserLogin(viewsets.ViewSet):
junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last() junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last()
if junior_data: if junior_data:
serializer = JuniorSerializer(junior_data).data serializer = JuniorSerializer(junior_data).data
device_details, created = UserDeviceDetails.objects.get_or_create(user=user)
if device_details:
device_details.device_id = device_id
device_details.save()
return custom_response(SUCCESS_CODE['3003'], serializer, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3003'], serializer, response_status=status.HTTP_200_OK)
else: else:
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED) return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED)
@@ -246,7 +307,8 @@ class UserLogin(viewsets.ViewSet):
email_verified = UserEmailOtp.objects.filter(email=username).last() email_verified = UserEmailOtp.objects.filter(email=username).last()
refresh = RefreshToken.for_user(user) refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token) access_token = str(refresh.access_token)
data = {"auth_token":access_token, "is_profile_complete": False, refresh_token = str(refresh)
data = {"auth_token":access_token, "refresh_token":refresh_token, "is_profile_complete": False,
"user_type": email_verified.user_type, "user_type": email_verified.user_type,
} }
is_verified = False is_verified = False
@@ -257,10 +319,11 @@ class UserLogin(viewsets.ViewSet):
email_verified.otp = otp email_verified.otp = otp
email_verified.save() email_verified.save()
data.update({"email_otp":otp}) data.update({"email_otp":otp})
send_otp_email(username, otp)
return custom_response(ERROR_CODE['2024'], {"email_otp": otp, "is_email_verified": is_verified}, return custom_response(ERROR_CODE['2024'], {"email_otp": otp, "is_email_verified": is_verified},
response_status=status.HTTP_200_OK) response_status=status.HTTP_200_OK)
data.update({"is_email_verified": is_verified}) data.update({"is_email_verified": is_verified})
return custom_response(None, data, response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3003'], data, response_status=status.HTTP_200_OK)
@action(methods=['post'], detail=False) @action(methods=['post'], detail=False)
def admin_login(self, request): def admin_login(self, request):
@@ -279,12 +342,14 @@ class UserLogin(viewsets.ViewSet):
logging.error(e) logging.error(e)
refresh = RefreshToken.for_user(user) refresh = RefreshToken.for_user(user)
access_token = str(refresh.access_token) access_token = str(refresh.access_token)
data = {"auth_token": access_token, "user_role": '3'} refresh_token = str(refresh)
data = {"auth_token": access_token, "refresh_token":refresh_token, "user_type": '3'}
return custom_response(None, data, response_status=status.HTTP_200_OK) return custom_response(None, data, response_status=status.HTTP_200_OK)
class UserEmailVerification(viewsets.ModelViewSet): class UserEmailVerification(viewsets.ModelViewSet):
"""User Email verification""" """User Email verification"""
serializer_class = EmailVerificationSerializer serializer_class = EmailVerificationSerializer
queryset = UserEmailOtp.objects.all()
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
try: try:
@@ -292,6 +357,15 @@ class UserEmailVerification(viewsets.ModelViewSet):
email_data = UserEmailOtp.objects.filter(email=self.request.GET.get('email'), email_data = UserEmailOtp.objects.filter(email=self.request.GET.get('email'),
otp=self.request.GET.get('otp')).last() otp=self.request.GET.get('otp')).last()
if email_data: 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.is_verified = True
email_data.save() email_data.save()
if email_data.user_type == '1': if email_data.user_type == '1':
@@ -306,7 +380,10 @@ class UserEmailVerification(viewsets.ModelViewSet):
guardian_data.save() guardian_data.save()
refresh = RefreshToken.for_user(user_obj) refresh = RefreshToken.for_user(user_obj)
access_token = str(refresh.access_token) access_token = str(refresh.access_token)
return custom_response(SUCCESS_CODE['3011'], {"auth_token":access_token}, response_status=status.HTTP_200_OK) 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: else:
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e: except Exception as e:
@@ -315,14 +392,166 @@ class UserEmailVerification(viewsets.ModelViewSet):
class ReSendEmailOtp(viewsets.ModelViewSet): class ReSendEmailOtp(viewsets.ModelViewSet):
"""Send otp on phone""" """Send otp on phone"""
queryset = UserEmailOtp.objects.all()
serializer_class = EmailVerificationSerializer
permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
otp = generate_otp() otp = generate_otp()
if User.objects.filter(email=request.data['email']): if User.objects.filter(email=request.data['email']):
expiry = OTP_EXPIRY
email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email']) email_data, created = UserEmailOtp.objects.get_or_create(email=request.data['email'])
if created:
email_data.expired_at = expiry
email_data.save()
if email_data: if email_data:
email_data.otp = otp email_data.otp = otp
email_data.expired_at = expiry
email_data.save() email_data.save()
send_otp_email(request.data['email'], otp) send_otp_email(request.data['email'], otp)
return custom_response(SUCCESS_CODE['3016'], response_status=status.HTTP_200_OK) return custom_response(SUCCESS_CODE['3016'], response_status=status.HTTP_200_OK)
else: else:
return custom_error_response(ERROR_CODE["2023"], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE["2023"], response_status=status.HTTP_400_BAD_REQUEST)
class ProfileAPIViewSet(viewsets.ModelViewSet):
"""Profile viewset"""
queryset = User.objects.all()
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"""
queryset = DefaultTaskImages.objects.all()
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"""
queryset = DefaultTaskImages.objects.all()
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"""
queryset = UserNotification.objects.all()
serializer_class = UserNotificationSerializer
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""profile view"""
queryset = self.queryset.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"""
queryset = UserNotification.objects.all()
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)
+64 -4
View File
@@ -6,6 +6,28 @@ import os
# GOOGLE_URL used for interact with google server to verify user existence. # GOOGLE_URL used for interact with google server to verify user existence.
#GOOGLE_URL = "https://www.googleapis.com/plus/v1/" #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 string constant for 'role'
SUPER_ADMIN = "Super Admin" SUPER_ADMIN = "Super Admin"
@@ -22,21 +44,49 @@ FILE_SIZE = 5 * 1024 * 1024
# String constant for configurable date for allocation lock period # String constant for configurable date for allocation lock period
ALLOCATION_LOCK_DATE = 1 ALLOCATION_LOCK_DATE = 1
sort_dict = { """user type"""
'1': 'name',
'2': '-name'
}
USER_TYPE = ( USER_TYPE = (
('1', 'junior'), ('1', 'junior'),
('2', 'guardian'), ('2', 'guardian'),
('3', 'superuser') ('3', 'superuser')
) )
"""gender"""
GENDERS = ( GENDERS = (
('1', 'Male'), ('1', 'Male'),
('2', 'Female') ('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')
)
# relationship
RELATIONSHIP = (
('1', 'parent'),
('2', 'legal_guardian')
)
"""
Define task status
in a number"""
PENDING = 1
IN_PROGRESS = 2
REJECTED = 3
REQUESTED = 4
COMPLETED = 5
TASK_POINTS = 5
# duplicate name used defined in constant PROJECT_NAME # duplicate name used defined in constant PROJECT_NAME
PROJECT_NAME = 'Zod Bank' PROJECT_NAME = 'Zod Bank'
# define user type constant
GUARDIAN = 'guardian' GUARDIAN = 'guardian'
JUNIOR = 'junior' JUNIOR = 'junior'
SUPERUSER = 'superuser' SUPERUSER = 'superuser'
@@ -48,4 +98,14 @@ BYTE_IMAGE_SIZE = 1024
# validate file size # validate file size
MAX_FILE_SIZE = 1024 * 1024 * 5 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
# real time url
time_url = "http://worldtimeapi.org/api/timezone/Asia/Riyadh"
ARTICLE_CARD_IMAGE_FOLDER = 'article-card-images'
+64 -6
View File
@@ -42,27 +42,67 @@ ERROR_CODE = {
"2016": "Invalid search.", "2016": "Invalid search.",
"2017": "{model} object with {pk} does not exist", "2017": "{model} object with {pk} does not exist",
"2018": "Attached File not found", "2018": "Attached File not found",
"2019": "Either File extension or File size doesn't meet the requirements", "2019": "Invalid Referral code",
"2020": "Enter valid mobile number", "2020": "Enter valid mobile number",
"2021": "Already register", "2021": "Already register",
"2022": "Invalid Guardian code", "2022": "Invalid Guardian code",
"2023": "Invalid user", "2023": "Invalid user",
# email not verified
"2024": "Email not verified", "2024": "Email not verified",
"2025": "Invalid input. Expected a list of strings.", "2025": "Invalid input. Expected a list of strings.",
# check old and new password
"2026": "New password should not same as old password", "2026": "New password should not same as old password",
"2027": "data should contain `identityToken`" "2027": "data should contain `identityToken`",
"2028": "You are not authorized person to sign up on this platform",
"2029": "Validity of otp verification is expired",
"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"
} }
"""Success message code"""
SUCCESS_CODE = { SUCCESS_CODE = {
"3000": "ok",
# Success code for password # Success code for password
"3001": "Sign up successfully", "3001": "Sign up successfully",
# Success code for Thank you # Success code for Thank you
"3002": "Thank you for contacting us! Our Consumer Experience Team will reach out to you shortly.", "3002": "Thank you for contacting us! Our Consumer Experience Team will reach out to you shortly.",
# Success code for account activation # Success code for account activation
"3003": "Log in successfully", "3003": "Log in successful",
# Success code for password reset # Success code for password reset
"3004": "Password reset link has been sent to your email address", "3004": "Password reset link has been sent to your email address",
# Success code for link verified # Success code for link verified
"3005": "Your link has been verified, it's valid", "3005": "Your account is deleted successfully.",
# Success code for password reset # Success code for password reset
"3006": "Your password has been reset successfully.", "3006": "Your password has been reset successfully.",
# Success code for password update # Success code for password update
@@ -79,9 +119,27 @@ SUCCESS_CODE = {
"3014": "Password has been updated successfully.", "3014": "Password has been updated successfully.",
"3015": "Verification code sent on your email.", "3015": "Verification code sent on your email.",
"3016": "Send otp on your Email successfully", "3016": "Send otp on your Email successfully",
"3017": "Profile image update successfully" "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"
} }
"""status code error"""
STATUS_CODE_ERROR = { STATUS_CODE_ERROR = {
# Status code for Invalid Input # Status code for Invalid Input
"4001": ["Invalid input."], "4001": ["Invalid input."],
Binary file not shown.
+21
View File
@@ -3,6 +3,7 @@ services:
nginx: nginx:
image: nginx:latest image: nginx:latest
container_name: nginx container_name: nginx
restart: always
ports: ports:
- "8000:8000" - "8000:8000"
volumes: volumes:
@@ -13,6 +14,26 @@ services:
web: web:
build: . build: .
container_name: django 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" 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: volumes:
- .:/usr/src/app - .:/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
+10 -1
View File
@@ -2,7 +2,7 @@
"""Third party Django app""" """Third party Django app"""
from django.contrib import admin from django.contrib import admin
"""Import Django app""" """Import Django app"""
from .models import Guardian from .models import Guardian, JuniorTask
# Register your models here. # Register your models here.
@admin.register(Guardian) @admin.register(Guardian)
class GuardianAdmin(admin.ModelAdmin): class GuardianAdmin(admin.ModelAdmin):
@@ -12,3 +12,12 @@ class GuardianAdmin(admin.ModelAdmin):
def __str__(self): def __str__(self):
"""Return email id""" """Return email id"""
return self.user__email 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)
@@ -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),
),
]
+97 -4
View File
@@ -3,8 +3,38 @@
from django.db import models from django.db import models
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
"""Import Django app""" """Import Django app"""
from base.constants import GENDERS 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() 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. # Create your models here.
class Guardian(models.Model): class Guardian(models.Model):
@@ -13,21 +43,35 @@ class Guardian(models.Model):
"""Contact details""" """Contact details"""
country_code = models.IntegerField(blank=True, null=True) country_code = models.IntegerField(blank=True, null=True)
phone = models.CharField(max_length=31, null=True, blank=True, default=None) phone = models.CharField(max_length=31, null=True, blank=True, default=None)
country_name = models.CharField(max_length=30, 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 info"""
image = models.ImageField(null=True, blank=True, default=None) image = models.URLField(null=True, blank=True, default=None)
"""Personal info""" """Personal info"""
family_name = models.CharField(max_length=50, null=True, blank=True, default=None) 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) 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) 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""" """Profile activity"""
is_active = models.BooleanField(default=True) is_active = models.BooleanField(default=True)
"""guardian is verified or not"""
is_verified = models.BooleanField(default=False) is_verified = models.BooleanField(default=False)
"""guardian profile is complete or not"""
is_complete_profile = models.BooleanField(default=False) is_complete_profile = models.BooleanField(default=False)
"""passcode of the guardian profile"""
passcode = models.IntegerField(null=True, blank=True, default=None) passcode = models.IntegerField(null=True, blank=True, default=None)
"""Codes""" """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) 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 = 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) referral_code_used = models.CharField(max_length=10, null=True, blank=True, default=None)
"""Profile created and updated time""" """Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
@@ -36,8 +80,57 @@ class Guardian(models.Model):
class Meta(object): class Meta(object):
""" Meta class """ """ Meta class """
db_table = 'guardians' db_table = 'guardians'
"""verbose name of the model"""
verbose_name = 'Guardian' verbose_name = 'Guardian'
"""change another name"""
verbose_name_plural = 'Guardian'
def __str__(self): def __str__(self):
"""Return email id""" """Return email id"""
return f'{self.user}' 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}'
+259 -35
View File
@@ -1,17 +1,47 @@
"""Serializer of Guardian""" """Serializer of Guardian"""
"""Third party Django app""" """Third party Django app"""
import logging import logging
import random
from rest_framework import serializers from rest_framework import serializers
# Import Refresh token of jwt
from rest_framework_simplejwt.tokens import RefreshToken from rest_framework_simplejwt.tokens import RefreshToken
from django.db import transaction from django.db import transaction
from django.contrib.auth.models import User from django.contrib.auth.models import User
from datetime import datetime, time
"""Import Django app""" """Import Django app"""
from .models import Guardian # Import guardian's model,
from account.models import UserProfile, UserEmailOtp # 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 .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.messages import ERROR_CODE, SUCCESS_CODE
from .utils import upload_image_to_alibaba from base.constants import NUMBER, JUN, ZOD, GRD
from junior.models import Junior from junior.models import Junior, JuniorPoints
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
# notification's constant
from notifications.constants import TASK_POINTS, TASK_REJECTED
# send notification function
from notifications.utils import send_notification
# In this serializer file
# 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): class UserSerializer(serializers.ModelSerializer):
"""User serializer""" """User serializer"""
auth_token = serializers.SerializerMethodField('get_auth_token') auth_token = serializers.SerializerMethodField('get_auth_token')
@@ -19,7 +49,7 @@ class UserSerializer(serializers.ModelSerializer):
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = User model = User
fields = ['email', 'password', 'auth_token'] fields = ['id', 'email', 'password', 'auth_token']
def get_auth_token(self, obj): def get_auth_token(self, obj):
"""generate auth token""" """generate auth token"""
@@ -34,11 +64,15 @@ class UserSerializer(serializers.ModelSerializer):
try: try:
"""Create user profile""" """Create user profile"""
user = User.objects.create_user(username=email, email=email, password=password) user = User.objects.create_user(username=email, email=email, password=password)
UserProfile.objects.create(user=user, user_type=user_type) UserNotification.objects.get_or_create(user=user)
if user_type == '1': if user_type == str(NUMBER['one']):
Junior.objects.create(auth=user) # create junior profile
if user_type == '2': Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
Guardian.objects.create(user=user) referral_code=generate_code(ZOD, user.id))
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 return user
except Exception as e: except Exception as e:
"""Error handling""" """Error handling"""
@@ -51,6 +85,7 @@ class UserSerializer(serializers.ModelSerializer):
"code": 400, "status":"failed", "code": 400, "status":"failed",
}) })
# update guardian profile
class CreateGuardianSerializer(serializers.ModelSerializer): class CreateGuardianSerializer(serializers.ModelSerializer):
"""Create guardian serializer""" """Create guardian serializer"""
"""Basic info""" """Basic info"""
@@ -60,15 +95,18 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
"""Contact details""" """Contact details"""
phone = serializers.CharField(max_length=20, required=False) phone = serializers.CharField(max_length=20, required=False)
country_code = serializers.IntegerField(required=False) country_code = serializers.IntegerField(required=False)
# basic info
family_name = serializers.CharField(max_length=100, required=False) family_name = serializers.CharField(max_length=100, required=False)
dob = serializers.DateField(required=False) dob = serializers.DateField(required=False)
# code info
referral_code = serializers.CharField(max_length=100, required=False) referral_code = serializers.CharField(max_length=100, required=False)
image = serializers.ImageField(required=False) # image info
image = serializers.URLField(required=False)
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = Guardian model = Guardian
fields = ['first_name', 'last_name', 'email', 'phone', 'family_name', 'gender', 'country_code', fields = ['id', 'first_name', 'last_name', 'email', 'phone', 'family_name', 'gender', 'country_code',
'dob', 'referral_code', 'passcode', 'is_complete_profile', 'referral_code_used', 'dob', 'referral_code', 'passcode', 'is_complete_profile', 'referral_code_used',
'country_name', 'image'] 'country_name', 'image']
@@ -86,24 +124,16 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
"""Create guardian profile""" """Create guardian profile"""
# phone_number = validated_data.get('phone', None)
# guardian_data = Guardian.objects.filter(phone=phone_number)
# junior_data = Junior.objects.filter(phone=phone_number)
# if phone_number and (guardian_data or junior_data):
# raise serializers.ValidationError({"details": ERROR_CODE['2012']})
user = User.objects.filter(username=self.context['user']).last() user = User.objects.filter(username=self.context['user']).last()
if user: if user:
"""Save first and last name of guardian""" """Save first and last name of guardian"""
if self.context.get('first_name') != '' and self.context.get('last_name') != '': if self.context.get('first_name') != '' and self.context.get('first_name') is not None:
user.first_name = self.context.get('first_name', user.first_name) user.first_name = self.context.get('first_name')
user.last_name = self.context.get('last_name', user.last_name) if self.context.get('last_name') != '' and self.context.get('last_name') is not None:
user.save() user.last_name = self.context.get('last_name')
user.save()
"""Create guardian data""" """Create guardian data"""
guardian, created = Guardian.objects.get_or_create(user=self.context['user']) guardian = Guardian.objects.filter(user=self.context['user']).last()
if created:
"""Create referral code and guardian code"""
guardian.referral_code = ''.join([str(random.randrange(9)) for _ in range(4)])
guardian.guardian_code = ''.join([str(random.randrange(9)) for _ in range(4)])
if guardian: if guardian:
"""update details according to the data get from request""" """update details according to the data get from request"""
guardian.gender = validated_data.get('gender',guardian.gender) guardian.gender = validated_data.get('gender',guardian.gender)
@@ -114,18 +144,17 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
guardian.country_code = validated_data.get('country_code', guardian.country_code) guardian.country_code = validated_data.get('country_code', guardian.country_code)
guardian.passcode = validated_data.get('passcode', guardian.passcode) guardian.passcode = validated_data.get('passcode', guardian.passcode)
guardian.country_name = validated_data.get('country_name', guardian.country_name) guardian.country_name = validated_data.get('country_name', guardian.country_name)
guardian.referral_code_used = validated_data.get('referral_code_used', guardian.referral_code_used) guardian.image = validated_data.get('image', guardian.image)
"""Complete profile of the junior if below all data are filled""" """Complete profile of the junior if below all data are filled"""
complete_profile_field = all([guardian.phone, guardian.gender, guardian.family_name, guardian.country_name, complete_profile_field = all([guardian.gender, guardian.country_name,
guardian.dob, guardian.country_code, user.first_name, user.last_name]) guardian.dob, user.first_name,
guardian.image])
guardian.is_complete_profile = False guardian.is_complete_profile = False
if complete_profile_field: if complete_profile_field:
guardian.is_complete_profile = True guardian.is_complete_profile = True
image = validated_data.pop('image', None) referral_code_used = validated_data.get('referral_code_used')
if image: update_referral_points(guardian.referral_code, referral_code_used)
filename = f"images/{image.name}" guardian.referral_code_used = validated_data.get('referral_code_used', guardian.referral_code_used)
image_url = upload_image_to_alibaba(image, filename)
guardian.image = image_url
guardian.save() guardian.save()
return guardian return guardian
@@ -134,3 +163,198 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
with transaction.atomic(): with transaction.atomic():
instance = super().save(**kwargs) instance = super().save(**kwargs)
return instance 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', 'referral_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()
# 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 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.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()
send_notification.delay(TASK_POINTS, None, junior_details.auth.id, {})
else:
# reject the task
instance.task_status = str(NUMBER['three'])
instance.is_approved = False
# update total task point
junior_data.total_points = junior_data.total_points - instance.points
# update reject time of task
instance.rejected_on = real_time()
send_notification.delay(TASK_REJECTED, None, junior_details.auth.id, {})
instance.save()
junior_data.save()
return junior_details
+3 -2
View File
@@ -1,6 +1,7 @@
"""task files""" """task files"""
"""Django import""" """Django import"""
import random import secrets
def generate_otp(): def generate_otp():
"""generate random otp""" """generate random otp"""
return ''.join([str(random.randrange(9)) for _ in range(6)]) digits = "0123456789"
return "".join(secrets.choice(digits) for _ in range(6))
+27 -4
View File
@@ -1,19 +1,42 @@
""" Urls files""" """ Urls files"""
"""Django import""" """Django import"""
from django.urls import path, include from django.urls import path, include
from .views import SignupViewset, UpdateGuardianProfile from .views import (SignupViewset, UpdateGuardianProfile, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView,
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView)
"""Third party import""" """Third party import"""
from rest_framework import routers from rest_framework import routers
"""Define Router""" """Define Router"""
router = routers.SimpleRouter() router = routers.SimpleRouter()
"""API End points with router""" # 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""" """Sign up API"""
router.register('sign-up', SignupViewset, basename='sign-up') router.register('sign-up', SignupViewset, basename='sign-up')
"""Create guardian profile API""" # Create guardian profile API"""
router.register('create-guardian-profile', UpdateGuardianProfile, basename='update-guardian-profile') router.register('create-guardian-profile', UpdateGuardianProfile, basename='update-guardian-profile')
"""Define Url pattern""" # 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')
# Define Url pattern"""
urlpatterns = [ urlpatterns = [
path('api/v1/', include(router.urls)), path('api/v1/', include(router.urls)),
] ]
+94 -1
View File
@@ -1,13 +1,106 @@
"""Utiles file of guardian"""
"""Django import"""
import oss2 import oss2
"""Import setting"""
from django.conf import settings from django.conf import settings
import logging
import requests
from django.core.exceptions import ObjectDoesNotExist
"""Import tempfile"""
import tempfile import tempfile
# Import date time module's function
from datetime import datetime, time
# import Number constant
from base.constants import NUMBER, time_url
# Import Junior's model
from junior.models import Junior, JuniorPoints
# Import guardian's model
from .models import JuniorTask
# Import app from celery
from zod_bank.celery import app
# notification's constant
from notifications.constants import REFERRAL_POINTS
# send notification function
from notifications.utils import send_notification
# Define upload image on
# ali baba cloud
# firstly save image
# 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): def upload_image_to_alibaba(image, filename):
"""upload image on oss alibaba bucket"""
# Save the image object to a temporary file # Save the image object to a temporary file
with tempfile.NamedTemporaryFile(delete=False) as temp_file: with tempfile.NamedTemporaryFile(delete=False) as temp_file:
"""write image in temporary file"""
temp_file.write(image.read()) temp_file.write(image.read())
"""auth of bucket"""
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET) 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) bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
# Upload the temporary file to Alibaba OSS # Upload the temporary file to Alibaba OSS
bucket.put_object_from_file(filename, temp_file.name) bucket.put_object_from_file(filename, temp_file.name)
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{filename}" """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.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'])]
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))
+309 -29
View File
@@ -1,48 +1,328 @@
"""Views of Guardian""" """Views of Guardian"""
"""Third party Django app"""
# django imports
# Import IsAuthenticated
# Import viewsets and status
# Import PageNumberPagination
# Import User
# Import timezone
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework import viewsets, status from rest_framework import viewsets, status
"""Import Django app""" from rest_framework.pagination import PageNumberPagination
from .serializers import UserSerializer, CreateGuardianSerializer from django.contrib.auth.models import User
from .models import Guardian from django.utils import timezone
from junior.models import Junior
from account.models import UserEmailOtp # 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)
from .models import Guardian, JuniorTask
from junior.models import Junior, JuniorPoints
from account.models import UserEmailOtp, UserNotification
from .tasks import generate_otp from .tasks import generate_otp
from account.utils import send_otp_email from account.utils import custom_response, custom_error_response, OTP_EXPIRY, send_otp_email
from account.utils import custom_response, custom_error_response
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER
from .utils import upload_image_to_alibaba
from notifications.constants import REGISTRATION, TASK_CREATED, LEADERBOARD_RANKING
from notifications.utils import send_notification
""" 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 your views here.
# create approve task API
class SignupViewset(viewsets.ModelViewSet): class SignupViewset(viewsets.ModelViewSet):
"""Signup view set""" """Signup view set"""
queryset = User.objects.all()
serializer_class = UserSerializer serializer_class = UserSerializer
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""Create user profile""" """Create user profile"""
serializer = UserSerializer(context=request.data['user_type'], data=request.data) try:
if serializer.is_valid(): if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]:
serializer.save() serializer = UserSerializer(context=request.data['user_type'], data=request.data)
"""Generate otp""" if serializer.is_valid():
otp = generate_otp() user = serializer.save()
UserEmailOtp.objects.create(email=request.data['email'], otp=otp, user_type=str(request.data['user_type'])) """Generate otp"""
"""Send email to the register user""" otp = generate_otp()
send_otp_email(request.data['email'], otp) # expire otp after 1 day
return custom_response(SUCCESS_CODE['3001'], {"email_otp": otp}, expiry = OTP_EXPIRY
response_status=status.HTTP_200_OK) # create user email otp object
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
user_type=str(request.data['user_type']), expired_at=expiry)
"""Send email to the register user"""
send_otp_email(request.data['email'], otp)
# send push notification for registration
send_notification.delay(REGISTRATION, None, user.id, {})
return custom_response(SUCCESS_CODE['3001'],
response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class UpdateGuardianProfile(viewsets.ViewSet): class UpdateGuardianProfile(viewsets.ViewSet):
"""Update guardian profile""" """Update guardian profile"""
queryset = Guardian.objects.all()
serializer_class = CreateGuardianSerializer serializer_class = CreateGuardianSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""Create guardian profile""" """Create guardian profile"""
serializer = CreateGuardianSerializer(context={"user":request.user, try:
"first_name":request.data.get('first_name', ''), data = request.data
"last_name": request.data.get('last_name',' ')}, image = request.data.get('image')
data=request.data) image_url = ''
if serializer.is_valid(): if image:
"""save serializer""" if image and image.size == NUMBER['zero']:
serializer.save() return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
return custom_response(None, serializer.data,response_status=status.HTTP_200_OK) filename = f"images/{image.name}"
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) # upload image on ali baba
image_url = upload_image_to_alibaba(image, filename)
data = {"image":image_url}
serializer = CreateGuardianSerializer(context={"user":request.user,
"first_name":request.data.get('first_name'),
"last_name": request.data.get('last_name'),
"image":image_url},
data=data)
if serializer.is_valid():
"""save serializer"""
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class AllTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
queryset = JuniorTask.objects.all()
permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs):
"""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]
pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
http_method_names = ('get',)
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
try:
status_value = self.request.GET.get('status')
search = self.request.GET.get('search')
if search and str(status_value) == '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user,
task_name__icontains=search).order_by('due_date', 'created_at')
elif search and str(status_value) != '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user,task_name__icontains=search,
task_status=status_value).order_by('due_date', 'created_at')
if search is None and str(status_value) == '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user).order_by('due_date', 'created_at')
elif search is None and str(status_value) != '0':
queryset = JuniorTask.objects.filter(guardian__user=request.user,
task_status=status_value).order_by('due_date','created_at')
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = TaskDetailsSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class 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
serializer.save()
junior_id = Junior.objects.filter(id=junior).last()
send_notification.delay(TASK_CREATED, None, junior_id.auth.id, {})
return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class SearchTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
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"""
serializer_class = TopJuniorSerializer
permission_classes = [IsAuthenticated]
queryset = JuniorPoints.objects.all()
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.delay(LEADERBOARD_RANKING, None, junior.junior.auth.id, {})
junior.save()
serializer = self.get_serializer(junior_total_points, 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:
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)
+17 -2
View File
@@ -2,13 +2,28 @@
"""Third party Django app""" """Third party Django app"""
from django.contrib import admin from django.contrib import admin
"""Import Django app""" """Import Django app"""
from .models import Junior from .models import Junior, JuniorPoints, JuniorGuardianRelationship
# Register your models here. # Register your models here.
@admin.register(Junior) @admin.register(Junior)
class JuniorAdmin(admin.ModelAdmin): class JuniorAdmin(admin.ModelAdmin):
"""Junior Admin""" """Junior Admin"""
list_display = ['auth'] list_display = ['auth', 'guardian_code']
def __str__(self): def __str__(self):
"""Return email id""" """Return email id"""
return self.auth__email 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']
@@ -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',
},
),
]
+103 -9
View File
@@ -1,35 +1,77 @@
"""Junior model """ """Junior model """
"""Import django""" """Import django"""
from django.db import models from django.db import models
"""Import get_user_model function"""
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
"""Import ArrayField"""
from django.contrib.postgres.fields import ArrayField from django.contrib.postgres.fields import ArrayField
"""Import django app""" """Import django app"""
from base.constants import GENDERS from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP
# Import guardian's model
from guardian.models import Guardian
"""Define User model"""
User = get_user_model() User = get_user_model()
# Create your models here. # Create your models here.
# Define junior model with
# various fields like
# phone, country code,
# country name,
# gender,
# date of birth,
# profile image,
# relationship type of the guardian
# signup method,
# guardian code,
# junior code,
# referral code,
# referral code that used by the junior
# is invited junior
# profile is active or not
# profile is complete or not
# passcode
# junior is verified or not
"""Define junior points model"""
# points of the junior
# position of the junior
# define junior guardian relation model
class Junior(models.Model): class Junior(models.Model):
"""Junior model""" """Junior model"""
auth = models.ForeignKey(User, on_delete=models.CASCADE, related_name='junior_profile', verbose_name='Email') auth = models.ForeignKey(User, on_delete=models.CASCADE, related_name='junior_profile', verbose_name='Email')
"""Contact details""" # Contact details"""
phone = models.CharField(max_length=31, null=True, blank=True, default=None) phone = models.CharField(max_length=31, null=True, blank=True, default=None)
country_code = models.IntegerField(blank=True, null=True) country_code = models.IntegerField(blank=True, null=True)
country_name = models.CharField(max_length=30, null=True, blank=True, default=None) # country name of the guardian"""
"""Personal info""" country_name = models.CharField(max_length=100, null=True, blank=True, default=None)
# Personal info"""
gender = models.CharField(max_length=10, choices=GENDERS, null=True, blank=True, default=None) gender = models.CharField(max_length=10, choices=GENDERS, null=True, blank=True, default=None)
# Date of birth"""
dob = models.DateField(max_length=15, null=True, blank=True, default=None) dob = models.DateField(max_length=15, null=True, blank=True, default=None)
image = models.ImageField(null=True, blank=True, default=None) # Image of the junior"""
"""Codes""" image = models.URLField(null=True, blank=True, default=None)
# Sign up method"""
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
# Codes"""
junior_code = models.CharField(max_length=10, null=True, blank=True, default=None) junior_code = models.CharField(max_length=10, null=True, blank=True, default=None)
# Guardian Codes"""
guardian_code = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None),null=True) guardian_code = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None),null=True)
# Referral code"""
referral_code = models.CharField(max_length=10, null=True, blank=True, default=None) referral_code = models.CharField(max_length=10, null=True, blank=True, default=None)
# Referral code that is used by junior while signup"""
referral_code_used = models.CharField(max_length=10, null=True, blank=True, default=None) referral_code_used = models.CharField(max_length=10, null=True, blank=True, default=None)
"""Profile activity""" # invited junior"""
is_invited = models.BooleanField(default=False)
# Profile activity"""
is_active = models.BooleanField(default=True) is_active = models.BooleanField(default=True)
# check password is set or not
is_password_set = models.BooleanField(default=True)
# junior profile is complete or not"""
is_complete_profile = models.BooleanField(default=False) is_complete_profile = models.BooleanField(default=False)
# passcode of the junior profile"""
passcode = models.IntegerField(null=True, blank=True, default=None) passcode = models.IntegerField(null=True, blank=True, default=None)
# junior is verified or not"""
is_verified = models.BooleanField(default=False) is_verified = models.BooleanField(default=False)
"""Profile created and updated time""" # Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True) created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True) updated_at = models.DateTimeField(auto_now=True)
@@ -37,7 +79,59 @@ class Junior(models.Model):
""" Meta class """ """ Meta class """
db_table = 'junior' db_table = 'junior'
verbose_name = 'Junior' verbose_name = 'Junior'
# another name of the model"""
verbose_name_plural = 'Junior'
def __str__(self): def __str__(self):
"""Return email id""" """Return email id"""
return f'{self.auth}' return f'{self.auth}'
class JuniorPoints(models.Model):
"""Junior model"""
junior = models.OneToOneField(Junior, on_delete=models.CASCADE, related_name='junior_points')
# Total earned points"""
total_points = models.IntegerField(blank=True, null=True, default=0)
# referral points"""
referral_points = models.IntegerField(blank=True, null=True, default=0)
# position of the junior"""
position = models.IntegerField(blank=True, null=True, default=99999)
# Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
db_table = 'junior_task_points'
verbose_name = 'Junior Task Points'
# another name of the model"""
verbose_name_plural = 'Junior Task Points'
def __str__(self):
"""Return email id"""
return f'{self.junior.auth}'
class JuniorGuardianRelationship(models.Model):
"""Junior Guardian relationship model"""
guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE, related_name='guardian_relation',
verbose_name='Guardian')
# associated junior with the task
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='junior_relation', verbose_name='Junior')
# relation between guardian and junior"""
relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True,
default='1')
"""Profile created and updated time"""
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta(object):
""" Meta class """
db_table = 'junior_guardian_relation'
"""verbose name of the model"""
verbose_name = 'Junior Guardian Relation'
verbose_name_plural = 'Junior Guardian Relation'
def __str__(self):
"""Return email id"""
return f'{self.guardian.user}'
+376 -29
View File
@@ -3,12 +3,24 @@
from rest_framework import serializers from rest_framework import serializers
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.db import transaction from django.db import transaction
import random from datetime import datetime
from django.utils import timezone
from rest_framework_simplejwt.tokens import RefreshToken
"""Import django app""" """Import django app"""
from junior.models import Junior from account.utils import send_otp_email, generate_code
from guardian.utils import upload_image_to_alibaba from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
from guardian.tasks import generate_otp
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from guardian.models import Guardian from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD
from guardian.models import Guardian, JuniorTask
from account.models import UserEmailOtp, UserNotification
from junior.utils import junior_notification_email, junior_approval_mail
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
from notifications.utils import send_notification
from notifications.constants import (INVITED_GUARDIAN, APPROVED_JUNIOR, SKIPPED_PROFILE_SETUP, TASK_ACTION,
TASK_SUBMITTED)
class ListCharField(serializers.ListField): class ListCharField(serializers.ListField):
"""Serializer for Array field""" """Serializer for Array field"""
@@ -35,14 +47,14 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
dob = serializers.DateField(required=False) dob = serializers.DateField(required=False)
referral_code = serializers.CharField(max_length=100, required=False) referral_code = serializers.CharField(max_length=100, required=False)
guardian_code = ListCharField(required=False) guardian_code = ListCharField(required=False)
image = serializers.ImageField(required=False) image = serializers.URLField(required=False)
class Meta(object): class Meta(object):
"""Meta info""" """Meta info"""
model = Junior model = Junior
fields = ['first_name', 'last_name', 'email', 'phone', 'gender', 'country_code', 'dob', 'referral_code', fields = ['id', 'first_name', 'last_name', 'email', 'phone', 'gender', 'country_code', 'dob', 'referral_code',
'passcode', 'is_complete_profile', 'guardian_code', 'referral_code_used', 'passcode', 'is_complete_profile', 'guardian_code', 'referral_code_used',
'country_name', 'image'] 'country_name', 'image', 'is_invited']
def get_first_name(self,obj): def get_first_name(self,obj):
"""first name of junior""" """first name of junior"""
@@ -58,47 +70,41 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
def create(self, validated_data): def create(self, validated_data):
"""Create junior profile""" """Create junior profile"""
image = validated_data.get('image', None) guardian_code = validated_data.get('guardian_code',None)
# phone_number = validated_data.get('phone', None)
# guardian_data = Guardian.objects.filter(phone=phone_number)
# junior_data = Junior.objects.filter(phone=phone_number)
# if phone_number and (junior_data or guardian_data):
# raise serializers.ValidationError({"details":ERROR_CODE['2012']})
user = User.objects.filter(username=self.context['user']).last() user = User.objects.filter(username=self.context['user']).last()
if user: if user:
"""Save first and last name of junior""" """Save first and last name of junior"""
if self.context.get('first_name') != '' and self.context.get('last_name') != '': if self.context.get('first_name') != '' and self.context.get('first_name') is not None:
user.first_name = self.context.get('first_name', user.first_name) user.first_name = self.context.get('first_name')
user.last_name = self.context.get('last_name', user.last_name) if self.context.get('last_name') != '' and self.context.get('last_name') is not None:
user.save() user.last_name = self.context.get('last_name')
user.save()
"""Create junior data""" """Create junior data"""
junior, created = Junior.objects.get_or_create(auth=self.context['user']) junior = Junior.objects.filter(auth=self.context['user']).last()
if created:
"""Create referral code and junior code"""
junior.referral_code = ''.join([str(random.randrange(9)) for _ in range(4)])
junior.junior_code = ''.join([str(random.randrange(9)) for _ in range(4)])
if junior: if junior:
"""update details according to the data get from request""" """update details according to the data get from request"""
junior.gender = validated_data.get('gender',junior.gender) junior.gender = validated_data.get('gender',junior.gender)
"""Update guardian code""" """Update guardian code"""
junior.guardian_code = validated_data.get('guardian_code', junior.guardian_code) junior.guardian_code = validated_data.get('guardian_code', junior.guardian_code)
"""condition for guardian code"""
if guardian_code:
junior.guardian_code = guardian_code
junior.dob = validated_data.get('dob',junior.dob) junior.dob = validated_data.get('dob',junior.dob)
junior.passcode = validated_data.get('passcode', junior.passcode) junior.passcode = validated_data.get('passcode', junior.passcode)
junior.country_name = validated_data.get('country_name', junior.country_name) junior.country_name = validated_data.get('country_name', junior.country_name)
"""Update country code and phone number""" """Update country code and phone number"""
junior.phone = validated_data.get('phone', junior.phone) junior.phone = validated_data.get('phone', junior.phone)
junior.country_code = validated_data.get('country_code', junior.country_code) junior.country_code = validated_data.get('country_code', junior.country_code)
junior.referral_code_used = validated_data.get('referral_code_used', junior.referral_code_used) junior.image = validated_data.get('image', junior.image)
"""Complete profile of the junior if below all data are filled""" """Complete profile of the junior if below all data are filled"""
complete_profile_field = all([junior.phone, junior.gender, junior.country_name, complete_profile_field = all([junior.gender, junior.country_name, junior.image,
junior.dob, junior.country_code, user.first_name, user.last_name]) junior.dob, user.first_name])
junior.is_complete_profile = False junior.is_complete_profile = False
if complete_profile_field: if complete_profile_field:
junior.is_complete_profile = True junior.is_complete_profile = True
if image: referral_code_used = validated_data.get('referral_code_used')
filename = f"images/{image.name}" update_referral_points(junior.referral_code, referral_code_used)
image_url = upload_image_to_alibaba(image, filename) junior.referral_code_used = validated_data.get('referral_code_used', junior.referral_code_used)
junior.image = image_url
junior.save() junior.save()
return junior return junior
@@ -107,3 +113,344 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
with transaction.atomic(): with transaction.atomic():
instance = super().save(**kwargs) instance = super().save(**kwargs)
return instance return instance
class JuniorDetailSerializer(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.auth.username
def get_first_name(self, obj):
"""user first name"""
return obj.auth.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.auth.last_name
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at',
'image', 'updated_at']
class JuniorDetailListSerializer(serializers.ModelSerializer):
"""junior serializer"""
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
assigned_task = serializers.SerializerMethodField('get_assigned_task')
points = serializers.SerializerMethodField('get_points')
in_progress_task = serializers.SerializerMethodField('get_in_progress_task')
completed_task = serializers.SerializerMethodField('get_completed_task')
requested_task = serializers.SerializerMethodField('get_requested_task')
rejected_task = serializers.SerializerMethodField('get_rejected_task')
pending_task = serializers.SerializerMethodField('get_pending_task')
position = serializers.SerializerMethodField('get_position')
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
def get_assigned_task(self, obj):
data = JuniorTask.objects.filter(junior=obj).count()
return data
def get_position(self, obj):
data = JuniorPoints.objects.filter(junior=obj).last()
if data:
return data.position
return 99999
def get_points(self, obj):
data = sum(JuniorTask.objects.filter(junior=obj, task_status=COMPLETED).values_list('points', flat=True))
return data
def get_in_progress_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=IN_PROGRESS).count()
return data
def get_completed_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=COMPLETED).count()
return data
def get_requested_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=REQUESTED).count()
return data
def get_rejected_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=REJECTED).count()
return data
def get_pending_task(self, obj):
data = JuniorTask.objects.filter(junior=obj, task_status=PENDING).count()
return data
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task',
'requested_task', 'rejected_task', 'position', 'is_invited']
class JuniorProfileSerializer(serializers.ModelSerializer):
"""junior serializer"""
email = serializers.SerializerMethodField('get_auth')
first_name = serializers.SerializerMethodField('get_first_name')
last_name = serializers.SerializerMethodField('get_last_name')
notification_count = serializers.SerializerMethodField('get_notification_count')
total_count = serializers.SerializerMethodField('get_total_count')
complete_field_count = serializers.SerializerMethodField('get_complete_field_count')
def get_auth(self, obj):
"""user email address"""
return obj.auth.username
def get_first_name(self, obj):
"""user first name"""
return obj.auth.first_name
def get_last_name(self, obj):
"""user last name"""
return obj.auth.last_name
def get_notification_count(self, obj):
"""total notification count"""
return 0
def get_total_count(self, obj):
"""total fields count"""
return NUMBER['five']
def get_complete_field_count(self, obj):
"""total filled fields count"""
field_list = [obj.auth.first_name, obj.country_name,
obj.gender, obj.dob, obj.image]
complete_field = [data for data in field_list if data is not None and data != '']
return len(complete_field)
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob',
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method',
'is_invited', 'passcode']
class AddJuniorSerializer(serializers.ModelSerializer):
"""Add junior serializer"""
class Meta(object):
"""Meta info"""
model = Junior
fields = ['id', 'gender','dob', 'is_invited']
def create(self, validated_data):
""" create junior"""
with transaction.atomic():
email = self.context['email']
guardian = self.context['user']
relationship = self.context['relationship']
full_name = self.context['first_name'] + ' ' + self.context['last_name']
guardian_data = Guardian.objects.filter(user__username=guardian).last()
user_data = User.objects.create(username=email, email=email,
first_name=self.context['first_name'],
last_name=self.context['last_name'])
password = User.objects.make_random_password()
user_data.set_password(password)
user_data.save()
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
dob=validated_data.get('dob'), is_invited=True,
guardian_code=[guardian_data.guardian_code],
junior_code=generate_code(JUN, user_data.id),
referral_code=generate_code(ZOD, user_data.id),
referral_code_used=guardian_data.referral_code,
is_password_set=False, is_verified=True)
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship)
"""Generate otp"""
otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1)
UserEmailOtp.objects.create(email=email, otp=otp_value,
user_type='1', expired_at=expiry_time, is_verified=True)
# add push notification
UserNotification.objects.get_or_create(user=user_data)
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail(guardian, full_name)
# push notification
send_notification.delay(SKIPPED_PROFILE_SETUP, None, junior_data.auth.id, {})
return junior_data
class RemoveJuniorSerializer(serializers.ModelSerializer):
"""User Update Serializer"""
class Meta(object):
"""Meta class"""
model = Junior
fields = ('id', 'is_invited')
def update(self, instance, validated_data):
if instance:
instance.is_invited = False
instance.guardian_code = '{}'
instance.save()
return instance
class CompleteTaskSerializer(serializers.ModelSerializer):
"""User task Serializer"""
class Meta(object):
"""Meta class"""
model = JuniorTask
fields = ('id', 'image')
def update(self, instance, validated_data):
instance.image = validated_data.get('image', instance.image)
instance.requested_on = real_time()
instance.task_status = str(NUMBER['four'])
instance.is_approved = False
instance.save()
send_notification.delay(TASK_SUBMITTED, None, instance.junior.auth.id, {})
send_notification.delay(TASK_ACTION, None, instance.guardian.user.id, {})
return instance
class JuniorPointsSerializer(serializers.ModelSerializer):
"""Junior points serializer"""
junior_id = serializers.SerializerMethodField('get_junior_id')
total_points = serializers.SerializerMethodField('get_points')
in_progress_task = serializers.SerializerMethodField('get_in_progress_task')
completed_task = serializers.SerializerMethodField('get_completed_task')
requested_task = serializers.SerializerMethodField('get_requested_task')
rejected_task = serializers.SerializerMethodField('get_rejected_task')
pending_task = serializers.SerializerMethodField('get_pending_task')
position = serializers.SerializerMethodField('get_position')
def get_junior_id(self, obj):
"""junior id"""
return obj.junior.id
def get_position(self, obj):
data = JuniorPoints.objects.filter(junior=obj.junior).last()
if data:
return data.position
return 99999
def get_points(self, obj):
"""total points"""
points = JuniorPoints.objects.filter(junior=obj.junior).last()
if points:
return points.total_points
def get_in_progress_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=IN_PROGRESS).count()
def get_completed_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=COMPLETED).count()
def get_requested_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=REQUESTED).count()
def get_rejected_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=REJECTED).count()
def get_pending_task(self, obj):
return JuniorTask.objects.filter(junior=obj.junior, task_status=PENDING).count()
class Meta(object):
"""Meta info"""
model = Junior
fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task',
'requested_task', 'rejected_task']
class AddGuardianSerializer(serializers.ModelSerializer):
"""Add guardian serializer"""
class Meta(object):
"""Meta info"""
model = Guardian
fields = ['id']
def create(self, validated_data):
""" invite and create guardian"""
with transaction.atomic():
email = self.context['email']
junior = self.context['user']
relationship = self.context['relationship']
full_name = self.context['first_name'] + ' ' + self.context['last_name']
junior_data = Junior.objects.filter(auth__username=junior).last()
instance = User.objects.filter(username=email).last()
if instance:
guardian_data = Guardian.objects.filter(user=instance).update(is_invited=True,
referral_code=generate_code(ZOD,
instance.id),
referral_code_used=junior_data.referral_code,
is_verified=True)
UserNotification.objects.get_or_create(user=instance)
return guardian_data
else:
user = User.objects.create(username=email, email=email,
first_name=self.context['first_name'],
last_name=self.context['last_name'])
password = User.objects.make_random_password()
user.set_password(password)
user.save()
guardian_data = Guardian.objects.create(user=user, is_invited=True,
referral_code=generate_code(ZOD, user.id),
referral_code_used=junior_data.referral_code,
is_password_set=False, is_verified=True)
"""Generate otp"""
otp_value = generate_otp()
expiry_time = timezone.now() + timezone.timedelta(days=1)
UserEmailOtp.objects.create(email=email, otp=otp_value,
user_type=str(NUMBER['two']), expired_at=expiry_time,
is_verified=True)
UserNotification.objects.get_or_create(user=user)
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
relationship=relationship)
"""Notification email"""
junior_notification_email(email, full_name, email, password)
junior_approval_mail(email, full_name)
send_notification.delay(INVITED_GUARDIAN, None, junior_data.auth.id, {})
send_notification.delay(APPROVED_JUNIOR, None, guardian_data.user.id, {})
return guardian_data
class StartTaskSerializer(serializers.ModelSerializer):
"""User task Serializer"""
task_duration = serializers.SerializerMethodField('get_task_duration')
def get_task_duration(self, obj):
""" remaining time to complete task"""
due_date = datetime.combine(obj.due_date, datetime.max.time())
# fetch real time
real_datetime = real_time()
# Perform the subtraction
if due_date > real_datetime:
time_difference = due_date - real_datetime
time_only = convert_timedelta_into_datetime(time_difference)
return str(time_difference.days) + ' days ' + str(time_only)
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
class Meta(object):
"""Meta class"""
model = JuniorTask
fields = ('id', 'task_duration')
def update(self, instance, validated_data):
instance.task_status = str(NUMBER['two'])
instance.save()
return instance
+36 -5
View File
@@ -1,19 +1,50 @@
""" Urls files""" """ Urls files"""
"""Django import""" """Django import"""
from django.urls import path, include from django.urls import path, include
from .views import UpdateJuniorProfile, ValidateGuardianCode from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView,
InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView,
CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode,
InviteGuardianAPIView, StartTaskAPIView)
"""Third party import""" """Third party import"""
from rest_framework import routers from rest_framework import routers
"""Router""" """Router"""
router = routers.SimpleRouter() 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
# Create junior profile API, validate junior profile,
# junior list,
# add junior list, invited junior,
# filter-junior,
# remove junior,
# junior task list
"""API End points with router""" """API End points with router"""
"""Create junior profile API"""
router.register('create-junior-profile', UpdateJuniorProfile, basename='profile-update') router.register('create-junior-profile', UpdateJuniorProfile, basename='profile-update')
"""validate guardian code API""" # validate guardian code API"""
router.register('validate-guardian-code', ValidateGuardianCode, basename='validate-guardian-code') router.register('validate-guardian-code', ValidateGuardianCode, basename='validate-guardian-code')
"""Define url pattern""" # junior list API"""
router.register('junior-list', JuniorListAPIView, basename='junior-list')
# Add junior list API"""
router.register('add-junior', AddJuniorAPIView, basename='add-junior')
# Invited junior list API"""
router.register('invited-junior', InvitedJuniorAPIView, basename='invited-junior')
# Filter junior list API"""
router.register('filter-junior', FilterJuniorAPIView, basename='filter-junior')
# junior's task list API"""
router.register('junior-task-list', JuniorTaskListAPIView, basename='junior-task-list')
# junior's task list API"""
router.register('junior-points', JuniorPointsListAPIView, basename='junior-points')
# validate referral code API"""
router.register('validate-referral-code', ValidateReferralCode, basename='validate-referral-code')
# invite guardian API"""
router.register('invite-guardian', InviteGuardianAPIView, basename='invite-guardian')
# Define url pattern"""
urlpatterns = [ urlpatterns = [
path('api/v1/', include(router.urls)), path('api/v1/', include(router.urls)),
path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view()),
path('api/v1/complete-task/', CompleteJuniorTaskAPIView.as_view()),
path('api/v1/start-task/', StartTaskAPIView.as_view())
] ]
+60
View File
@@ -0,0 +1,60 @@
"""Account utils"""
"""Import django"""
from django.conf import settings
"""Third party Django app"""
from templated_email import send_templated_mail
from .models import JuniorPoints
from django.db.models import F
# junior notification
# email for sending email
# when guardian create junior profile
# guardian get email when junior send
# request for approving the profile and
# being part of the zod bank and access the platform
# define junior notification email
# junior approval email
def junior_notification_email(recipient_email, full_name, email, password):
"""Notification email"""
from_email = settings.EMAIL_FROM_ADDRESS
# recipient email"""
recipient_list = [recipient_email]
# use send template mail for sending email"""
send_templated_mail(
template_name='junior_notification_email.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'full_name': full_name,
'url':'link',
'email': email,
'password': password
}
)
return full_name
def junior_approval_mail(guardian, full_name):
"""junior approval mail"""
from_email = settings.EMAIL_FROM_ADDRESS
recipient_list = [guardian]
# use send template mail for sending email"""
send_templated_mail(
template_name='junior_approval_mail.email',
from_email=from_email,
recipient_list=recipient_list,
context={
'full_name': full_name
}
)
return full_name
def update_positions_based_on_points():
"""Update position of the junior"""
# First, retrieve all the JuniorPoints instances ordered by total_points in descending order.
juniors_points = JuniorPoints.objects.order_by('-total_points')
# Now, iterate through the queryset and update the position field based on the order.
position = 1
for junior_point in juniors_points:
junior_point.position = position
junior_point.save()
position += 1
+395 -18
View File
@@ -1,40 +1,417 @@
"""Junior view file""" """Junior view file"""
from rest_framework import viewsets, status import os
from rest_framework import viewsets, status, generics,views
from rest_framework.permissions import IsAuthenticated from rest_framework.permissions import IsAuthenticated
from rest_framework.pagination import PageNumberPagination
from django.contrib.auth.models import User
import datetime
import requests
"""Django app import""" """Django app import"""
from junior.models import Junior
from .serializers import CreateJuniorSerializer # Import guardian's model,
from guardian.models import Guardian # 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 junior serializer
# Import update_positions_based_on_points
# Import upload_image_to_alibaba
# Import custom_response, custom_error_response
# Import constants
from junior.models import Junior, JuniorPoints
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\
RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer,
AddGuardianSerializer, StartTaskSerializer)
from guardian.models import Guardian, JuniorTask
from guardian.serializers import TaskDetailsSerializer
from base.messages import ERROR_CODE, SUCCESS_CODE from base.messages import ERROR_CODE, SUCCESS_CODE
from base.constants import NUMBER
from account.utils import custom_response, custom_error_response from account.utils import custom_response, custom_error_response
from guardian.utils import upload_image_to_alibaba
from .utils import update_positions_based_on_points
from notifications.utils import send_notification
from notifications.constants import REMOVE_JUNIOR
""" Define APIs """
# Define validate guardian code API,
# update junior profile,
# list of all assosicated junior
# Add junior API
# invite junior API
# search junior API
# remove junior API,
# approve junior API
# create referral code
# validation API
# invite guardian API
# by junior
# Start task
# by junior API
# Create your views here. # Create your views here.
class UpdateJuniorProfile(viewsets.ViewSet): class UpdateJuniorProfile(viewsets.ViewSet):
"""Update junior profile""" """Update junior profile"""
queryset = Junior.objects.all()
serializer_class = CreateJuniorSerializer serializer_class = CreateJuniorSerializer
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
"""Use CreateJuniorSerializer""" """Use CreateJuniorSerializer"""
serializer = CreateJuniorSerializer(context={"user":request.user, try:
"first_name":request.data.get('first_name', ''), request_data = request.data
"last_name": request.data.get('last_name',' ')}, image = request.data.get('image')
data=request.data) image_url = ''
if serializer.is_valid(): if image:
"""save serializer""" # check image size
serializer.save() if image.size == NUMBER['zero']:
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK) return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST) # convert into file
filename = f"images/{image.name}"
# upload image on ali baba
image_url = upload_image_to_alibaba(image, filename)
request_data = {"image": image_url}
serializer = CreateJuniorSerializer(context={"user":request.user, "image":image_url,
"first_name": request.data.get('first_name'),
"last_name": request.data.get('last_name')
},
data=request_data)
if serializer.is_valid():
"""save serializer"""
serializer.save()
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ValidateGuardianCode(viewsets.ViewSet): class ValidateGuardianCode(viewsets.ViewSet):
"""Check guardian code exist or not""" """Check guardian code exist or not"""
queryset = Guardian.objects.all()
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
def list(self, request, *args, **kwargs): def list(self, request, *args, **kwargs):
"""check guardian code""" """check guardian code"""
guardian_code = self.request.GET.get('guardian_code').split(',') try:
for code in guardian_code: guardian_code = self.request.GET.get('guardian_code').split(',')
guardian_data = Guardian.objects.filter(guardian_code=code).exists() for code in guardian_code:
# fetch guardian object
guardian_data = Guardian.objects.filter(guardian_code=code).exists()
if guardian_data:
# successfully check guardian code
return custom_response(SUCCESS_CODE['3013'], response_status=status.HTTP_200_OK)
else:
return custom_error_response(ERROR_CODE["2022"], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class JuniorListAPIView(viewsets.ModelViewSet):
"""Junior list of assosicated guardian"""
serializer_class = JuniorDetailListSerializer
queryset = Junior.objects.all()
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def list(self, request, *args, **kwargs):
""" junior list"""
try:
update_positions_based_on_points()
guardian_data = Guardian.objects.filter(user__email=request.user).last()
# fetch junior object
if guardian_data: if guardian_data:
return custom_response(SUCCESS_CODE['3013'], response_status=status.HTTP_200_OK) queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code))
# use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(ERROR_CODE['2045'], response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class AddJuniorAPIView(viewsets.ModelViewSet):
"""Add Junior by guardian"""
queryset = Junior.objects.all()
serializer_class = AddJuniorSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
""" junior list"""
try:
info_data = {'user': request.user, 'relationship': str(request.data['relationship']), 'email': request.data['email'], 'first_name': request.data['first_name'],
'last_name': request.data['last_name']}
if User.objects.filter(username=request.data['email']):
return custom_error_response(ERROR_CODE['2059'], response_status=status.HTTP_400_BAD_REQUEST)
# use AddJuniorSerializer serializer
serializer = AddJuniorSerializer(data=request.data, context=info_data)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class InvitedJuniorAPIView(viewsets.ModelViewSet):
"""Junior list of assosicated guardian"""
serializer_class = JuniorDetailListSerializer
queryset = Junior.objects.all()
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
http_method_names = ('get',)
def get_queryset(self):
"""Get the queryset for the view"""
guardian = Guardian.objects.filter(user__email=self.request.user).last()
junior_queryset = Junior.objects.filter(guardian_code__icontains=str(guardian.guardian_code),
is_invited=True)
return junior_queryset
def list(self, request, *args, **kwargs):
""" junior list"""
try:
queryset = self.get_queryset()
paginator = self.pagination_class()
# pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class FilterJuniorAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = JuniorDetailListSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
queryset = Junior.objects.all()
http_method_names = ('get',)
def get_queryset(self):
"""Get the queryset for the view"""
title = self.request.GET.get('title')
guardian_data = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code),
is_invited=True, auth__first_name=title)
return queryset
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
try:
queryset = self.get_queryset()
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use JuniorDetailListSerializer serializer
serializer = JuniorDetailListSerializer(paginated_queryset, many=True)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class RemoveJuniorAPIView(views.APIView):
"""Remove junior API"""
serializer_class = RemoveJuniorSerializer
model = Junior
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
junior_id = self.request.GET.get('id')
guardian = Guardian.objects.filter(user__email=self.request.user).last()
# fetch junior query
junior_queryset = Junior.objects.filter(id=junior_id, guardian_code__icontains=str(guardian.guardian_code),
is_invited=True).last()
if junior_queryset:
# use RemoveJuniorSerializer serializer
serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
send_notification.delay(REMOVE_JUNIOR, None, junior_queryset.auth.id, {})
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else: else:
return custom_error_response(ERROR_CODE["2022"], response_status=status.HTTP_400_BAD_REQUEST) return custom_error_response(ERROR_CODE['2034'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class JuniorTaskListAPIView(viewsets.ModelViewSet):
"""Update guardian profile"""
serializer_class = TaskDetailsSerializer
permission_classes = [IsAuthenticated]
pagination_class = PageNumberPagination
queryset = JuniorTask.objects.all()
http_method_names = ('get',)
def list(self, request, *args, **kwargs):
"""Create guardian profile"""
try:
status_value = self.request.GET.get('status')
search = self.request.GET.get('search')
if search and str(status_value) == '0':
# search with title and for all task list
queryset = JuniorTask.objects.filter(junior__auth=request.user,
task_name__icontains=search).order_by('due_date', 'created_at')
elif search and str(status_value) != '0':
# search with title and fetch task list with status wise
queryset = JuniorTask.objects.filter(junior__auth=request.user, task_name__icontains=search,
task_status=status_value).order_by('due_date', 'created_at')
if search is None and str(status_value) == '0':
# fetch all task list
queryset = JuniorTask.objects.filter(junior__auth=request.user).order_by('due_date', 'created_at')
elif search is None and str(status_value) != '0':
# fetch task list with status wise
queryset = JuniorTask.objects.filter(junior__auth=request.user,
task_status=status_value).order_by('due_date','created_at')
paginator = self.pagination_class()
# use Pagination
paginated_queryset = paginator.paginate_queryset(queryset, request)
# use TaskDetailsSerializer serializer
serializer = 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 CompleteJuniorTaskAPIView(views.APIView):
"""Update junior task API"""
serializer_class = CompleteTaskSerializer
model = JuniorTask
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
task_id = self.request.data.get('task_id')
image = request.data['image']
if image and image.size == NUMBER['zero']:
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
# create file
filename = f"images/{image.name}"
# upload image
filename = f"images/{image.name}"
image_url = upload_image_to_alibaba(image, filename)
# fetch junior query
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last()
if task_queryset:
# use CompleteTaskSerializer serializer
if task_queryset.task_status in [str(NUMBER['four']), str(NUMBER['five'])]:
"""Already request send """
return custom_error_response(ERROR_CODE['2049'], response_status=status.HTTP_400_BAD_REQUEST)
serializer = CompleteTaskSerializer(task_queryset, data={'image': image_url}, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3032'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
return custom_error_response(ERROR_CODE['2044'], 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 JuniorPointsListAPIView(viewsets.ModelViewSet):
"""Junior Points viewset"""
serializer_class = JuniorPointsSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
"""get queryset"""
return JuniorTask.objects.filter(junior__auth__email=self.request.user).last()
def list(self, request, *args, **kwargs):
"""profile view"""
try:
queryset = self.get_queryset()
# update position of junior
update_positions_based_on_points()
serializer = JuniorPointsSerializer(queryset)
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class ValidateReferralCode(viewsets.ViewSet):
"""Check guardian code exist or not"""
permission_classes = [IsAuthenticated]
http_method_names = ('get',)
def get_queryset(self):
"""Get queryset based on referral_code."""
referral_code = self.request.GET.get('referral_code')
if referral_code:
# search referral code in guardian model
guardian_queryset = Guardian.objects.filter(referral_code=referral_code).last()
if guardian_queryset:
return guardian_queryset
else:
# search referral code in junior model
junior_queryset = Junior.objects.filter(referral_code=referral_code).last()
if junior_queryset:
return junior_queryset
return None
def list(self, request, *args, **kwargs):
"""check guardian code"""
try:
if self.get_queryset():
return custom_response(SUCCESS_CODE['3033'], response_status=status.HTTP_200_OK)
return custom_error_response(ERROR_CODE["2019"], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class InviteGuardianAPIView(viewsets.ModelViewSet):
"""Invite guardian by junior"""
serializer_class = AddGuardianSerializer
permission_classes = [IsAuthenticated]
http_method_names = ('post',)
def create(self, request, *args, **kwargs):
""" junior list"""
try:
if request.data['email'] == '':
return custom_error_response(ERROR_CODE['2062'], response_status=status.HTTP_400_BAD_REQUEST)
info = {'user': request.user, 'email': request.data['email'], 'first_name': request.data['first_name'],
'last_name': request.data['last_name'], 'relationship': str(request.data['relationship'])}
# use AddJuniorSerializer serializer
serializer = AddGuardianSerializer(data=request.data, context=info)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3034'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
class StartTaskAPIView(views.APIView):
"""Update junior task API"""
serializer_class = StartTaskSerializer
model = JuniorTask
permission_classes = [IsAuthenticated]
def put(self, request, format=None):
try:
task_id = self.request.data.get('task_id')
task_queryset = JuniorTask.objects.filter(id=task_id, junior__auth__email=self.request.user).last()
if task_queryset and task_queryset.task_status == str(NUMBER['one']):
# use StartTaskSerializer serializer
serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True)
if serializer.is_valid():
# save serializer
serializer.save()
return custom_response(SUCCESS_CODE['3035'], serializer.data, response_status=status.HTTP_200_OK)
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
else:
# task in another state
return custom_error_response(ERROR_CODE['2060'], response_status=status.HTTP_400_BAD_REQUEST)
except Exception as e:
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
+7
View File
@@ -1,9 +1,15 @@
#!/usr/bin/env python #!/usr/bin/env python
"""Django's command-line utility for administrative tasks.""" """Django's command-line utility for administrative tasks."""
"""Django import""" """Django import"""
# Import OS module
import os import os
# Import sys module"""
import sys import sys
# define all function
# execute command line
# Import execute from command line
# fetch django settings
def main(): def main():
"""Main function""" """Main function"""
@@ -18,6 +24,7 @@ def main():
"available on your PYTHONPATH environment variable? Did you " "available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?" "forget to activate a virtual environment?"
) from exc ) from exc
"""execute command line function"""
execute_from_command_line(sys.argv) execute_from_command_line(sys.argv)
View File
+12
View File
@@ -0,0 +1,12 @@
"""
notification admin file
"""
from django.contrib import admin
from notifications.models import Notification
@admin.register(Notification)
class NotificationAdmin(admin.ModelAdmin):
"""Notification Admin"""
list_display = ['id', 'notification_type', 'notification_to', 'data', 'is_read']
+13
View File
@@ -0,0 +1,13 @@
"""
notification app file
"""
# django imports
from django.apps import AppConfig
class NotificationsConfig(AppConfig):
"""
notification app config
"""
default_auto_field = 'django.db.models.BigAutoField'
name = 'notifications'
+72
View File
@@ -0,0 +1,72 @@
"""
notification constants file
"""
from base.constants import NUMBER
REGISTRATION = NUMBER['one']
TASK_CREATED = NUMBER['two']
INVITED_GUARDIAN = NUMBER['three']
APPROVED_JUNIOR = NUMBER['four']
REFERRAL_POINTS = NUMBER['five']
TASK_POINTS = NUMBER['six']
TASK_REJECTED = NUMBER['seven']
SKIPPED_PROFILE_SETUP = NUMBER['eight']
TASK_SUBMITTED = NUMBER['nine']
TASK_ACTION = NUMBER['ten']
LEADERBOARD_RANKING = NUMBER['eleven']
REMOVE_JUNIOR = NUMBER['twelve']
TEST_NOTIFICATION = 99
NOTIFICATION_DICT = {
REGISTRATION: {
"title": "Successfully registered!",
"body": "You have registered successfully. Now login and complete your profile."
},
TASK_CREATED: {
"title": "Task created!",
"body": "Task created successfully."
},
INVITED_GUARDIAN: {
"title": "Invite guardian",
"body": "Invite guardian successfully"
},
APPROVED_JUNIOR: {
"title": "Approve junior",
"body": "You have request from junior to associate with you"
},
REFERRAL_POINTS: {
"title": "Earn Referral points",
"body": "You earn 5 points for referral."
},
TASK_POINTS: {
"title": "Earn Task points!",
"body": "You earn 5 points for task."
},
TASK_REJECTED: {
"title": "Task rejected!",
"body": "Your task has been rejected."
},
SKIPPED_PROFILE_SETUP: {
"title": "Skipped profile setup!",
"body": "Your guardian has been setup your profile."
},
TASK_SUBMITTED: {
"title": "Task submitted!",
"body": "Your task has been submitted successfully."
},
TASK_ACTION: {
"title": "Task approval!",
"body": "You have request for task approval."
},
LEADERBOARD_RANKING: {
"title": "Leader board rank!",
"body": "Your rank is ."
},
REMOVE_JUNIOR: {
"title": "Disassociate by guardian!",
"body": "Your guardian disassociate you ."
},
TEST_NOTIFICATION: {
"title": "Test Notification",
"body": "This notification is for testing purpose"
}
}
+30
View File
@@ -0,0 +1,30 @@
# Generated by Django 4.2.2 on 2023-07-19 07:40
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import django.utils.timezone
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Notification',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('notification_type', models.CharField(blank=True, max_length=50, null=True)),
('data', models.JSONField(blank=True, default=dict, null=True)),
('is_read', models.BooleanField(default=False)),
('created_at', models.DateTimeField(default=django.utils.timezone.now)),
('notification_from', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='from_notification', to=settings.AUTH_USER_MODEL)),
('notification_to', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='to_notification', to=settings.AUTH_USER_MODEL)),
],
),
]
+24
View File
@@ -0,0 +1,24 @@
"""
notification models file
"""
# django imports
from django.db import models
from django.contrib.auth import get_user_model
from django.utils import timezone
USER = get_user_model()
class Notification(models.Model):
""" used to save the notifications """
notification_type = models.CharField(max_length=50, blank=True, null=True)
notification_to = models.ForeignKey(USER, related_name='to_notification', on_delete=models.CASCADE)
notification_from = models.ForeignKey(USER, related_name='from_notification', on_delete=models.SET_NULL,
blank=True, null=True)
data = models.JSONField(default=dict, blank=True, null=True)
is_read = models.BooleanField(default=False)
created_at = models.DateTimeField(default=timezone.now)
def __str__(self):
""" string representation """
return f"{self.notification_to.id} | {self.notification_to.email}"
+28
View File
@@ -0,0 +1,28 @@
"""
notification serializer file
"""
# third party imports
from rest_framework import serializers
# local imports
from notifications.utils import register_fcm_token
class RegisterDevice(serializers.Serializer):
"""
used to create and validate register device token
"""
registration_id = serializers.CharField(max_length=250)
device_id = serializers.CharField(max_length=250)
type = serializers.ChoiceField(choices=["ios", "web", "android"])
class Meta:
""" meta class """
fields = ('registration_id', 'type', 'device_id')
def create(self, validated_data):
""" override this method to create device token for users """
registration_id = validated_data['registration_id']
device_type = validated_data['type']
return register_fcm_token(self.context['user_id'], registration_id,
validated_data['device_id'], device_type)
+6
View File
@@ -0,0 +1,6 @@
"""
notification test file
"""
from django.test import TestCase
# Create your tests here.
+18
View File
@@ -0,0 +1,18 @@
"""
notifications urls file
"""
# django imports
from django.urls import path, include
from rest_framework import routers
# local imports
from notifications.views import NotificationViewSet
# initiate router
router = routers.SimpleRouter()
router.register('notifications', NotificationViewSet, basename='notifications')
urlpatterns = [
path('api/v1/', include(router.urls)),
]
+69
View File
@@ -0,0 +1,69 @@
"""
notifications utils file
"""
# third party imports
from fcm_django.models import FCMDevice
from celery import shared_task
from firebase_admin.messaging import Message, Notification as FirebaseNotification
# django imports
from django.contrib.auth import get_user_model
from account.models import UserNotification
from notifications.constants import NOTIFICATION_DICT
from notifications.models import Notification
# local imports
User = get_user_model()
def register_fcm_token(user_id, registration_id, device_id, device_type):
""" used to register the fcm device token"""
device, _ = FCMDevice.objects.update_or_create(device_id=device_id,
defaults={'user_id': user_id, 'type': device_type,
'active': True,
'registration_id': registration_id})
return device
def remove_fcm_token(user_id: int, access_token: str, registration_id) -> None:
"""
remove access_token and registration_token
"""
try:
# remove fcm token for this device
FCMDevice.objects.filter(user_id=user_id).delete()
except Exception as e:
print(e)
def get_basic_detail(notification_type, from_user_id, to_user_id):
""" used to get the basic details """
notification_data = NOTIFICATION_DICT[notification_type]
from_user = User.objects.get(id=from_user_id) if from_user_id else None
to_user = User.objects.get(id=to_user_id)
return notification_data, from_user, to_user
@shared_task()
def send_notification(notification_type, from_user_id, to_user_id, extra_data):
""" used to send the push for the given notification type """
(notification_data, from_user, to_user) = get_basic_detail(notification_type, from_user_id, to_user_id)
user_notification_type = UserNotification.objects.filter(user=to_user).first()
# data = notification_data.data
data = notification_data
Notification.objects.create(notification_type=notification_type, notification_from=from_user,
notification_to=to_user, data=data)
if user_notification_type.push_notification:
data.update({'badge': Notification.objects.filter(notification_to=to_user, is_read=False).count()})
send_push(to_user, data)
def send_push(user, data):
""" used to send push notification to specific user """
notification_data = data.pop('data', None)
user.fcmdevice_set.filter(active=True).send_message(
Message(notification=FirebaseNotification(data['title'], data['body']), data=notification_data)
)
+42
View File
@@ -0,0 +1,42 @@
"""
notifications views file
"""
# django imports
from django.db.models import Q
from rest_framework import viewsets
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
# local imports
from account.utils import custom_response
from base.messages import SUCCESS_CODE
from notifications.constants import TEST_NOTIFICATION
from notifications.serializers import RegisterDevice
from notifications.utils import send_notification
class NotificationViewSet(viewsets.GenericViewSet):
""" used to do the notification actions """
serializer_class = None
permission_classes = [IsAuthenticated, ]
@action(methods=['post'], detail=False, url_path='device', url_name='device', serializer_class=RegisterDevice)
def fcm_registration(self, request):
"""
used to save the fcm token
"""
serializer = self.get_serializer_class()(data=request.data,
context={'user_id': request.auth.payload['user_id']})
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE["3000"])
@action(methods=['get'], detail=False, url_path='test', url_name='test', serializer_class=None)
def send_test_notification(self, request):
"""
to send test notification
:return:
"""
send_notification.delay(TEST_NOTIFICATION, None, request.auth.payload['user_id'], {})
return custom_response(SUCCESS_CODE["3000"])
+25
View File
@@ -8,6 +8,7 @@ async-timeout==4.0.2
billiard==4.1.0 billiard==4.1.0
boto3==1.26.157 boto3==1.26.157
botocore==1.29.157 botocore==1.29.157
CacheControl==0.13.1
cachetools==5.3.1 cachetools==5.3.1
celery==5.3.1 celery==5.3.1
certifi==2023.5.7 certifi==2023.5.7
@@ -19,6 +20,8 @@ click==8.1.3
click-didyoumean==0.3.0 click-didyoumean==0.3.0
click-plugins==1.1.1 click-plugins==1.1.1
click-repl==0.3.0 click-repl==0.3.0
coreapi==2.3.3
coreschema==0.0.4
crcmod==1.7 crcmod==1.7
cron-descriptor==1.4.0 cron-descriptor==1.4.0
cryptography==41.0.1 cryptography==41.0.1
@@ -39,24 +42,46 @@ django-timezone-field==5.1
djangorestframework==3.14.0 djangorestframework==3.14.0
djangorestframework-simplejwt==5.2.2 djangorestframework-simplejwt==5.2.2
drf-yasg==1.21.6 drf-yasg==1.21.6
fcm-django==2.0.0
firebase-admin==6.2.0
google-api-core==2.11.1
google-api-python-client==2.93.0
google-auth==2.21.0 google-auth==2.21.0
google-auth-httplib2==0.1.0
google-cloud-core==2.3.3
google-cloud-firestore==2.11.1
google-cloud-storage==2.10.0
google-crc32c==1.5.0
google-resumable-media==2.5.0
googleapis-common-protos==1.59.1
grpcio==1.56.0
grpcio-status==1.56.0
gunicorn==20.1.0 gunicorn==20.1.0
httplib2==0.22.0
idna==3.4 idna==3.4
inflection==0.5.1 inflection==0.5.1
itypes==1.2.0
Jinja2==3.1.2
jmespath==0.10.0 jmespath==0.10.0
kombu==5.3.1 kombu==5.3.1
MarkupSafe==2.1.3
msgpack==1.0.5 msgpack==1.0.5
ntplib==0.4.0
numpy==1.25.1
oss2==2.18.0 oss2==2.18.0
packaging==23.1 packaging==23.1
phonenumbers==8.13.15 phonenumbers==8.13.15
Pillow==9.5.0 Pillow==9.5.0
prompt-toolkit==3.0.38 prompt-toolkit==3.0.38
proto-plus==1.22.3
protobuf==4.23.4
psycopg==3.1.9 psycopg==3.1.9
pyasn1==0.5.0 pyasn1==0.5.0
pyasn1-modules==0.3.0 pyasn1-modules==0.3.0
pycparser==2.21 pycparser==2.21
pycryptodome==3.18.0 pycryptodome==3.18.0
PyJWT==2.7.0 PyJWT==2.7.0
pyparsing==3.1.0
python-crontab==2.7.1 python-crontab==2.7.1
python-dateutil==2.8.2 python-dateutil==2.8.2
python-dotenv==1.0.0 python-dotenv==1.0.0
View File
+38
View File
@@ -0,0 +1,38 @@
"""
web_admin admin file
"""
# django imports
from django.contrib import admin
# local imports
from web_admin.models import Article, ArticleCard, ArticleSurvey, SurveyOption, DefaultArticleCardImage
@admin.register(Article)
class ArticleAdmin(admin.ModelAdmin):
"""Article Admin"""
list_display = ['id', 'title', 'description', 'is_published', 'is_deleted']
@admin.register(ArticleCard)
class ArticleCardAdmin(admin.ModelAdmin):
"""Article Card Admin"""
list_display = ['id', 'article', 'title', 'description', 'image_url']
@admin.register(ArticleSurvey)
class ArticleSurveyAdmin(admin.ModelAdmin):
"""Article Survey Admin"""
list_display = ['id', 'article', 'question', 'points']
@admin.register(SurveyOption)
class SurveyOptionAdmin(admin.ModelAdmin):
"""Survey Option Admin"""
list_display = ['id', 'survey', 'option', 'is_answer']
@admin.register(DefaultArticleCardImage)
class DefaultArticleCardImagesAdmin(admin.ModelAdmin):
"""Default Article Card Images Option Admin"""
list_display = ['image_name', 'image_url']
+13
View File
@@ -0,0 +1,13 @@
"""
web_admin app file
"""
# django imports
from django.apps import AppConfig
class WebAdminConfig(AppConfig):
"""
web admin app config
"""
default_auto_field = 'django.db.models.BigAutoField'
name = 'web_admin'
+61
View File
@@ -0,0 +1,61 @@
# Generated by Django 4.2.2 on 2023-07-14 13:15
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Article',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('description', models.TextField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('is_published', models.BooleanField(default=True)),
('is_deleted', models.BooleanField(default=False)),
],
),
migrations.CreateModel(
name='ArticleSurvey',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('question', models.CharField(max_length=255)),
('points', models.IntegerField()),
('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='article_survey', to='web_admin.article')),
],
),
migrations.CreateModel(
name='SurveyOption',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('option', models.CharField(max_length=255)),
('is_answer', models.BooleanField(default=False)),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
('survey', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='survey_options', to='web_admin.articlesurvey')),
],
),
migrations.CreateModel(
name='ArticleCard',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=255)),
('description', models.TextField()),
('image', models.ImageField(upload_to='card_images/')),
('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='article_cards', to='web_admin.article')),
],
),
]
@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2023-07-20 11:51
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='articlecard',
name='image',
field=models.URLField(blank=True, default=None, null=True),
),
]
@@ -0,0 +1,28 @@
# Generated by Django 4.2.2 on 2023-07-24 14:15
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('web_admin', '0002_alter_articlecard_image'),
]
operations = [
migrations.CreateModel(
name='DefaultArticleCardImage',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('image_name', models.CharField(max_length=20)),
('image_url', models.URLField()),
('created_at', models.DateTimeField(auto_now_add=True)),
('updated_at', models.DateTimeField(auto_now=True)),
],
),
migrations.RenameField(
model_name='articlecard',
old_name='image',
new_name='image_url',
),
]
View File
+81
View File
@@ -0,0 +1,81 @@
"""
web_admin model file
"""
# django imports
from django.db import models
class Article(models.Model):
"""
Article model
"""
title = models.CharField(max_length=255)
description = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
is_published = models.BooleanField(default=True)
is_deleted = models.BooleanField(default=False)
def __str__(self):
"""Return title"""
return f'{self.id} | {self.title}'
class ArticleCard(models.Model):
"""
Article Card model
"""
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='article_cards')
title = models.CharField(max_length=255)
description = models.TextField()
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)
def __str__(self):
"""Return title"""
return f'{self.id} | {self.title}'
class ArticleSurvey(models.Model):
"""
Article Survey model
"""
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='article_survey')
question = models.CharField(max_length=255)
points = models.IntegerField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
"""Return title"""
return f'{self.id} | {self.article}'
class SurveyOption(models.Model):
"""
Survey Options model
"""
survey = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='survey_options')
option = models.CharField(max_length=255)
is_answer = models.BooleanField(default=False)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
"""Return title"""
return f'{self.id} | {self.survey}'
class DefaultArticleCardImage(models.Model):
"""
Default images upload in oss bucket
"""
image_name = models.CharField(max_length=20)
image_url = models.URLField()
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
def __str__(self):
"""return image_name as an object"""
return self.image_name
+26
View File
@@ -0,0 +1,26 @@
"""
web_admin permission classes
"""
# django imports
from rest_framework import permissions
class AdminPermission(permissions.BasePermission):
"""
to check for usertype admin only
"""
def has_permission(self, request, view):
"""
Return True if user_type is admin
"""
if request.user.is_superuser:
return True
return False
def has_object_permission(self, request, view, obj):
"""
check for object level permission
"""
if request.user.is_superuser:
return True
return False
View File
+278
View File
@@ -0,0 +1,278 @@
"""
web_admin serializers file
"""
# django imports
from rest_framework import serializers
from django.contrib.auth import get_user_model
from base.constants import (ARTICLE_SURVEY_POINTS, MAX_ARTICLE_CARD, MIN_ARTICLE_SURVEY, MAX_ARTICLE_SURVEY, NUMBER,
USER_TYPE, ARTICLE_CARD_IMAGE_FOLDER)
# local imports
from base.messages import ERROR_CODE
from guardian.utils import upload_image_to_alibaba
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage
from web_admin.utils import pop_id
USER = get_user_model()
class ArticleCardSerializer(serializers.ModelSerializer):
"""
Article Card serializer
"""
id = serializers.IntegerField(required=False)
image = serializers.FileField(required=False)
image_url = serializers.URLField(required=False)
class Meta:
"""
meta class
"""
model = ArticleCard
fields = ('id', 'title', 'description', 'image', 'image_url')
def create(self, validated_data):
if 'image' in validated_data and validated_data['image'] is not None:
image = validated_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
validated_data['image_url'] = upload_image_to_alibaba(image, filename)
article_card = ArticleCard.objects.create(article_id='1', **validated_data)
return article_card
class SurveyOptionSerializer(serializers.ModelSerializer):
"""
survey option serializer
"""
id = serializers.IntegerField(required=False)
class Meta:
"""
meta class
"""
model = SurveyOption
fields = ('id', 'option', 'is_answer')
class ArticleSurveySerializer(serializers.ModelSerializer):
"""
article survey serializer
"""
id = serializers.IntegerField(required=False)
survey_options = SurveyOptionSerializer(many=True)
class Meta:
"""
meta class
"""
model = ArticleSurvey
fields = ('id', 'question', 'survey_options')
class ArticleSerializer(serializers.ModelSerializer):
"""
serializer for article API
"""
article_cards = ArticleCardSerializer(many=True)
article_survey = ArticleSurveySerializer(many=True)
class Meta:
"""
meta class
"""
model = Article
fields = ('id', 'title', 'description', 'article_cards', 'article_survey')
def validate(self, attrs):
"""
to validate request data
:return: validated attrs
"""
article_cards = attrs.get('article_cards', None)
article_survey = attrs.get('article_survey', None)
if article_cards is None or len(article_cards) > int(MAX_ARTICLE_CARD):
raise serializers.ValidationError({'details': ERROR_CODE['2039']})
if article_survey is None or len(article_survey) < int(MIN_ARTICLE_SURVEY) or int(
MAX_ARTICLE_SURVEY) < len(article_survey):
raise serializers.ValidationError({'details': ERROR_CODE['2040']})
return attrs
def create(self, validated_data):
"""
to create article.
ID in post data dict is for update api.
:return: article object
"""
article_cards = validated_data.pop('article_cards')
article_survey = validated_data.pop('article_survey')
article = Article.objects.create(**validated_data)
for card in article_cards:
card = pop_id(card)
if 'image' in card and card['image'] is not None:
image = card.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
card['image_url'] = upload_image_to_alibaba(image, filename)
ArticleCard.objects.create(article=article, **card)
for survey in article_survey:
survey = pop_id(survey)
options = survey.pop('survey_options')
survey_obj = ArticleSurvey.objects.create(article=article, points=ARTICLE_SURVEY_POINTS, **survey)
for option in options:
option = pop_id(option)
SurveyOption.objects.create(survey=survey_obj, **option)
return article
def update(self, instance, validated_data):
"""
to update article and related table
:param instance: article object,
:return: article object
"""
article_cards = validated_data.pop('article_cards')
article_survey = validated_data.pop('article_survey')
instance.title = validated_data.get('title', instance.title)
instance.description = validated_data.get('description', instance.description)
instance.save()
# Update or create cards
for card_data in article_cards:
card_id = card_data.get('id', None)
if card_id:
card = ArticleCard.objects.get(id=card_id, article=instance)
card.title = card_data.get('title', card.title)
card.description = card_data.get('description', card.description)
if 'image' in card_data and card_data['image'] is not None:
image = card_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
card.image_url = upload_image_to_alibaba(image, filename)
card.save()
else:
card_data = pop_id(card_data)
if 'image' in card_data and card_data['image'] is not None:
image = card_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
# upload image on ali baba
card_data['image_url'] = upload_image_to_alibaba(image, filename)
ArticleCard.objects.create(article=instance, **card_data)
# Update or create survey sections
for survey_data in article_survey:
survey_id = survey_data.get('id', None)
options_data = survey_data.pop('survey_options')
if survey_id:
survey = ArticleSurvey.objects.get(id=survey_id, article=instance)
survey.question = survey_data.get('question', survey.question)
survey.save()
else:
survey_data = pop_id(survey_data)
survey = ArticleSurvey.objects.create(article=instance, **survey_data)
# Update or create survey options
for option_data in options_data:
option_id = option_data.get('id', None)
if option_id:
option = SurveyOption.objects.get(id=option_id, survey=survey)
option.option = option_data.get('option', option.option)
option.is_answer = option_data.get('is_answer', option.is_answer)
option.save()
else:
option_data = pop_id(option_data)
SurveyOption.objects.create(survey=survey, **option_data)
return instance
class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
"""
Article Card serializer
"""
image = serializers.FileField(required=False)
image_url = serializers.URLField(required=False)
class Meta:
"""
meta class
"""
model = DefaultArticleCardImage
fields = ('image_name', 'image', 'image_url')
def validate(self, attrs):
"""
to validate data
:return: validated data
"""
if 'image' not in attrs and attrs.get('image') is None:
raise serializers.ValidationError({'details': ERROR_CODE['2061']})
return attrs
def create(self, validated_data):
"""
to create and upload image
:return: card_image object
"""
image = validated_data.pop('image')
filename = f"{ARTICLE_CARD_IMAGE_FOLDER}/{image.name}"
if image and image.size == NUMBER['zero']:
raise serializers.ValidationError(ERROR_CODE['2035'])
# upload image on ali baba
validated_data['image_url'] = upload_image_to_alibaba(image, filename)
card_image = DefaultArticleCardImage.objects.create(**validated_data)
return card_image
class UserManagementListSerializer(serializers.ModelSerializer):
"""
user management serializer
"""
name = serializers.SerializerMethodField()
phone_number = serializers.SerializerMethodField()
user_type = serializers.SerializerMethodField()
class Meta:
"""
meta class
"""
model = USER
fields = ('name', 'email', 'phone_number', 'user_type', 'is_active')
@staticmethod
def get_name(obj):
"""
:param obj: user object
:return: full name
"""
return (obj.first_name + obj.last_name) if obj.last_name else obj.first_name
@staticmethod
def get_phone_number(obj):
"""
:param obj: user object
:return: user phone number
"""
if profile := obj.guardian_profile.all().first():
return profile.phone
elif profile := obj.junior_profile.all().first():
return profile.phone
else:
return None
@staticmethod
def get_user_type(obj):
"""
:param obj: user object
:return: user type
"""
if obj.guardian_profile.all().first():
return dict(USER_TYPE).get('2')
elif obj.junior_profile.all().first():
return dict(USER_TYPE).get('1')
else:
return None
+88
View File
@@ -0,0 +1,88 @@
"""
web_admin auth serializers file
"""
from datetime import datetime
# django imports
from rest_framework import serializers
from django.contrib.auth import get_user_model
from django.conf import settings
from django.utils import timezone
from rest_framework import status
from templated_email import send_templated_mail
# local imports
from account.models import UserEmailOtp
from account.utils import custom_error_response
from base.messages import ERROR_CODE
from guardian.tasks import generate_otp
USER = get_user_model()
class AdminForgotPasswordSerializer(serializers.ModelSerializer):
email = serializers.EmailField(required=True)
class Meta:
model = USER
fields = ('email',)
def validate(self, attrs):
""" used to validate the incoming data """
user = USER.objects.filter(email=attrs['email']).first()
if not user:
raise serializers.ValidationError(ERROR_CODE['2004'])
elif not user.is_superuser:
raise serializers.ValidationError(ERROR_CODE['2036'])
attrs.update({'user': user})
return attrs
def create(self, validated_data):
email = validated_data['email']
try:
USER.objects.get(email=email)
except USER.DoesNotExist:
return custom_error_response(ERROR_CODE['2004'], response_status=status.HTTP_404_NOT_FOUND)
verification_code = generate_otp()
# 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 = timezone.now() + timezone.timedelta(days=1)
user_data, created = UserEmailOtp.objects.get_or_create(email=email)
if created:
user_data.expired_at = expiry
if user_data:
user_data.otp = verification_code
user_data.expired_at = expiry
user_data.save()
return user_data
class AdminVerifyOTPSerializer(serializers.Serializer):
otp = serializers.CharField(max_length=6, min_length=6)
class Meta:
""" meta class """
fields = ('otp',)
def validate(self, attrs):
otp = attrs.pop('otp')
# fetch email otp object of the user
user_opt_details = UserEmailOtp.objects.filter(otp=otp).last()
if not user_opt_details:
raise serializers.ValidationError(ERROR_CODE['2008'])
if user_opt_details.expired_at.replace(tzinfo=None) < datetime.utcnow():
raise serializers.ValidationError(ERROR_CODE['2029'])
user_details = USER.objects.filter(email=user_opt_details.email).last()
user_opt_details.delete()
if user_details:
return attrs
+6
View File
@@ -0,0 +1,6 @@
"""
web_admin test file
"""
from django.test import TestCase
# Create your tests here.
+24
View File
@@ -0,0 +1,24 @@
"""
web_admin urls file
"""
# django imports
from django.urls import path, include
from rest_framework import routers
# local imports
from web_admin.views.article import ArticleViewSet, DefaultArticleCardImagesViewSet, UserManagementViewSet
from web_admin.views.auth import ForgetAndResetPasswordViewSet
# initiate router
router = routers.SimpleRouter()
router.register('article', ArticleViewSet, basename='article')
router.register('default-card-images', DefaultArticleCardImagesViewSet, basename='default-card-images')
router.register('user', UserManagementViewSet, basename='user')
# forgot and reset password api for admin
router.register('admin', ForgetAndResetPasswordViewSet, basename='admin')
urlpatterns = [
path('api/v1/', include(router.urls)),
]
+13
View File
@@ -0,0 +1,13 @@
"""
web_utils file
"""
def pop_id(data):
"""
to pop id, not in use
:param data:
:return: data
"""
data.pop('id') if 'id' in data else data
return data
View File
+224
View File
@@ -0,0 +1,224 @@
"""
web_admin views file
"""
# django imports
from rest_framework.viewsets import GenericViewSet, mixins
from rest_framework.filters import OrderingFilter, SearchFilter
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.permissions import IsAuthenticated, AllowAny
from django.contrib.auth import get_user_model
# local imports
from account.utils import custom_response, custom_error_response
from base.constants import USER_TYPE
from base.messages import SUCCESS_CODE, ERROR_CODE
from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticleCardImage
from web_admin.permission import AdminPermission
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleCardSerializer,
DefaultArticleCardImageSerializer,
UserManagementListSerializer)
USER = get_user_model()
class ArticleViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.UpdateModelMixin,
mixins.ListModelMixin, mixins.RetrieveModelMixin, mixins.DestroyModelMixin):
"""
article api
"""
serializer_class = ArticleSerializer
permission_classes = [IsAuthenticated, AdminPermission]
queryset = Article
filter_backends = (OrderingFilter, SearchFilter,)
search_fields = ['title']
http_method_names = ['get', 'post', 'put', 'delete']
def get_queryset(self):
article = self.queryset.objects.filter(is_deleted=False).prefetch_related(
'article_cards', 'article_survey', 'article_survey__survey_options'
).order_by('-created_at')
queryset = self.filter_queryset(article)
return queryset
def create(self, request, *args, **kwargs):
"""
article create api method
:param request:
:param args:
:param kwargs:
:return: success message
"""
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE["3027"])
def update(self, request, *args, **kwargs):
"""
article update api method
:param request:
:param args:
:param kwargs:
:return: success message
"""
article = self.get_object()
serializer = self.serializer_class(article, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE["3028"])
def list(self, request, *args, **kwargs):
"""
article list api method
:param request:
:param args:
:param kwargs:
:return: list of article
"""
queryset = self.get_queryset()
paginator = self.pagination_class()
paginated_queryset = paginator.paginate_queryset(queryset, request)
serializer = self.serializer_class(paginated_queryset, many=True)
return custom_response(None, data=serializer.data)
def retrieve(self, request, *args, **kwargs):
"""
article detail api method
:param request:
:param args:
:param kwargs:
:return: article detail data
"""
queryset = self.get_object()
serializer = self.serializer_class(queryset, many=False)
return custom_response(None, data=serializer.data)
def destroy(self, request, *args, **kwargs):
"""
article delete (soft delete) api method
:param request:
:param args:
:param kwargs:
:return: success message
"""
article = self.get_object()
article.is_deleted = True
article.save()
if article:
return custom_response(SUCCESS_CODE["3029"])
return custom_error_response(ERROR_CODE["2041"], status.HTTP_400_BAD_REQUEST)
@action(methods=['get'], url_name='remove_card', url_path='remove_card',
detail=True, serializer_class=None)
def remove_article_card(self, request, *args, **kwargs):
"""
article card remove (delete) api method
:param request:
:param args:
:param kwargs:
:return: success message
"""
try:
ArticleCard.objects.filter(id=kwargs['pk']).first().delete()
return custom_response(SUCCESS_CODE["3030"])
except AttributeError:
return custom_error_response(ERROR_CODE["2042"], response_status=status.HTTP_400_BAD_REQUEST)
@action(methods=['get'], url_name='remove_survey', url_path='remove_survey',
detail=True, serializer_class=None)
def remove_article_survey(self, request, *args, **kwargs):
"""
article survey remove (delete) api method
:param request:
:param args:
:param kwargs:
:return: success message
"""
try:
ArticleSurvey.objects.filter(id=kwargs['pk']).first().delete()
return custom_response(SUCCESS_CODE["3031"])
except AttributeError:
return custom_error_response(ERROR_CODE["2043"], response_status=status.HTTP_400_BAD_REQUEST)
@action(methods=['post'], url_name='test-add-card', url_path='test-add-card',
detail=False, serializer_class=ArticleCardSerializer, permission_classes=[AllowAny])
def add_card(self, request):
"""
:param request:
:return:
"""
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE["3000"])
@action(methods=['get'], url_name='test-list-card', url_path='test-list-card',
detail=False, serializer_class=ArticleCardSerializer, permission_classes=[AllowAny])
def list_card(self, request):
"""
:param request:
:return:
"""
queryset = ArticleCard.objects.all()
serializer = self.serializer_class(queryset, many=True)
return custom_response(None, serializer.data)
class DefaultArticleCardImagesViewSet(GenericViewSet, mixins.CreateModelMixin, mixins.ListModelMixin):
"""
api to upload and list default article card images
"""
serializer_class = DefaultArticleCardImageSerializer
# permission_classes = [IsAuthenticated, AdminPermission]
queryset = DefaultArticleCardImage.objects.all()
def create(self, request, *args, **kwargs):
"""
api method to upload default article card images
:param request:
:return: success message
"""
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE["3000"])
def list(self, request, *args, **kwargs):
"""
api method to list default article card images
:param request:
:return: default article card images
"""
queryset = self.queryset
serializer = self.serializer_class(queryset, many=True)
return custom_response(None, data=serializer.data)
class UserManagementViewSet(GenericViewSet, mixins.ListModelMixin):
"""
api to manage (list, view, edit) user
"""
serializer_class = UserManagementListSerializer
permission_classes = []
queryset = USER.objects.prefetch_related(
'guardian_profile', 'junior_profile')
def get_queryset(self):
if self.request.query_params.get('user_type') == dict(USER_TYPE).get('2'):
return self.queryset.filter(junior_profile__isnull=True)
elif self.request.query_params.get('user_type') == dict(USER_TYPE).get('1'):
return self.queryset.filter(guardian_profile__isnull=True)
else:
return self.queryset
def list(self, request, *args, **kwargs):
"""
api method to list all the user
:param request:
:return:
"""
queryset = self.get_queryset()
serializer = self.serializer_class(queryset, many=True)
return custom_response(None, data=serializer.data)
+43
View File
@@ -0,0 +1,43 @@
"""
web_admin auth views file
"""
# django imports
from rest_framework.viewsets import GenericViewSet, mixins
from rest_framework.decorators import action
from django.contrib.auth import get_user_model
# local imports
from account.utils import custom_response
from base.constants import USER_TYPE
from base.messages import SUCCESS_CODE, ERROR_CODE
from web_admin.permission import AdminPermission
from web_admin.serializers.auth_serializer import AdminForgotPasswordSerializer, AdminVerifyOTPSerializer
USER = get_user_model()
class ForgetAndResetPasswordViewSet(GenericViewSet):
queryset = None
@action(methods=['post'], url_name='forgot-password', url_path='forgot-password',
detail=False, serializer_class=AdminForgotPasswordSerializer)
def admin_forgot_password(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE['3015'])
@action(methods=['post'], url_name='verify-otp', url_path='verify-otp',
detail=False, serializer_class=AdminVerifyOTPSerializer)
def admin_verify_otp(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
return custom_response(SUCCESS_CODE['3011'])
@action(methods=['post'], url_name='resend-otp', url_path='resend-otp',
detail=False, serializer_class=AdminForgotPasswordSerializer)
def admin_resend_otp(self, request):
serializer = self.serializer_class(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return custom_response(SUCCESS_CODE['3015'])
+13
View File
@@ -0,0 +1,13 @@
{
"type": "service_account",
"project_id": "zod-bank-ebb2a",
"private_key_id": "f1115f1b1fece77ba7a119b70e2502ce0777a7d4",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQCvLWEobyVN7D4+\nSZ4NwcugwuVk9MV7CjhQGB8lqzf/1Z0plBytjpjM75+orFk5n2tnOgTxsWCqR1R7\nLry4x2QH3HgJebd/TZTIyfepcAeuzUVhq9prgWVRsvxjpihMPZufA/Q1GEX5TBwX\nEasBW91Qwas2NBhUrzotnUBxOshVB4zCo3Ls9dbAN9O2O6paUMvcofSsRZ9XkB6P\nFFKy6nbQ3Bo+Lw3ntUfG1JQgkkxto2Vudiiq6J2dE6Eih2acEhEezQoJVpkMK+si\nlGp88T3j8nTx3o6ol99ke+3ZejPVE5sUbuhokSV/tS1Goy3whP+ys9lQtIyt3/mJ\nlmkoB9ShAgMBAAECggEAAk3H0GFF08C3EBWyDqH5dbLXbtH92e/UeNAMNcV4mGbY\n5GKGFywmEVmg2N22EPqa/s+Y6QxxrD9u9G1+EhbnIIx7olOBkScJ9C0c9CWztCon\ntPaofd1E1cI7I7UfVTg2ZLAHrBN4H70eadYc4va8bBtHj0EHYONz7S8sEBQ1Qna2\nIQuCEWY6MzhwCNEFIJd8ikd0GnkAJCuInK3F+2c37kugdgjRKxkTIfWmhNIfyWn3\ngoui5wltuuDKETVj9VUMyN6P7kffIJzS4mMRm/9F92scpPRbK+X1TrHpFsO826oP\nUuSBi5oOZYNyAvMBXGdnemoUNtAE+xg4kqwYJu5T0QKBgQDlbO08yH5MSLUxXZfL\nkCYg1mCUC4ySpL/qVFNuyvY+ex9gIg6gj4vEpK8s1lt0yvxrTng/MXxMMg7o9dO8\nmSlv/665twk/7NfkexJPWs1ph+54tud3Sa0TiVw6U5ObPMr1EYc7RnoELDR9Exc1\nUDJB+T3f3SGJicZn4pALyx132wKBgQDDd9lhqWLXJeG7JkInhLywuSVmnk077hY8\nxAfHqlO+/EEkM/YBtUvtC4KNiSkVftQwSM80NRiHlPvlWZ0G8l9RAfM/xxL/XUq6\nOu1fW1uJuukJgXFePV9SQLzfgd1fk1f/dKuiPSNhCzgTD7dFks0Ip6FD6/PCdN7x\neqRUqDccMwKBgQCTk1mW+7CiCTLkKjv2KScdgEhncnZd7bO1W8C/R7bVwgUQpVeb\nWDqjpvs3cDssCVYNAFDA9Wfq61hD6bzlV/AbpvARbfd5MzQ8OB4zBUmUVGfFJoIF\nbVLzeivlKNWNybETqs6+Bjt+a6Dnw1vuY0OwxE5Urb1g50rEkCvwKhsueQKBgQDB\nUt3rG46oX80cPkCbuUquNs/o6JRWu6m+u9s9/RYLBI6g8ctT8S2A6ytaNNgvbFsM\nzlYwunriTdW9Bp6p6jmfcyBUad4+NtTbz8BJ2Z91Xylwv1eS73xBa8nh/R0nlCEq\nhQfj1DgTmPcC0z5eT0z+TFzRQqK6JsEBcFzrZdvrxQKBgQDGEtWElG5XoDwnwO5e\nmfFLRKHfG+Xa6FChl2eeDIGOxv/Y6SMwT68t0gtnDFQGtcIMvC+kC/1Rv1v8yrOD\nCzEToh91Fw1c1eayNnsLq07qYnVWoMa7xFFSDhtBAnepqlU+81qaDvp+yILTIYSP\nwUuk3NpdJs2LkMetm5zJC9PJ2w==\n-----END PRIVATE KEY-----\n",
"client_email": "[email protected]",
"client_id": "102629742363778142120",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-umcph%40zod-bank-ebb2a.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}
+45
View File
@@ -0,0 +1,45 @@
"""
Celery Basic configuration
"""
# python imports
import os
# third party imports
from celery import Celery
from dotenv import load_dotenv
from celery.schedules import crontab
# OR, the same with increased verbosity:
load_dotenv(verbose=True)
env_path = os.path.join(os.path.abspath(os.path.join('.env', os.pardir)), '.env')
load_dotenv(dotenv_path=env_path)
os.environ.setdefault('DJANGO_SETTINGS_MODULE', os.getenv('SETTINGS'))
# celery app
app = Celery()
app.config_from_object('django.conf:settings')
# Load task modules from all registered Django apps.
app.autodiscover_tasks()
@app.task(bind=True)
def debug_task(self):
""" celery debug task """
print(f'Request: {self.request!r}')
"""cron task"""
app.conf.beat_schedule = {
"expired_task": {
"task": "guardian.utils.update_expired_task_status",
"schedule": crontab(minute=0, hour=0),
},
}
+109 -53
View File
@@ -9,13 +9,16 @@ https://docs.djangoproject.com/en/3.0/topics/settings/
For the full list of settings and their values, see For the full list of settings and their values, see
https://docs.djangoproject.com/en/3.0/ref/settings/ https://docs.djangoproject.com/en/3.0/ref/settings/
""" """
# Django Import
import os import os
from dotenv import load_dotenv from dotenv import load_dotenv
from datetime import timedelta from datetime import timedelta
from firebase_admin import initialize_app
load_dotenv() load_dotenv()
# OR, the same with increased verbosity: # OR, the same with increased verbosity:
load_dotenv(verbose=True) load_dotenv(verbose=True)
# env path
env_path = os.path.join(os.path.abspath(os.path.join('.env', os.pardir)), '.env') env_path = os.path.join(os.path.abspath(os.path.join('.env', os.pardir)), '.env')
load_dotenv(dotenv_path=env_path) load_dotenv(dotenv_path=env_path)
@@ -29,37 +32,49 @@ BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = '-pb+8w#)6qsh+w&tr+q$tholf7=54v%05e^9!lneiqqgtddg6q' SECRET_KEY = os.getenv('SECRET_KEY')
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True DEBUG = os.getenv('DEBUG')
# cors allow setting
CORS_ORIGIN_ALLOW_ALL = True
# allow all host
ALLOWED_HOSTS = ['*'] ALLOWED_HOSTS = ['*']
# Application definition # Application definition
INSTALLED_APPS = [ INSTALLED_APPS = [
# Add your installed Django apps here
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.auth', 'django.contrib.auth',
'django.contrib.contenttypes', 'django.contrib.contenttypes',
'django.contrib.sessions', 'django.contrib.sessions',
'django.contrib.messages', 'django.contrib.messages',
'django.contrib.staticfiles', 'django.contrib.staticfiles',
# Add Django rest frame work apps here
'django_extensions', 'django_extensions',
'storages', 'storages',
'drf_yasg', 'drf_yasg',
'corsheaders', 'corsheaders',
'django.contrib.postgres', 'django.contrib.postgres',
'rest_framework', 'rest_framework',
'fcm_django',
'django_celery_beat',
# Add your custom apps here.
'django_ses', 'django_ses',
'account', 'account',
'junior', 'junior',
'guardian', 'guardian',
'notifications',
'web_admin',
# 'social_django' # 'social_django'
] ]
# define middle ware here
MIDDLEWARE = [ MIDDLEWARE = [
# Add your middleware classes here.
'corsheaders.middleware.CorsMiddleware',
'django.middleware.security.SecurityMiddleware', 'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware', 'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware', 'django.middleware.common.CommonMiddleware',
@@ -67,10 +82,13 @@ MIDDLEWARE = [
'django.contrib.auth.middleware.AuthenticationMiddleware', 'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware', 'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware', 'django.middleware.clickjacking.XFrameOptionsMiddleware',
'account.custom_middleware.CustomMiddleware'
] ]
# define root
ROOT_URLCONF = 'zod_bank.urls' ROOT_URLCONF = 'zod_bank.urls'
# define templates
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
@@ -87,17 +105,22 @@ TEMPLATES = [
}, },
] ]
# define wsgi
WSGI_APPLICATION = 'zod_bank.wsgi.application' WSGI_APPLICATION = 'zod_bank.wsgi.application'
# define rest frame work
REST_FRAMEWORK = { REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [ 'DEFAULT_AUTHENTICATION_CLASSES': [
# 'rest_framework.authentication.SessionAuthentication', # 'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication', 'rest_framework.authentication.BasicAuthentication',
'rest_framework_simplejwt.authentication.JWTAuthentication', 'rest_framework_simplejwt.authentication.JWTAuthentication',],
] 'DEFAULT_PAGINATION_CLASS': 'rest_framework.pagination.PageNumberPagination',
'PAGE_SIZE': 10,
} }
# define jwt token
SIMPLE_JWT = { SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(minutes=50), 'ACCESS_TOKEN_LIFETIME': timedelta(hours=2, minutes=59, seconds=59, microseconds=999999),
'REFRESH_TOKEN_LIFETIME': timedelta(days=7), 'REFRESH_TOKEN_LIFETIME': timedelta(hours=71, minutes=59, seconds=59, microseconds=999999),
} }
# Database # Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases # https://docs.djangoproject.com/en/3.0/ref/settings/#databases
@@ -111,9 +134,34 @@ DATABASES = {
'PORT':os.getenv('DB_PORT'), 'PORT':os.getenv('DB_PORT'),
} }
} }
# define swagger setting
SWAGGER_SETTINGS = {
"exclude_namespaces": [],
"api_version": '0.1',
"api_path": "",
"enabled_methods": [
'get',
'post',
'put',
'patch',
'delete'
],
"api_key": '',
"is_authenticated": True,
"is_superuser": False,
'SECURITY_DEFINITIONS': {
"api_key": {
"type": "apiKey",
"name": "Authorization",
"in": "header",
},
},
}
# Password validation # Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators # https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
# password validation
AUTH_PASSWORD_VALIDATORS = [ AUTH_PASSWORD_VALIDATORS = [
{ {
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
@@ -133,21 +181,21 @@ AUTH_PASSWORD_VALIDATORS = [
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/ # https://docs.djangoproject.com/en/3.0/topics/i18n/
# language code
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'en-us'
# time zone
TIME_ZONE = 'UTC' TIME_ZONE = 'UTC'
# define I18N
USE_I18N = True USE_I18N = True
# define L10N
USE_L10N = True USE_L10N = True
# define TZ
USE_TZ = True USE_TZ = True
# cors header settings # cors header settings
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https') SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
CORS_ORIGIN_ALLOW_ALL = True
# cors allow method
CORS_ALLOW_METHODS = ( CORS_ALLOW_METHODS = (
'DELETE', 'DELETE',
'GET', 'GET',
@@ -156,7 +204,7 @@ CORS_ALLOW_METHODS = (
'POST', 'POST',
'PUT', 'PUT',
) )
# cors allow header
CORS_ALLOW_HEADERS = ( CORS_ALLOW_HEADERS = (
'accept', 'accept',
'accept-encoding', 'accept-encoding',
@@ -169,52 +217,60 @@ CORS_ALLOW_HEADERS = (
'x-requested-with', 'x-requested-with',
) )
CORS_EXPOSE_HEADERS = (
'Access-Control-Allow-Origin: *',
)
# Firebase settings
FIREBASE_APP = initialize_app()
# fcm django settings
FCM_DJANGO_SETTINGS = {
"APP_VERBOSE_NAME": "ZOD_Bank",
"ONE_DEVICE_PER_USER": False,
"DELETE_INACTIVE_DEVICES": True,
"UPDATE_ON_DUPLICATE_REG_ID": True,
}
"""Static files (CSS, JavaScript, Images) """Static files (CSS, JavaScript, Images)
https://docs.djangoproject.com/en/3.0/howto/static-files/""" https://docs.djangoproject.com/en/3.0/howto/static-files/"""
# AUTHENTICATION_BACKENDS = [ # google client id
# 'social_core.backends.google.GoogleOAuth2', GOOGLE_CLIENT_ID = os.getenv('GOOGLE_CLIENT_ID')
# 'django.contrib.auth.backends.ModelBackend', # google client secret key
# ] GOOGLE_CLIENT_SECRET = os.getenv('GOOGLE_CLIENT_SECRET')
#
# LOGIN_URL = 'login'
# LOGIN_REDIRECT_URL = 'home'
# LOGOUT_URL = 'logout'
# LOGOUT_REDIRECT_URL = 'login'
# SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = '' # CELERY SETUP
# SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = '' CELERY_BROKER_URL = os.getenv('CELERY_BROKER_URL')
CELERY_RESULT_BACKEND = os.getenv('CELERY_RESULT_BACKEND')
CELERY_ACCEPT_CONTENT = ['application/json']
CELERY_TASK_SERIALIZER = 'json'
CELERY_RESULT_SERIALIZER = 'json'
GOOGLE_CLIENT_ID = "182276566528-hlbjncs19fo502jposod6kft2p9k4grk.apps.googleusercontent.com" # email settings
GOOGLE_CLIENT_SECRET = "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" EMAIL_BACKEND = os.getenv("MAIL_BACKEND")
EMAIL_HOST = os.getenv("MAIL_HOST")
EMAIL_PORT = os.getenv("MAIL_PORT")
EMAIL_USE_TLS = os.getenv("MAIL_USE_TLS")
EMAIL_HOST_USER = os.getenv("MAIL_HOST_USER")
EMAIL_HOST_PASSWORD = os.getenv("MAIL_HOST_PASSWORD")
EMAIL_FROM_ADDRESS = os.getenv("MAIL_FROM_ADDRESS")
DEFAULT_ADDRESS = os.getenv("DEFAULT_ADDRESS")
# EMAIL_BACKEND = os.getenv('EMAIL_BACKEND')
# EMAIL_HOST = os.getenv('EMAIL_HOST')
# EMAIL_PORT = os.getenv('EMAIL_PORT')
# EMAIL_USE_TLS = os.getenv('EMAIL_USE_TLS')
# EMAIL_HOST_USER = os.getenv('EMAIL_HOST_USER') # Replace with your Gmail email address
# EMAIL_HOST_PASSWORD = os.getenv('EMAIL_HOST_PASSWORD') # Replace with your Gmail email password or App password
# EMAIL_FROM_ADDRESS = os.getenv('EMAIL_FROM_ADDRESS')
EMAIL_BACKEND="django.core.mail.backends.smtp.EmailBackend" # ali baba cloud settings
EMAIL_HOST="smtp.sendgrid.net"
EMAIL_PORT="587"
EMAIL_USE_TLS="True"
EMAIL_HOST_USER="apikey" # Replace with your Gmail email address
EMAIL_HOST_PASSWORD="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"
EMAIL_FROM_ADDRESS="[email protected]"
# ALIYUN_OSS_ACCESS_KEY_ID = os.getenv('ALIYUN_OSS_ACCESS_KEY_ID') ALIYUN_OSS_ACCESS_KEY_ID = os.getenv('ALIYUN_OSS_ACCESS_KEY_ID')
# ALIYUN_OSS_ACCESS_KEY_SECRET = os.getenv('ALIYUN_OSS_ACCESS_KEY_SECRET') ALIYUN_OSS_ACCESS_KEY_SECRET = os.getenv('ALIYUN_OSS_ACCESS_KEY_SECRET')
# ALIYUN_OSS_BUCKET_NAME = os.getenv('ALIYUN_OSS_BUCKET_NAME') ALIYUN_OSS_BUCKET_NAME = os.getenv('ALIYUN_OSS_BUCKET_NAME')
# ALIYUN_OSS_ENDPOINT = os.getenv('ALIYUN_OSS_ENDPOINT') ALIYUN_OSS_ENDPOINT = os.getenv('ALIYUN_OSS_ENDPOINT')
# ALIYUN_OSS_REGION = os.getenv('ALIYUN_OSS_REGION') ALIYUN_OSS_REGION = os.getenv('ALIYUN_OSS_REGION')
ALIYUN_OSS_ACCESS_KEY_ID="LTAI5t7w1gq1CswJtvxtEZTd"
ALIYUN_OSS_ACCESS_KEY_SECRET="6yknAFpP2gVMhCWAJwbAjCEw2eehpf"
ALIYUN_OSS_BUCKET_NAME="zod-dev"
ALIYUN_OSS_ENDPOINT="oss-me-central-1.aliyuncs.com"
ALIYUN_OSS_REGION="Global"
# define static url
STATIC_URL = 'static/' STATIC_URL = 'static/'
# define static root
STATIC_ROOT = 'static' STATIC_ROOT = 'static'
MEDIA_URL = "/media/"
MEDIA_ROOT = os.path.join(os.path.dirname(BASE_DIR), 'media')
+2
View File
@@ -30,4 +30,6 @@ urlpatterns = [
path('', include(('account.urls', 'account'), namespace='account')), path('', include(('account.urls', 'account'), namespace='account')),
path('', include('guardian.urls')), path('', include('guardian.urls')),
path('', include(('junior.urls', 'junior'), namespace='junior')), path('', include(('junior.urls', 'junior'), namespace='junior')),
path('', include(('notifications.urls', 'notifications'), namespace='notifications')),
path('', include(('web_admin.urls', 'web_admin'), namespace='web_admin')),
] ]