mirror of
https://github.com/HamzaSha1/zod-backend.git
synced 2026-10-23 09:27:57 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b1d8949b08 | ||
|
|
801bc45bc5 | ||
|
|
d1927b24ee | ||
|
|
012d93f70f | ||
|
|
a1f9f93654 | ||
|
|
652fe9e680 | ||
|
|
13665f4c9a | ||
|
|
bf5453c7b7 | ||
|
|
6e3166967e | ||
|
|
47a00f313a | ||
|
|
ad4d782e72 | ||
|
|
8050e70cf7 | ||
|
|
18a53e1c48 | ||
|
|
f1333491e0 | ||
|
|
bd7eddb275 | ||
|
|
4c34c2496b | ||
|
|
e121c92fb4 | ||
|
|
18143e0219 | ||
|
|
251a912948 | ||
|
|
f7bb83cebb | ||
|
|
994e9a270e | ||
|
|
32c35f8649 | ||
|
|
ea02d7f5bb | ||
|
|
648628d3ea | ||
|
|
6ba3d7d8db | ||
|
|
651405ddef | ||
|
|
3afd7fecf3 | ||
|
|
c484669a2d | ||
|
|
d1a4b86b09 | ||
|
|
a3c2b68a0d | ||
|
|
e157e98a17 | ||
|
|
a653518cfd | ||
|
|
af25dc4e82 | ||
|
|
eaf67b682f | ||
|
|
71bbef84aa | ||
|
|
085607128b | ||
|
|
fbdef7c0c4 | ||
|
|
535e2e7f56 | ||
|
|
08f54f28a4 | ||
|
|
de710c8a7b | ||
|
|
af06dddbeb | ||
|
|
0a9dde2038 | ||
|
|
f11959d6bc | ||
|
|
7aee372606 | ||
|
|
1dec5ace3f | ||
|
|
2444b6f55e | ||
|
|
3ecbc1d303 | ||
|
|
ca0041caa6 | ||
|
|
c800853e9b | ||
|
|
373c1b70ab | ||
|
|
3a84b1ea75 | ||
|
|
bf1004696a | ||
|
|
d937c1bb92 | ||
|
|
0a877b410e | ||
|
|
9d4e7b05e4 | ||
|
|
b20e1cf516 | ||
|
|
6b0ea91742 | ||
|
|
859d26d073 | ||
|
|
154b9de32b | ||
|
|
4a554272a0 | ||
|
|
8533a27cb7 | ||
|
|
7db6502a89 | ||
|
|
a8e9a09d3f | ||
|
|
1e97d7bd6b | ||
|
|
b084b255dc | ||
|
|
441842df74 | ||
|
|
78fb5f5650 | ||
|
|
f3478c972e | ||
|
|
f63d9ddea0 | ||
|
|
bc18c67527 | ||
|
|
0471a3d588 | ||
|
|
ffb99f5099 | ||
|
|
8183edf319 | ||
|
|
8f214d11a7 | ||
|
|
b5a89df59a | ||
|
|
5524eeed64 | ||
|
|
aeaa7d7ab8 | ||
|
|
69be3bb2ac | ||
|
|
be9f600bcc | ||
|
|
65d0932893 | ||
|
|
9b14eedb18 | ||
|
|
6c96ea0820 | ||
|
|
a211baa10a | ||
|
|
a93dc83bd1 | ||
|
|
116fb00358 | ||
|
|
20fa6e43da | ||
|
|
2cffc4e128 | ||
|
|
ec585d35f3 | ||
|
|
d62efa2139 | ||
|
|
7b75a3233c | ||
|
|
e1ef289c69 | ||
|
|
4f79a690c1 | ||
|
|
0af2a35206 | ||
|
|
a262b03292 | ||
|
|
d24f075110 | ||
|
|
f7624bc1e7 | ||
|
|
dc12b35842 | ||
|
|
a80f9db557 | ||
|
|
3ad29e677d | ||
|
|
8b0a5d9a8e | ||
|
|
16d823f97d | ||
|
|
d4008d6cc2 | ||
|
|
3dae22a870 | ||
|
|
a8d291474a | ||
|
|
e6482167ae | ||
|
|
9a932d31b5 | ||
|
|
e2c84eb83d | ||
|
|
5bfc3966b3 | ||
|
|
37d191eef8 | ||
|
|
e9ee8ec8b2 | ||
|
|
cc5ecc0647 | ||
|
|
63dfd731fc | ||
|
|
e9d4d7091e | ||
|
|
219bae792e | ||
|
|
df3cab99a5 | ||
|
|
d2242d4c64 | ||
|
|
b7d5916f8e | ||
|
|
5e17edcf3f | ||
|
|
b6fe943bfc | ||
|
|
8e529b292d | ||
|
|
61ca2e7d2d | ||
|
|
966f94eb1e | ||
|
|
f424c99478 | ||
|
|
34359360e0 | ||
|
|
685a822e52 | ||
|
|
e57344e54f | ||
|
|
235fc9baac | ||
|
|
3dc8268b07 | ||
|
|
8be8d56036 | ||
|
|
c79a891c89 | ||
|
|
eb4e09964d | ||
|
|
cf9376663c | ||
|
|
9891060ee3 | ||
|
|
5b2ab2275e | ||
|
|
9b6a84c7d1 | ||
|
|
e9d4fbe373 | ||
|
|
21b92f8c74 | ||
|
|
c46b2ef52c | ||
|
|
c47f6222d9 | ||
|
|
b82902081f | ||
|
|
e9315beab9 | ||
|
|
3a938720dd | ||
|
|
464899f7d3 | ||
|
|
8a436bb79f | ||
|
|
2e0ceb8c92 | ||
|
|
1a2fd2d3a8 | ||
|
|
a65eb2f77d | ||
|
|
3d06139c4f | ||
|
|
f5a03e2fdf | ||
|
|
226ddfa7e6 | ||
|
|
1028822908 | ||
|
|
342f27fc62 | ||
|
|
e9aa2dfda9 | ||
|
|
624e7a4edb | ||
|
|
3072bd5cdb | ||
|
|
930b58cf05 | ||
|
|
339c49577e | ||
|
|
09f006eb13 | ||
|
|
cd3b385756 | ||
|
|
ab1a2be679 | ||
|
|
11605540d7 | ||
|
|
8cd4864748 | ||
|
|
ad0b5bfc75 | ||
|
|
f541608656 | ||
|
|
b1b7c42438 | ||
|
|
8f9450b743 | ||
|
|
bb3441e4ed | ||
|
|
5b9f7b1828 | ||
|
|
89dda61bff | ||
|
|
31f071a463 | ||
|
|
20644a6293 | ||
|
|
a8b01d4c3a | ||
|
|
56e1484b87 | ||
|
|
557a7d7a5c | ||
|
|
4e0c6a91f4 | ||
|
|
f931e20334 | ||
|
|
8d159ac3a4 | ||
|
|
95ce57ec7d | ||
|
|
7068551050 | ||
|
|
c23d65f67c | ||
|
|
73f05dc843 | ||
|
|
290089b9ef | ||
|
|
5f3a9c35fa | ||
|
|
b92115168d | ||
|
|
19acef10ef | ||
|
|
05fd76a50b | ||
|
|
60e6d96379 | ||
|
|
fad39d399f | ||
|
|
d573d56a35 | ||
|
|
08a08960ee | ||
|
|
f8e529600b | ||
|
|
9765c90b9b | ||
|
|
930c10b578 | ||
|
|
8ca74e4b03 | ||
|
|
95dad86b12 | ||
|
|
dfff643c71 | ||
|
|
214566ec8f | ||
|
|
210e1a39e9 | ||
|
|
e380f3f6ab | ||
|
|
99a59162a2 | ||
|
|
67a5f670f8 | ||
|
|
82189d3953 | ||
|
|
65ecd9a125 | ||
|
|
a7bce32993 | ||
|
|
4495d8999c | ||
|
|
61e94c9469 | ||
|
|
1c3d4731c1 | ||
|
|
46853011be | ||
|
|
2216411fd2 | ||
|
|
606c1fa6e6 | ||
|
|
092cbfad3e | ||
|
|
249a469d89 | ||
|
|
d202774df1 | ||
|
|
1adf0c2f70 | ||
|
|
b193166914 | ||
|
|
48b455e38a | ||
|
|
6d54a718b9 | ||
|
|
454f66b3a0 | ||
|
|
5045f5acda | ||
|
|
e84180a6c2 | ||
|
|
21e006ae2a | ||
|
|
92e5104e3f | ||
|
|
dfb73a7d36 | ||
|
|
3921f76f22 | ||
|
|
cdf1a7b74e | ||
|
|
b4026a4f11 | ||
|
|
71a3e36bf3 | ||
|
|
4bc91abebf | ||
|
|
5b236dfc81 | ||
|
|
150bb9250d | ||
|
|
51d3b77ff7 | ||
|
|
92c67dd455 | ||
|
|
9a5447bca2 | ||
|
|
392086760f | ||
|
|
ceaf584332 | ||
|
|
ef4c459229 | ||
|
|
dd2890bca6 | ||
|
|
b46109e487 | ||
|
|
3f6c9a2d99 | ||
|
|
f74302df04 | ||
|
|
9bdd324c4e | ||
|
|
04ed9c668c | ||
|
|
e1d092d663 | ||
|
|
4e8243c17e | ||
|
|
13ba311822 | ||
|
|
043c8c2f63 | ||
|
|
a5f239b9d9 | ||
|
|
36427b50c1 | ||
|
|
728d19da99 | ||
|
|
5da07002e0 | ||
|
|
3017b0ece0 | ||
|
|
e73113fcca | ||
|
|
ed1b5dd453 | ||
|
|
c278670c39 | ||
|
|
60af098e1e | ||
|
|
74328f37b2 | ||
|
|
d29a60558f | ||
|
|
451c3bdae7 | ||
|
|
d8f4467d98 | ||
|
|
7c809776b6 | ||
|
|
a9cc05c675 | ||
|
|
e46d649fdc | ||
|
|
adb827f5a0 | ||
|
|
a02dfd4e31 | ||
|
|
d3b0be953e | ||
|
|
11b9f00285 | ||
|
|
4bc16c56bd | ||
|
|
08dc9f8538 | ||
|
|
6373d1e02c | ||
|
|
83d7d119be | ||
|
|
a1d959299a | ||
|
|
070637bf1d | ||
|
|
19a6475097 | ||
|
|
fd9a4902ae | ||
|
|
082f93ff9d | ||
|
|
25eaee31a4 | ||
|
|
d5c30f2029 | ||
|
|
cae28e0b54 | ||
|
|
54200dba52 | ||
|
|
846a9d42d4 | ||
|
|
69c19cf097 | ||
|
|
18cb885410 | ||
|
|
4ca60af5da | ||
|
|
4d7209df19 | ||
|
|
94c76b7f2e | ||
|
|
af8ea39200 | ||
|
|
6e9304b2cc | ||
|
|
5eab8f4907 | ||
|
|
807526acfa | ||
|
|
f750487b88 | ||
|
|
8b0032f6d2 | ||
|
|
bb65f81200 | ||
|
|
04db40e100 | ||
|
|
1e162255d7 | ||
|
|
f4149379c2 | ||
|
|
a479f3cb62 | ||
|
|
fbd6f9ece5 | ||
|
|
11c84208b7 | ||
|
|
4f02cef0f9 | ||
|
|
b8f1acaed8 | ||
|
|
80dffb7942 | ||
|
|
0a8c2cf56d | ||
|
|
9e5dd5e3d5 | ||
|
|
03fb4f4176 | ||
|
|
22afe7e555 | ||
|
|
69723b362f | ||
|
|
9e612c7171 | ||
|
|
7a9be0326a | ||
|
|
aaa1c55227 | ||
|
|
656f0da89a | ||
|
|
b961044e4d | ||
|
|
28e71e132c | ||
|
|
b30643299f | ||
|
|
b6b70af13f | ||
|
|
22dd7fc10b | ||
|
|
2498394127 | ||
|
|
924bc20193 | ||
|
|
af7582b9e2 | ||
|
|
856d27d803 | ||
|
|
19f56280e4 | ||
|
|
2fc65d462d | ||
|
|
d3800dbc85 | ||
|
|
9373dc4697 | ||
|
|
32eaa6c3f2 | ||
|
|
11ff8fc700 | ||
|
|
9c75cb1615 | ||
|
|
3f8b0e2eb7 | ||
|
|
69ce6857e8 | ||
|
|
cdf656fdad | ||
|
|
3efa31efe4 | ||
|
|
275b09d2ea | ||
|
|
3cb0e3ed8b | ||
|
|
823c5ea4c4 | ||
|
|
c9ee482512 | ||
|
|
08750813ce | ||
|
|
75f4f66285 | ||
|
|
881bda739b | ||
|
|
d86f082e58 | ||
|
|
8eaf8751c2 | ||
|
|
b12c5071fe | ||
|
|
f75201b3dd | ||
|
|
6867920754 | ||
|
|
45ea7013f8 | ||
|
|
7d428f5eb5 | ||
|
|
22ba4288d9 | ||
|
|
9ff64e64ef | ||
|
|
4b23394569 | ||
|
|
81893c8841 | ||
|
|
10a1ea9b76 | ||
|
|
7ab16cb3de | ||
|
|
7fca493d0d | ||
|
|
85e4ae8761 | ||
|
|
6ebd5ae410 | ||
|
|
f57b111555 | ||
|
|
6596414675 | ||
|
|
ed7fd3c15d | ||
|
|
8cf7148114 | ||
|
|
b98443479f | ||
|
|
f917244265 | ||
|
|
ceb5bc13c3 | ||
|
|
ae0fc4fe8d | ||
|
|
0a1b9c7e70 | ||
|
|
f2cf1488e9 | ||
|
|
0426974539 | ||
|
|
b0e26f41b9 | ||
|
|
c20249730a | ||
|
|
b3b499e661 | ||
|
|
f377e283fd | ||
|
|
648a2ec4d5 | ||
|
|
83ec922584 | ||
|
|
88221ec77a | ||
|
|
e853346910 | ||
|
|
a088764b7b | ||
|
|
4a2f36cde8 | ||
|
|
401ee1ddf8 | ||
|
|
baacb1a18f | ||
|
|
5f1c645e3a | ||
|
|
07d150309b | ||
|
|
79648637aa | ||
|
|
756bea0471 | ||
|
|
465632f519 | ||
|
|
b9e2d9bc8a | ||
|
|
d82c8cd4ae | ||
|
|
dd0f2b027a | ||
|
|
3806d1f3a6 | ||
|
|
3801e6e027 | ||
|
|
f3e2ab9a34 | ||
|
|
685f627707 | ||
|
|
b2d172eae5 | ||
|
|
404825dc85 | ||
|
|
6e6f0a55d0 | ||
|
|
75d0b12008 | ||
|
|
2a4011ca5d | ||
|
|
ebb468166e | ||
|
|
e89fc513cb | ||
|
|
06176912ee | ||
|
|
ed8fc156ac | ||
|
|
e1af1c200d | ||
|
|
bff97f59b2 | ||
|
|
af121f5a53 | ||
|
|
4c3aa03e13 | ||
|
|
f3a8b52617 | ||
|
|
0c3a77cd11 | ||
|
|
d3564efbb9 | ||
|
|
329df77790 | ||
|
|
ee92c98f34 | ||
|
|
b238379a22 | ||
|
|
fbce04b97e | ||
|
|
afaed69eec | ||
|
|
3c0eaf676e | ||
|
|
6ffaa66a4d | ||
|
|
4b8e7189c1 | ||
|
|
d68f24543d | ||
|
|
3534561697 | ||
|
|
de774111c0 | ||
|
|
c081bc621d | ||
|
|
bab50e4492 | ||
|
|
151a177e76 | ||
|
|
73a62186a7 | ||
|
|
83c66ab3b6 | ||
|
|
94e53e1dcd | ||
|
|
68b77ef766 | ||
|
|
d2c8b18e3f | ||
|
|
a2000459d3 | ||
|
|
c079f3ceca | ||
|
|
671cdfc27b | ||
|
|
a83e27b12a | ||
|
|
87aa1af02b | ||
|
|
bdc0ba4fd5 | ||
|
|
5f3026f08f | ||
|
|
a323ad7df7 | ||
|
|
2b6e943d8c | ||
|
|
3af2584d6a | ||
|
|
bca0d8d49b | ||
|
|
b860d937f5 | ||
|
|
62cf2b14bf | ||
|
|
11b10bf751 | ||
|
|
d0ef4391cc | ||
|
|
5a1b419863 | ||
|
|
7bf0e62604 | ||
|
|
80843502a6 | ||
|
|
cbd3d139a5 | ||
|
|
3b31d08996 | ||
|
|
3cdd685ea8 | ||
|
|
c4152c0773 | ||
|
|
8a454c3f97 | ||
|
|
71405780f8 | ||
|
|
a8558b5e6e | ||
|
|
7c4f9b2506 | ||
|
|
cc3490b059 | ||
|
|
32476149bc | ||
|
|
c57224ad0e | ||
|
|
c8a8e1149b | ||
|
|
7c69dfd49f | ||
|
|
6dcf045131 | ||
|
|
6e84814117 | ||
|
|
c587ba322e | ||
|
|
2db0e47e3b | ||
|
|
52cdd4b0ba | ||
|
|
6be0682913 | ||
|
|
bfeeb5e41f | ||
|
|
bb56fc7a33 | ||
|
|
e6d45f3bf2 | ||
|
|
7cb792c6cf | ||
|
|
49b648e86b | ||
|
|
79e85fa968 | ||
|
|
5ea28bbd75 | ||
|
|
096961976d | ||
|
|
4d104bab1c | ||
|
|
ed28117a7f | ||
|
|
180104ece8 | ||
|
|
0a7c081f63 | ||
|
|
a2b4f3b758 | ||
|
|
d86f7d3436 | ||
|
|
a5a309cf5b | ||
|
|
0a5eaa233f | ||
|
|
b32d4def2d | ||
|
|
410282036e | ||
|
|
2a225ea79d | ||
|
|
65e012dd60 | ||
|
|
8013767d71 | ||
|
|
fdd0b88b11 | ||
|
|
bb1351c309 | ||
|
|
c15bd2339a | ||
|
|
5ee01af825 | ||
|
|
b8382e225c | ||
|
|
532b56c687 | ||
|
|
6fe29f0070 | ||
|
|
96d71d951e | ||
|
|
effad5cddc | ||
|
|
8c0da25814 | ||
|
|
b032300ab7 | ||
|
|
4b09ff41cb | ||
|
|
8762c00dba | ||
|
|
4410712b11 | ||
|
|
e2f737c5b4 | ||
|
|
0f5dfc2e9a | ||
|
|
b488b08f3f | ||
|
|
7102c44055 | ||
|
|
8e2d9c44b9 | ||
|
|
fa96364901 | ||
|
|
49abf12ce8 | ||
|
|
4002d1e561 | ||
|
|
9f9926da14 | ||
|
|
555d8231e7 | ||
|
|
3223a2b050 | ||
|
|
2e7d36485c | ||
|
|
2e3870dc53 | ||
|
|
86e2c75afc | ||
|
|
e238c85674 | ||
|
|
5429140a5c | ||
|
|
6e6ba881f6 | ||
|
|
74fc656bd1 | ||
|
|
e9b0e480e6 | ||
|
|
49fc8d9dca | ||
|
|
39271282e2 | ||
|
|
9b84d176af | ||
|
|
49336e2e18 | ||
|
|
0df748ccbf | ||
|
|
9d600a1ea8 | ||
|
|
06d841f444 | ||
|
|
e3a5d52a1d | ||
|
|
130dcd83e7 | ||
|
|
19a2a3d1db | ||
|
|
02f01b8a16 | ||
|
|
3d84c03163 | ||
|
|
5cc43aa2b8 | ||
|
|
4c0cac7cb0 | ||
|
|
6aa198d1ad | ||
|
|
ba4d5933de | ||
|
|
4d04f16cee | ||
|
|
032186bf43 | ||
|
|
04342133ef | ||
|
|
751af642b8 | ||
|
|
e90326fcb6 | ||
|
|
0cec3a5cf1 | ||
|
|
ae7250b34a | ||
|
|
8f1f49de45 | ||
|
|
de5d3b19d0 | ||
|
|
2b0fc56190 | ||
|
|
4779749b0c | ||
|
|
6d6d21137f | ||
|
|
cd944b10f5 | ||
|
|
1399b585e8 | ||
|
|
2d3100286d | ||
|
|
761e723cd3 | ||
|
|
71e7601d6b | ||
|
|
45410fa0ca | ||
|
|
ac00543312 | ||
|
|
b3f35d271b | ||
|
|
a4d9997580 | ||
|
|
c43db90219 | ||
|
|
d3b7b6cd07 | ||
|
|
1ea4720ab4 | ||
|
|
f7cfd53594 | ||
|
|
dae6ffbed1 | ||
|
|
75bc387bec | ||
|
|
aadced6a13 | ||
|
|
c3f7750349 | ||
|
|
0c760a58f3 | ||
|
|
b156e62dc8 | ||
|
|
b11137bfad | ||
|
|
23fb2a0695 | ||
|
|
fe8ddd02a5 | ||
|
|
3dc16ce79c | ||
|
|
b75756c43a | ||
|
|
c01c2b4e23 | ||
|
|
ddfc4e946c | ||
|
|
f2533d9a08 | ||
|
|
4e22dd9871 | ||
|
|
e5b88794ad | ||
|
|
6cbe824d9e | ||
|
|
df438241ce | ||
|
|
bb06238623 | ||
|
|
ada987413b | ||
|
|
323168b7d0 |
+1
-2
@@ -6,7 +6,6 @@ media/
|
|||||||
*.name
|
*.name
|
||||||
*.iml
|
*.iml
|
||||||
*.log
|
*.log
|
||||||
*.xml
|
|
||||||
*.pyo
|
*.pyo
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.idea
|
.idea
|
||||||
@@ -21,4 +20,4 @@ static/*
|
|||||||
__pycache__/
|
__pycache__/
|
||||||
*.env
|
*.env
|
||||||
ve/*
|
ve/*
|
||||||
|
celerybeat-schedule
|
||||||
|
|||||||
+14
-1
@@ -2,7 +2,7 @@
|
|||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
|
|
||||||
"""Import django app"""
|
"""Import django app"""
|
||||||
from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete, UserDeviceDetails
|
from .models import UserEmailOtp, DefaultTaskImages, UserNotification, UserDelete, UserDeviceDetails, ForceUpdate
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
|
|
||||||
@admin.register(UserDelete)
|
@admin.register(UserDelete)
|
||||||
@@ -39,6 +39,19 @@ 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(ForceUpdate)
|
||||||
|
class ForceUpdateAdmin(admin.ModelAdmin):
|
||||||
|
"""Force update"""
|
||||||
|
list_display = ['version', 'device_type']
|
||||||
|
readonly_fields = ('device_type',)
|
||||||
|
|
||||||
|
def has_add_permission(self, request):
|
||||||
|
count = ForceUpdate.objects.all().count()
|
||||||
|
if count < 2:
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
def has_delete_permission(self, request, obj=None):
|
||||||
|
return False
|
||||||
@admin.register(UserDeviceDetails)
|
@admin.register(UserDeviceDetails)
|
||||||
class UserDeviceDetailsAdmin(admin.ModelAdmin):
|
class UserDeviceDetailsAdmin(admin.ModelAdmin):
|
||||||
"""User profile admin"""
|
"""User profile admin"""
|
||||||
|
|||||||
@@ -5,9 +5,11 @@ from rest_framework.response import Response
|
|||||||
from rest_framework.renderers import JSONRenderer
|
from rest_framework.renderers import JSONRenderer
|
||||||
"""App django"""
|
"""App django"""
|
||||||
from account.utils import custom_error_response
|
from account.utils import custom_error_response
|
||||||
from account.models import UserDeviceDetails
|
from account.models import UserDeviceDetails, ForceUpdate
|
||||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
|
from base.constants import NUMBER
|
||||||
|
from junior.models import Junior
|
||||||
|
from guardian.models import Guardian
|
||||||
# Custom middleware
|
# Custom middleware
|
||||||
# when user login with
|
# when user login with
|
||||||
# multiple device simultaneously
|
# multiple device simultaneously
|
||||||
@@ -15,6 +17,18 @@ from base.messages import ERROR_CODE, SUCCESS_CODE
|
|||||||
# multiple devices only
|
# multiple devices only
|
||||||
# user can login in single
|
# user can login in single
|
||||||
# device at a time"""
|
# device at a time"""
|
||||||
|
# force update
|
||||||
|
# use 308 status code for force update
|
||||||
|
|
||||||
|
def custom_response(custom_error, response_status = status.HTTP_404_NOT_FOUND):
|
||||||
|
"""custom response"""
|
||||||
|
response = Response(custom_error.data, status=response_status)
|
||||||
|
# Set content type header to "application/json"
|
||||||
|
response['Content-Type'] = 'application/json'
|
||||||
|
# Render the response as JSON
|
||||||
|
renderer = JSONRenderer()
|
||||||
|
response.content = renderer.render(response.data)
|
||||||
|
return response
|
||||||
class CustomMiddleware(object):
|
class CustomMiddleware(object):
|
||||||
"""Custom middleware"""
|
"""Custom middleware"""
|
||||||
def __init__(self, get_response):
|
def __init__(self, get_response):
|
||||||
@@ -26,15 +40,34 @@ class CustomMiddleware(object):
|
|||||||
response = self.get_response(request)
|
response = self.get_response(request)
|
||||||
# Code to be executed after the view is called
|
# Code to be executed after the view is called
|
||||||
device_id = request.META.get('HTTP_DEVICE_ID')
|
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||||
|
user_type = request.META.get('HTTP_USER_TYPE')
|
||||||
|
version = request.META.get('HTTP_VERSION')
|
||||||
|
device_type = str(request.META.get('HTTP_TYPE'))
|
||||||
|
|
||||||
|
api_endpoint = request.path
|
||||||
|
unrestricted_api = ('/api/v1/user/login/', '/api/v1/logout/', '/api/v1/generate-token/')
|
||||||
if request.user.is_authenticated:
|
if request.user.is_authenticated:
|
||||||
"""device details"""
|
# device details
|
||||||
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
|
if device_id:
|
||||||
if device_id and not device_details:
|
device_details = UserDeviceDetails.objects.filter(user=request.user, device_id=device_id).last()
|
||||||
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
|
if not device_details and api_endpoint not in unrestricted_api:
|
||||||
response = Response(custom_error.data, status=status.HTTP_404_NOT_FOUND)
|
custom_error = custom_error_response(ERROR_CODE['2037'], response_status=status.HTTP_404_NOT_FOUND)
|
||||||
# Set content type header to "application/json"
|
response = custom_response(custom_error)
|
||||||
response['Content-Type'] = 'application/json'
|
if user_type and str(user_type) == str(NUMBER['one']):
|
||||||
# Render the response as JSON
|
junior = Junior.objects.filter(auth=request.user, is_active=False).last()
|
||||||
renderer = JSONRenderer()
|
if junior:
|
||||||
response.content = renderer.render(response.data)
|
custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND)
|
||||||
|
response = custom_response(custom_error)
|
||||||
|
elif user_type and str(user_type) == str(NUMBER['two']):
|
||||||
|
guardian = Guardian.objects.filter(user=request.user, is_active=False).last()
|
||||||
|
if guardian:
|
||||||
|
custom_error = custom_error_response(ERROR_CODE['2075'], response_status=status.HTTP_404_NOT_FOUND)
|
||||||
|
response = custom_response(custom_error)
|
||||||
|
|
||||||
|
if version and device_type:
|
||||||
|
force_update = ForceUpdate.objects.filter(version=version, device_type=device_type).last()
|
||||||
|
if not force_update:
|
||||||
|
custom_error = custom_error_response(ERROR_CODE['2079'],
|
||||||
|
response_status=status.HTTP_308_PERMANENT_REDIRECT)
|
||||||
|
response = custom_response(custom_error, status.HTTP_308_PERMANENT_REDIRECT)
|
||||||
return response
|
return response
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-22 07:39
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('account', '0009_alter_userdevicedetails_device_id'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='ForceUpdate',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('version', models.CharField(blank=True, max_length=50, null=True)),
|
||||||
|
('device_type', models.CharField(blank=True, choices=[('1', 'android'), ('2', 'ios')], default=None, max_length=15, null=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Force Update Version',
|
||||||
|
'verbose_name_plural': 'Force Update Version',
|
||||||
|
'db_table': 'force_update',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
+25
-2
@@ -2,8 +2,9 @@
|
|||||||
"""Django import"""
|
"""Django import"""
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
"""App import"""
|
"""App import"""
|
||||||
from base.constants import USER_TYPE
|
from base.constants import USER_TYPE, DEVICE_TYPE
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
|
|
||||||
class UserProfile(models.Model):
|
class UserProfile(models.Model):
|
||||||
@@ -152,7 +153,7 @@ class UserDeviceDetails(models.Model):
|
|||||||
"""
|
"""
|
||||||
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_device_details')
|
user = models.OneToOneField(User, on_delete=models.CASCADE, related_name='user_device_details')
|
||||||
"""Device ID"""
|
"""Device ID"""
|
||||||
device_id = models.CharField(max_length=500)
|
device_id = models.CharField(max_length=500, null=True, blank=True)
|
||||||
|
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
@@ -165,3 +166,25 @@ class UserDeviceDetails(models.Model):
|
|||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.user.email
|
return self.user.email
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class ForceUpdate(models.Model):
|
||||||
|
"""
|
||||||
|
Force update
|
||||||
|
"""
|
||||||
|
"""Version ID"""
|
||||||
|
version = models.CharField(max_length=50, null=True, blank=True)
|
||||||
|
device_type = models.CharField(max_length=15, choices=DEVICE_TYPE, 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 = 'force_update'
|
||||||
|
verbose_name = 'Force Update Version'
|
||||||
|
verbose_name_plural = 'Force Update Version'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
return self.version
|
||||||
|
|||||||
+125
-37
@@ -1,9 +1,10 @@
|
|||||||
"""Account serializer"""
|
"""Account serializer"""
|
||||||
"""Django Import"""
|
"""Django Import"""
|
||||||
import random
|
# 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
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
|
import secrets
|
||||||
"""App import"""
|
"""App import"""
|
||||||
# Import guardian's model,
|
# Import guardian's model,
|
||||||
# Import junior's model,
|
# Import junior's model,
|
||||||
@@ -17,9 +18,9 @@ from rest_framework_simplejwt.tokens import RefreshToken
|
|||||||
|
|
||||||
from guardian.models import Guardian
|
from guardian.models import Guardian
|
||||||
from junior.models import Junior
|
from junior.models import Junior
|
||||||
from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp
|
from account.models import UserEmailOtp, DefaultTaskImages, UserDelete, UserNotification, UserPhoneOtp, ForceUpdate
|
||||||
from base.constants import GUARDIAN, JUNIOR, SUPERUSER
|
from base.constants import GUARDIAN, JUNIOR, SUPERUSER, NUMBER
|
||||||
from base.messages import ERROR_CODE_REQUIRED, ERROR_CODE, SUCCESS_CODE, STATUS_CODE_ERROR
|
from base.messages import ERROR_CODE, SUCCESS_CODE, STATUS_CODE_ERROR
|
||||||
from .utils import delete_user_account_condition_social, delete_user_account_condition
|
from .utils import delete_user_account_condition_social, delete_user_account_condition
|
||||||
|
|
||||||
# In this serializer file
|
# In this serializer file
|
||||||
@@ -40,7 +41,7 @@ from .utils import delete_user_account_condition_social, delete_user_account_con
|
|||||||
# email verification serializer,
|
# email verification serializer,
|
||||||
# phone otp serializer
|
# phone otp serializer
|
||||||
|
|
||||||
|
# create all serializer here
|
||||||
class GoogleLoginSerializer(serializers.Serializer):
|
class GoogleLoginSerializer(serializers.Serializer):
|
||||||
"""google login serializer"""
|
"""google login serializer"""
|
||||||
access_token = serializers.CharField(max_length=5000, required=True)
|
access_token = serializers.CharField(max_length=5000, required=True)
|
||||||
@@ -90,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()
|
||||||
@@ -102,50 +104,108 @@ class ResetPasswordSerializer(serializers.Serializer):
|
|||||||
return user_opt_details
|
return user_opt_details
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
|
|
||||||
class ChangePasswordSerializer(serializers.Serializer):
|
class ChangePasswordSerializer(serializers.Serializer):
|
||||||
"""Update Password after verification"""
|
"""Update Password after verification"""
|
||||||
current_password = serializers.CharField(max_length=100)
|
current_password = serializers.CharField(max_length=100, required=True)
|
||||||
new_password = serializers.CharField(required=True)
|
new_password = serializers.CharField(required=True)
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = User
|
model = User
|
||||||
|
|
||||||
def validate_current_password(self, value):
|
def validate_current_password(self, value):
|
||||||
user = self.context
|
user = self.context
|
||||||
|
# check old password
|
||||||
if self.context.password not in ('', None) and user.check_password(value):
|
if self.context.password not in ('', None) and user.check_password(value):
|
||||||
return value
|
return value
|
||||||
raise serializers.ValidationError(ERROR_CODE['2015'])
|
raise serializers.ValidationError(ERROR_CODE['2015'])
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
"""
|
||||||
|
change password
|
||||||
|
"""
|
||||||
new_password = validated_data.pop('new_password')
|
new_password = validated_data.pop('new_password')
|
||||||
current_password = validated_data.pop('current_password')
|
current_password = validated_data.pop('current_password')
|
||||||
"""Check new password is different from current password"""
|
# Check new password is different from current password
|
||||||
if new_password == current_password:
|
if new_password == current_password:
|
||||||
raise serializers.ValidationError({"details": ERROR_CODE['2026']})
|
raise serializers.ValidationError({"details": ERROR_CODE['2026']})
|
||||||
user_details = User.objects.filter(email=self.context).last()
|
|
||||||
if user_details:
|
|
||||||
user_details.set_password(new_password)
|
|
||||||
user_details.save()
|
|
||||||
return {'password':new_password}
|
|
||||||
return ''
|
|
||||||
|
|
||||||
|
user_details = self.context
|
||||||
|
user_details.set_password(new_password)
|
||||||
|
user_details.save()
|
||||||
|
return {'password':new_password}
|
||||||
|
|
||||||
|
|
||||||
class ForgotPasswordSerializer(serializers.Serializer):
|
class ForgotPasswordSerializer(serializers.Serializer):
|
||||||
"""Forget password serializer"""
|
"""Forget password serializer"""
|
||||||
email = serializers.EmailField()
|
email = serializers.EmailField(required=True)
|
||||||
|
|
||||||
|
def validate_email(self, value):
|
||||||
|
"""
|
||||||
|
validate email exist ot not
|
||||||
|
value: string
|
||||||
|
return none
|
||||||
|
"""
|
||||||
|
if not User.objects.get(email=value):
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2004']})
|
||||||
|
return value
|
||||||
|
|
||||||
|
class AdminLoginSerializer(serializers.ModelSerializer):
|
||||||
|
"""admin login serializer"""
|
||||||
|
email = serializers.EmailField(required=True)
|
||||||
|
password = serializers.CharField(required=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = User
|
||||||
|
fields = ('email', 'password')
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
user = User.objects.filter(email__iexact=attrs['email'], is_superuser=True
|
||||||
|
).only('id', 'first_name', 'last_name', 'email',
|
||||||
|
'username', 'is_active', 'is_superuser').first()
|
||||||
|
|
||||||
|
if not user or not user.check_password(attrs['password']):
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2002']})
|
||||||
|
|
||||||
|
self.context.update({'user': user})
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
"""
|
||||||
|
used to return the user object after validation
|
||||||
|
"""
|
||||||
|
return self.context['user']
|
||||||
|
|
||||||
|
|
||||||
class SuperUserSerializer(serializers.ModelSerializer):
|
class SuperUserSerializer(serializers.ModelSerializer):
|
||||||
"""Super admin serializer"""
|
"""Super admin serializer"""
|
||||||
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):
|
||||||
@@ -156,6 +216,7 @@ class GuardianSerializer(serializers.ModelSerializer):
|
|||||||
last_name = serializers.SerializerMethodField('get_last_name')
|
last_name = serializers.SerializerMethodField('get_last_name')
|
||||||
auth_token = serializers.SerializerMethodField('get_auth_token')
|
auth_token = serializers.SerializerMethodField('get_auth_token')
|
||||||
refresh_token = serializers.SerializerMethodField('get_refresh_token')
|
refresh_token = serializers.SerializerMethodField('get_refresh_token')
|
||||||
|
sign_up = serializers.SerializerMethodField()
|
||||||
|
|
||||||
def get_auth_token(self, obj):
|
def get_auth_token(self, obj):
|
||||||
refresh = RefreshToken.for_user(obj.user)
|
refresh = RefreshToken.for_user(obj.user)
|
||||||
@@ -169,10 +230,17 @@ class GuardianSerializer(serializers.ModelSerializer):
|
|||||||
|
|
||||||
def get_user_type(self, obj):
|
def get_user_type(self, obj):
|
||||||
"""user type"""
|
"""user type"""
|
||||||
email_verified = UserEmailOtp.objects.filter(email=obj.user.username).last()
|
if self.context.get('user_type', ''):
|
||||||
if email_verified and email_verified.user_type != None:
|
return self.context.get('user_type')
|
||||||
return email_verified.user_type
|
# remove the below code once user_type can be passed
|
||||||
return '2'
|
# from everywhere from where this serializer is being called
|
||||||
|
else:
|
||||||
|
email_verified = UserEmailOtp.objects.filter(
|
||||||
|
email=obj.user.username
|
||||||
|
).last()
|
||||||
|
if email_verified and email_verified.user_type is not None:
|
||||||
|
return email_verified.user_type
|
||||||
|
return str(NUMBER['two'])
|
||||||
|
|
||||||
def get_auth(self, obj):
|
def get_auth(self, obj):
|
||||||
"""user email address"""
|
"""user email address"""
|
||||||
@@ -186,12 +254,16 @@ class GuardianSerializer(serializers.ModelSerializer):
|
|||||||
"""user last name"""
|
"""user last name"""
|
||||||
return obj.user.last_name
|
return obj.user.last_name
|
||||||
|
|
||||||
|
def get_sign_up(self, obj):
|
||||||
|
return True if self.context.get('sign_up', '') else False
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = Guardian
|
model = Guardian
|
||||||
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
|
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
|
||||||
'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active',
|
'phone', 'family_name', 'gender', 'dob', 'referral_code', 'is_active', 'is_deleted',
|
||||||
'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type', 'country_name']
|
'is_complete_profile', 'passcode', 'image', 'created_at', 'updated_at', 'user_type',
|
||||||
|
'country_name', 'sign_up']
|
||||||
|
|
||||||
|
|
||||||
class JuniorSerializer(serializers.ModelSerializer):
|
class JuniorSerializer(serializers.ModelSerializer):
|
||||||
@@ -202,6 +274,7 @@ class JuniorSerializer(serializers.ModelSerializer):
|
|||||||
last_name = serializers.SerializerMethodField('get_last_name')
|
last_name = serializers.SerializerMethodField('get_last_name')
|
||||||
auth_token = serializers.SerializerMethodField('get_auth_token')
|
auth_token = serializers.SerializerMethodField('get_auth_token')
|
||||||
refresh_token = serializers.SerializerMethodField('get_refresh_token')
|
refresh_token = serializers.SerializerMethodField('get_refresh_token')
|
||||||
|
sign_up = serializers.SerializerMethodField()
|
||||||
|
|
||||||
def get_auth_token(self, obj):
|
def get_auth_token(self, obj):
|
||||||
refresh = RefreshToken.for_user(obj.auth)
|
refresh = RefreshToken.for_user(obj.auth)
|
||||||
@@ -209,7 +282,7 @@ class JuniorSerializer(serializers.ModelSerializer):
|
|||||||
return access_token
|
return access_token
|
||||||
|
|
||||||
def get_refresh_token(self, obj):
|
def get_refresh_token(self, obj):
|
||||||
refresh = RefreshToken.for_user(obj.user)
|
refresh = RefreshToken.for_user(obj.auth)
|
||||||
refresh_token = str(refresh)
|
refresh_token = str(refresh)
|
||||||
return refresh_token
|
return refresh_token
|
||||||
|
|
||||||
@@ -217,7 +290,7 @@ class JuniorSerializer(serializers.ModelSerializer):
|
|||||||
email_verified = UserEmailOtp.objects.filter(email=obj.auth.username).last()
|
email_verified = UserEmailOtp.objects.filter(email=obj.auth.username).last()
|
||||||
if email_verified and email_verified.user_type is not None:
|
if email_verified and email_verified.user_type is not None:
|
||||||
return email_verified.user_type
|
return email_verified.user_type
|
||||||
return '1'
|
return str(NUMBER['one'])
|
||||||
|
|
||||||
def get_auth(self, obj):
|
def get_auth(self, obj):
|
||||||
return obj.auth.username
|
return obj.auth.username
|
||||||
@@ -228,19 +301,23 @@ class JuniorSerializer(serializers.ModelSerializer):
|
|||||||
def get_last_name(self, obj):
|
def get_last_name(self, obj):
|
||||||
return obj.auth.last_name
|
return obj.auth.last_name
|
||||||
|
|
||||||
|
def get_sign_up(self, obj):
|
||||||
|
return True if self.context.get('sign_up', '') else False
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = Junior
|
model = Junior
|
||||||
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
|
fields = ['id', 'auth_token', 'refresh_token', 'email', 'first_name', 'last_name', 'country_code',
|
||||||
'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active',
|
'phone', 'gender', 'dob', 'guardian_code', 'referral_code','is_active', 'is_password_set',
|
||||||
'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited']
|
'is_complete_profile', 'created_at', 'image', 'updated_at', 'user_type', 'country_name','is_invited',
|
||||||
|
'is_deleted', 'sign_up']
|
||||||
|
|
||||||
class EmailVerificationSerializer(serializers.ModelSerializer):
|
class EmailVerificationSerializer(serializers.ModelSerializer):
|
||||||
"""Email verification serializer"""
|
"""Email verification serializer"""
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = UserEmailOtp
|
model = UserEmailOtp
|
||||||
fields = '__all__'
|
fields = ('email',)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -251,6 +328,7 @@ class DefaultTaskImagesSerializer(serializers.ModelSerializer):
|
|||||||
model = DefaultTaskImages
|
model = DefaultTaskImages
|
||||||
fields = ['id', 'task_name', 'image_url']
|
fields = ['id', 'task_name', 'image_url']
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
|
# create default task object
|
||||||
data = DefaultTaskImages.objects.create(**validated_data)
|
data = DefaultTaskImages.objects.create(**validated_data)
|
||||||
return data
|
return data
|
||||||
|
|
||||||
@@ -273,10 +351,11 @@ class UserDeleteSerializer(serializers.ModelSerializer):
|
|||||||
data = validated_data.get('reason')
|
data = validated_data.get('reason')
|
||||||
passwd = self.context['password']
|
passwd = self.context['password']
|
||||||
signup_method = self.context['signup_method']
|
signup_method = self.context['signup_method']
|
||||||
random_num = random.randint(0, 10000)
|
random_num = secrets.randbelow(10001)
|
||||||
user_tb = User.objects.filter(id=user.id).last()
|
user_tb = User.objects.filter(id=user.id).last()
|
||||||
user_type_datas = UserEmailOtp.objects.filter(email=user.email).last()
|
user_type_datas = UserEmailOtp.objects.filter(email=user.email).last()
|
||||||
if user_tb and user_tb.check_password(passwd) and signup_method == '1':
|
# 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
|
user_type_data = user_type_datas.user_type
|
||||||
instance = delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num)
|
instance = delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num)
|
||||||
return instance
|
return instance
|
||||||
@@ -303,19 +382,28 @@ class UpdateUserNotificationSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['push_notification', 'email_notification', 'sms_notification']
|
fields = ['push_notification', 'email_notification', 'sms_notification']
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
instance = UserNotification.objects.filter(user=self.context).last()
|
instance, _ = UserNotification.objects.update_or_create(
|
||||||
if instance:
|
user=self.context,
|
||||||
instance.push_notification = validated_data.get('push_notification',instance.push_notification)
|
defaults={
|
||||||
instance.email_notification = validated_data.get('email_notification', instance.email_notification)
|
'push_notification': validated_data.get('push_notification'),
|
||||||
instance.sms_notification = validated_data.get('sms_notification', instance.sms_notification)
|
'email_notification': validated_data.get('email_notification'),
|
||||||
instance.save()
|
'sms_notification': validated_data.get('sms_notification'),
|
||||||
else:
|
})
|
||||||
instance = UserNotification.objects.create(user=self.context)
|
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
class UserPhoneOtpSerializer(serializers.ModelSerializer):
|
class UserPhoneOtpSerializer(serializers.ModelSerializer):
|
||||||
"""User Phone serializers"""
|
"""User Phone serializers"""
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = UserPhoneOtp
|
model = UserPhoneOtp
|
||||||
fields = '__all__'
|
fields = '__all__'
|
||||||
|
|
||||||
|
class ForceUpdateSerializer(serializers.ModelSerializer):
|
||||||
|
""" ForceUpdate Serializer
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
""" meta info """
|
||||||
|
model = ForceUpdate
|
||||||
|
fields = ('id', 'version', 'device_type')
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 0 27px 15px;">
|
<td style="padding: 0 27px 15px;">
|
||||||
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
|
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
|
||||||
Hi {{full_name}},
|
Hello,
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 0 27px 22px;">
|
<td style="padding: 0 27px 22px;">
|
||||||
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
|
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
|
||||||
You are receiving this email for join the ZOD bank platform. Please use <b>{{ url }} </b> link to join the platform.
|
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.
|
<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>
|
</p>
|
||||||
|
|||||||
@@ -1,21 +1,21 @@
|
|||||||
{% extends "templated_email/email_base.email" %}
|
{% extends "templated_email/email_base.email" %}
|
||||||
|
|
||||||
{% block subject %}
|
{% block subject %}
|
||||||
{{subject}}
|
Support Mail
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block plain %}
|
{% block plain %}
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 0 27px 15px;">
|
<td style="padding: 0 27px 15px;">
|
||||||
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
|
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
|
||||||
Hi {{name}},
|
Hi Support Team,
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
<tr>
|
||||||
<td style="padding: 0 27px 22px;">
|
<td style="padding: 0 27px 22px;">
|
||||||
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
|
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
|
||||||
<b>{{name}}</b> have some queries and need some support. Please support them by using their email address <b> {{sender}}</b>. <br> <br> <b>Queries are:- </b> <br> {{ message }}
|
<b>{{name}}</b> have some queries and need some support. Please support them by using their email address <b> {{sender}}</b>. <br> <br> <b>Queries are:- </b> <br><li> {{ message }}</li>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{% extends "templated_email/email_base.email" %}
|
||||||
|
|
||||||
|
{% block subject %}
|
||||||
|
Account Activated
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block plain %}
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 0 27px 15px;">
|
||||||
|
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
|
||||||
|
Hi User,
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 0 27px 22px;">
|
||||||
|
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
|
||||||
|
We're pleased to inform you that your account has been successfully reactivated by our admin team. Welcome back to ZOD ! <br><br> You can now access all the features and services as before. If you have any questions or need assistance, please feel free to reach out to our support team. <br><br> Thank you for being a valued member of our community.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endblock %}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
{% extends "templated_email/email_base.email" %}
|
||||||
|
|
||||||
|
{% block subject %}
|
||||||
|
Account Deactivated
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block plain %}
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 0 27px 15px;">
|
||||||
|
<p style="margin: 0; font-size: 16px; line-height: 20px; padding: 36px 0 0; font-weight: 500; color: #1f2532;">
|
||||||
|
Hi User,
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 0 27px 22px;">
|
||||||
|
<p style="margin: 0;font-size: 14px; font-weight: 400; line-height: 21px; color: #1f2532;">
|
||||||
|
Your account has been deactivated by admin. Please reach out to the admin for assistance.
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endblock %}
|
||||||
+58
-3
@@ -1,5 +1,60 @@
|
|||||||
"""Test cases file of account"""
|
"""
|
||||||
"""Django import"""
|
test cases file of account
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
from django.test import TestCase
|
from django.test import TestCase
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
from rest_framework import status
|
||||||
|
from django.contrib.auth.models import User
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
|
|
||||||
|
|
||||||
|
class UserLoginTestCase(TestCase):
|
||||||
|
"""
|
||||||
|
test cases for login
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
set up data
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client = APIClient()
|
||||||
|
self.user_email = '[email protected]'
|
||||||
|
self.user = User.objects.create_superuser(username=self.user_email, email=self.user_email)
|
||||||
|
self.user.set_password('user@1234')
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
def test_admin_login_success(self):
|
||||||
|
"""
|
||||||
|
test admin login with valid credentials
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('account:admin-login')
|
||||||
|
data = {
|
||||||
|
'email': self.user_email,
|
||||||
|
'password': 'user@1234',
|
||||||
|
}
|
||||||
|
response = self.client.post(url, data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertIn('auth_token', response.data['data'])
|
||||||
|
self.assertIn('refresh_token', response.data['data'])
|
||||||
|
self.assertEqual(response.data['data']['username'], data['email'])
|
||||||
|
|
||||||
|
def test_admin_login_invalid_credentials(self):
|
||||||
|
"""
|
||||||
|
test admin login with invalid credentials
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('account:admin-login')
|
||||||
|
data = {
|
||||||
|
'email': self.user_email,
|
||||||
|
'password': 'user@1235',
|
||||||
|
}
|
||||||
|
response = self.client.post(url, data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertNotIn('auth_token', response.data)
|
||||||
|
self.assertNotIn('refresh_token', response.data)
|
||||||
|
|
||||||
|
# Add more test cases as needed
|
||||||
|
|
||||||
# Create your tests here.
|
|
||||||
|
|||||||
+5
-4
@@ -28,18 +28,17 @@ from .views import (UserLogin, SendPhoneOtp, UserPhoneVerification, UserEmailVer
|
|||||||
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
|
ForgotPasswordAPIView, ResetPasswordAPIView, ChangePasswordAPIView, UpdateProfileImage,
|
||||||
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
|
GoogleLoginViewSet, SigninWithApple, ProfileAPIViewSet, UploadImageAPIViewSet,
|
||||||
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
|
DefaultImageAPIViewSet, DeleteUserProfileAPIViewSet, UserNotificationAPIViewSet,
|
||||||
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView)
|
UpdateUserNotificationAPIViewSet, SendSupportEmail, LogoutAPIView, AccessTokenAPIView,
|
||||||
|
AdminLoginViewSet, ForceUpdateViewSet)
|
||||||
"""Router"""
|
"""Router"""
|
||||||
router = routers.SimpleRouter()
|
router = routers.SimpleRouter()
|
||||||
|
|
||||||
"""API End points with router"""
|
"""API End points with router"""
|
||||||
router.register('user', UserLogin, basename='user')
|
router.register('user', UserLogin, basename='user')
|
||||||
"""super admin login"""
|
"""super admin login"""
|
||||||
router.register('admin', UserLogin, basename='admin')
|
router.register('admin', AdminLoginViewSet, basename='admin')
|
||||||
"""google login end point"""
|
"""google login end point"""
|
||||||
router.register('google-login', GoogleLoginViewSet, basename='admin')
|
router.register('google-login', GoogleLoginViewSet, basename='admin')
|
||||||
router.register('send-phone-otp', SendPhoneOtp, basename='send-phone-otp')
|
|
||||||
router.register('user-phone-verification', UserPhoneVerification, basename='user-phone-verification')
|
|
||||||
"""email verification end point"""
|
"""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"""
|
"""Resend email otp end point"""
|
||||||
@@ -56,6 +55,8 @@ router.register('delete', DeleteUserProfileAPIViewSet, basename='delete')
|
|||||||
router.register('user-notification', UserNotificationAPIViewSet, basename='user-notification')
|
router.register('user-notification', UserNotificationAPIViewSet, basename='user-notification')
|
||||||
"""update user account notification"""
|
"""update user account notification"""
|
||||||
router.register('update-user-notification', UpdateUserNotificationAPIViewSet, basename='update-user-notification')
|
router.register('update-user-notification', UpdateUserNotificationAPIViewSet, basename='update-user-notification')
|
||||||
|
# Force update entry API
|
||||||
|
router.register('force-update', ForceUpdateViewSet, basename='force-update')
|
||||||
"""Define url pattern"""
|
"""Define url pattern"""
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('api/v1/', include(router.urls)),
|
path('api/v1/', include(router.urls)),
|
||||||
|
|||||||
+112
-11
@@ -1,4 +1,6 @@
|
|||||||
"""Account utils"""
|
"""Account utils"""
|
||||||
|
from celery import shared_task
|
||||||
|
import random
|
||||||
"""Import django"""
|
"""Import django"""
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
from rest_framework import viewsets, status
|
from rest_framework import viewsets, status
|
||||||
@@ -10,6 +12,7 @@ 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
|
from rest_framework import serializers
|
||||||
# Django App Import
|
# Django App Import
|
||||||
@@ -19,10 +22,11 @@ from rest_framework import serializers
|
|||||||
# Import messages from base package"""
|
# Import messages from base package"""
|
||||||
from junior.models import Junior
|
from junior.models import Junior
|
||||||
from guardian.models import Guardian
|
from guardian.models import Guardian
|
||||||
from account.models import UserDelete
|
from account.models import UserDelete, UserDeviceDetails
|
||||||
from base.messages import ERROR_CODE
|
from base.messages import ERROR_CODE
|
||||||
|
from django.utils import timezone
|
||||||
|
from base.constants import NUMBER
|
||||||
|
from junior.models import JuniorPoints
|
||||||
# Define delete
|
# Define delete
|
||||||
# user account condition,
|
# user account condition,
|
||||||
# Define delete
|
# Define delete
|
||||||
@@ -42,7 +46,7 @@ from base.messages import ERROR_CODE
|
|||||||
# referral code,
|
# referral code,
|
||||||
# Define function for generating
|
# Define function for generating
|
||||||
# alphanumeric code
|
# alphanumeric code
|
||||||
|
# otp expiry
|
||||||
def delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num):
|
def delete_user_account_condition(user, user_type_data, user_type, user_tb, data, random_num):
|
||||||
"""delete user account"""
|
"""delete user account"""
|
||||||
if user_type == '1' and user_type_data == '1':
|
if user_type == '1' and user_type_data == '1':
|
||||||
@@ -55,6 +59,7 @@ def delete_user_account_condition(user, user_type_data, user_type, user_tb, data
|
|||||||
user_tb.username = 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
|
d_email = user_tb.email
|
||||||
o_mail = user.email
|
o_mail = user.email
|
||||||
|
# update user email with dummy email
|
||||||
user_tb.save()
|
user_tb.save()
|
||||||
"""create object in user delete model"""
|
"""create object in user delete model"""
|
||||||
instance = UserDelete.objects.create(user=user_tb, d_email=d_email, old_email=o_mail,
|
instance = UserDelete.objects.create(user=user_tb, d_email=d_email, old_email=o_mail,
|
||||||
@@ -74,6 +79,7 @@ def delete_user_account_condition_social(user, user_type,user_tb, data, random_n
|
|||||||
user_tb.username = 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
|
dummy_email = user_tb.email
|
||||||
old_mail = user.email
|
old_mail = user.email
|
||||||
|
# update user email with dummy email
|
||||||
user_tb.save()
|
user_tb.save()
|
||||||
"""create object in user delete model"""
|
"""create object in user delete model"""
|
||||||
instance_data = UserDelete.objects.create(user=user_tb, d_email=dummy_email, old_email=old_mail,
|
instance_data = UserDelete.objects.create(user=user_tb, d_email=dummy_email, old_email=old_mail,
|
||||||
@@ -84,23 +90,30 @@ def junior_account_update(user_tb):
|
|||||||
"""junior account delete"""
|
"""junior account delete"""
|
||||||
junior_data = Junior.objects.filter(auth__email=user_tb.email).first()
|
junior_data = Junior.objects.filter(auth__email=user_tb.email).first()
|
||||||
if junior_data:
|
if junior_data:
|
||||||
|
# Update junior account
|
||||||
junior_data.is_active = False
|
junior_data.is_active = False
|
||||||
junior_data.is_verified = False
|
junior_data.is_verified = False
|
||||||
junior_data.guardian_code = '{}'
|
junior_data.guardian_code = None
|
||||||
|
junior_data.guardian_code_status = None
|
||||||
|
junior_data.is_deleted = True
|
||||||
junior_data.save()
|
junior_data.save()
|
||||||
|
JuniorPoints.objects.filter(junior=junior_data).delete()
|
||||||
|
|
||||||
def guardian_account_update(user_tb):
|
def guardian_account_update(user_tb):
|
||||||
"""update guardian account after delete the user account"""
|
"""update guardian account after delete the user account"""
|
||||||
guardian_data = Guardian.objects.filter(user__email=user_tb.email).first()
|
guardian_data = Guardian.objects.filter(user__email=user_tb.email).first()
|
||||||
if guardian_data:
|
if guardian_data:
|
||||||
|
# Update guardian account
|
||||||
guardian_data.is_active = False
|
guardian_data.is_active = False
|
||||||
guardian_data.is_verified = False
|
guardian_data.is_verified = False
|
||||||
|
guardian_data.is_deleted = True
|
||||||
guardian_data.save()
|
guardian_data.save()
|
||||||
jun_data = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code))
|
jun_data = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code))
|
||||||
"""Disassociate relation between guardian and junior"""
|
"""Disassociate relation between guardian and junior"""
|
||||||
for data in jun_data:
|
for data in jun_data:
|
||||||
data.guardian_code.remove(guardian_data.guardian_code)
|
data.guardian_code.remove(guardian_data.guardian_code)
|
||||||
data.save()
|
data.save()
|
||||||
|
@shared_task()
|
||||||
def send_otp_email(recipient_email, otp):
|
def send_otp_email(recipient_email, otp):
|
||||||
"""Send otp on email with template"""
|
"""Send otp on email with template"""
|
||||||
from_email = settings.EMAIL_FROM_ADDRESS
|
from_email = settings.EMAIL_FROM_ADDRESS
|
||||||
@@ -116,7 +129,45 @@ def send_otp_email(recipient_email, otp):
|
|||||||
)
|
)
|
||||||
return otp
|
return otp
|
||||||
|
|
||||||
def send_support_email(name, sender, subject, message):
|
|
||||||
|
@shared_task()
|
||||||
|
def send_all_email(template_name, email, otp):
|
||||||
|
"""
|
||||||
|
Send all type of email by passing template name
|
||||||
|
template_name: string
|
||||||
|
email: string
|
||||||
|
otp: string
|
||||||
|
"""
|
||||||
|
from_email = settings.EMAIL_FROM_ADDRESS
|
||||||
|
recipient_list = [email]
|
||||||
|
send_templated_mail(
|
||||||
|
template_name=template_name,
|
||||||
|
from_email=from_email,
|
||||||
|
recipient_list=recipient_list,
|
||||||
|
context={
|
||||||
|
'verification_code': otp
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
return otp
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def user_device_details(user, device_id):
|
||||||
|
"""
|
||||||
|
Used to store the device id of the user
|
||||||
|
user: user object
|
||||||
|
device_id: string
|
||||||
|
return
|
||||||
|
"""
|
||||||
|
device_details, created = UserDeviceDetails.objects.get_or_create(user__id=user)
|
||||||
|
if device_details:
|
||||||
|
device_details.device_id = device_id
|
||||||
|
device_details.save()
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def send_support_email(name, sender, message):
|
||||||
"""Send otp on email with template"""
|
"""Send otp on email with template"""
|
||||||
to_email = [settings.EMAIL_FROM_ADDRESS]
|
to_email = [settings.EMAIL_FROM_ADDRESS]
|
||||||
from_email = settings.DEFAULT_ADDRESS
|
from_email = settings.DEFAULT_ADDRESS
|
||||||
@@ -128,17 +179,19 @@ def send_support_email(name, sender, subject, message):
|
|||||||
context={
|
context={
|
||||||
'name': name.title(),
|
'name': name.title(),
|
||||||
'sender': sender,
|
'sender': sender,
|
||||||
'subject': subject,
|
|
||||||
'message': message
|
'message': message
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
return name
|
return name
|
||||||
def custom_response(detail, data=None, response_status=status.HTTP_200_OK):
|
|
||||||
|
|
||||||
|
def custom_response(detail, data=None, response_status=status.HTTP_200_OK, count=None):
|
||||||
"""Custom response code"""
|
"""Custom response code"""
|
||||||
if not data:
|
if not data:
|
||||||
"""when data is none"""
|
"""when data is none"""
|
||||||
data = None
|
data = None
|
||||||
return Response({"data": data, "message": detail, "status": "success", "code": response_status})
|
return Response({"data": data, "message": detail, "status": "success",
|
||||||
|
"code": response_status, "count": count})
|
||||||
|
|
||||||
|
|
||||||
def custom_error_response(detail, response_status):
|
def custom_error_response(detail, response_status):
|
||||||
@@ -151,8 +204,12 @@ def custom_error_response(detail, response_status):
|
|||||||
if not detail:
|
if not detail:
|
||||||
"""when details is empty"""
|
"""when details is empty"""
|
||||||
detail = {}
|
detail = {}
|
||||||
return Response({"error": detail, "status": "failed", "code": response_status})
|
if response_status == 406:
|
||||||
|
return Response({"error": detail, "status": "failed", "code": response_status,},
|
||||||
|
status=status.HTTP_308_PERMANENT_REDIRECT)
|
||||||
|
else:
|
||||||
|
return Response({"error": detail, "status": "failed", "code": response_status},
|
||||||
|
status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
def get_user_data(attrs):
|
def get_user_data(attrs):
|
||||||
"""
|
"""
|
||||||
@@ -223,3 +280,47 @@ def generate_code(value, user_id):
|
|||||||
return code
|
return code
|
||||||
|
|
||||||
|
|
||||||
|
OTP_EXPIRY = timezone.now() + timezone.timedelta(days=1)
|
||||||
|
|
||||||
|
|
||||||
|
def get_user_full_name(user_obj):
|
||||||
|
"""
|
||||||
|
to get user's full name
|
||||||
|
"""
|
||||||
|
return f"{user_obj.first_name} {user_obj.last_name}" if user_obj.first_name or user_obj.last_name else "User"
|
||||||
|
|
||||||
|
|
||||||
|
def make_special_password(length=10):
|
||||||
|
"""
|
||||||
|
to make secured password
|
||||||
|
:param length:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# Define character sets
|
||||||
|
lowercase_letters = string.ascii_lowercase
|
||||||
|
uppercase_letters = string.ascii_uppercase
|
||||||
|
digits = string.digits
|
||||||
|
special_characters = '@#$%&*?'
|
||||||
|
|
||||||
|
# Combine character sets
|
||||||
|
alphabets = lowercase_letters + uppercase_letters
|
||||||
|
|
||||||
|
# Create a password with random characters
|
||||||
|
password = [
|
||||||
|
secrets.choice(uppercase_letters) +
|
||||||
|
secrets.choice(lowercase_letters) +
|
||||||
|
secrets.choice(digits) +
|
||||||
|
secrets.choice(special_characters) +
|
||||||
|
''.join(secrets.choice(alphabets) for _ in range(length - 4))
|
||||||
|
]
|
||||||
|
|
||||||
|
return ''.join(password)
|
||||||
|
|
||||||
|
def task_status_fun(status_value):
|
||||||
|
"""task status"""
|
||||||
|
task_status_value = ['1']
|
||||||
|
if str(status_value) == '2':
|
||||||
|
task_status_value = ['2', '4']
|
||||||
|
elif str(status_value) == '3':
|
||||||
|
task_status_value = ['3', '5', '6']
|
||||||
|
return task_status_value
|
||||||
|
|||||||
+349
-130
@@ -1,9 +1,13 @@
|
|||||||
"""Account view """
|
"""Account view """
|
||||||
"""Django import"""
|
import threading
|
||||||
|
|
||||||
|
from notifications.utils import remove_fcm_token
|
||||||
|
|
||||||
|
# django imports
|
||||||
|
from rest_framework.viewsets import GenericViewSet, mixins
|
||||||
from datetime import datetime, timedelta
|
from datetime import datetime, timedelta
|
||||||
from rest_framework import viewsets, status, views
|
from rest_framework import viewsets, status, views
|
||||||
from rest_framework.decorators import action
|
from rest_framework.decorators import action
|
||||||
import random
|
|
||||||
import logging
|
import logging
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
import jwt
|
import jwt
|
||||||
@@ -16,35 +20,39 @@ import google.auth.transport.requests
|
|||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
import requests
|
import requests
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import mixins
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
"""App Import"""
|
|
||||||
|
# local imports
|
||||||
from guardian.models import Guardian
|
from guardian.models import Guardian
|
||||||
from junior.models import Junior
|
from junior.models import Junior, JuniorPoints
|
||||||
from guardian.utils import upload_image_to_alibaba
|
from guardian.utils import upload_image_to_alibaba
|
||||||
from account.models import UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification
|
from account.models import (UserDeviceDetails, UserPhoneOtp, UserEmailOtp, DefaultTaskImages, UserNotification,
|
||||||
|
ForceUpdate)
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
"""Account serializer"""
|
|
||||||
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
|
from .serializers import (SuperUserSerializer, GuardianSerializer, JuniorSerializer, EmailVerificationSerializer,
|
||||||
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
|
ForgotPasswordSerializer, ResetPasswordSerializer, ChangePasswordSerializer,
|
||||||
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer,
|
GoogleLoginSerializer, UpdateGuardianImageSerializer, UpdateJuniorProfileImageSerializer,
|
||||||
DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer,
|
DefaultTaskImagesSerializer, DefaultTaskImagesDetailsSerializer, UserDeleteSerializer,
|
||||||
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer)
|
UserNotificationSerializer, UpdateUserNotificationSerializer, UserPhoneOtpSerializer,
|
||||||
|
AdminLoginSerializer, ForceUpdateSerializer)
|
||||||
from rest_framework_simplejwt.tokens import RefreshToken
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
from base.constants import NUMBER, ZOD, JUN, GRD
|
from base.constants import NUMBER, ZOD, JUN, GRD, USER_TYPE_FLAG
|
||||||
from guardian.tasks import generate_otp
|
from guardian.tasks import generate_otp
|
||||||
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
|
from account.utils import (send_otp_email, send_support_email, custom_response, custom_error_response,
|
||||||
generate_code)
|
generate_code, OTP_EXPIRY, user_device_details, send_all_email)
|
||||||
from junior.serializers import JuniorProfileSerializer
|
from junior.serializers import JuniorProfileSerializer
|
||||||
from guardian.serializers import GuardianProfileSerializer
|
from guardian.serializers import GuardianProfileSerializer
|
||||||
|
|
||||||
class GoogleLoginMixin:
|
class GoogleLoginMixin(object):
|
||||||
"""google login mixin"""
|
"""google login mixin"""
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def google_login(self, request):
|
def google_login(request):
|
||||||
"""google login function"""
|
"""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.META.get('HTTP_USER_TYPE')
|
||||||
|
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||||
if not access_token:
|
if not access_token:
|
||||||
return Response({'error': 'Access token is required.'}, status=status.HTTP_400_BAD_REQUEST)
|
return Response({'error': 'Access token is required.'}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
@@ -77,14 +85,43 @@ class GoogleLoginMixin:
|
|||||||
if user_data.exists():
|
if user_data.exists():
|
||||||
if str(user_type) == '1':
|
if str(user_type) == '1':
|
||||||
junior_query = Junior.objects.filter(auth=user_data.last()).last()
|
junior_query = Junior.objects.filter(auth=user_data.last()).last()
|
||||||
|
if not junior_query:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2071"],
|
||||||
|
response_status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
if not junior_query.is_active:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2075"],
|
||||||
|
response_status=status.HTTP_404_NOT_FOUND
|
||||||
|
)
|
||||||
serializer = JuniorSerializer(junior_query)
|
serializer = JuniorSerializer(junior_query)
|
||||||
if str(user_type) == '2':
|
elif str(user_type) == '2':
|
||||||
guardian_query = Guardian.objects.filter(user=user_data.last()).last()
|
guardian_query = Guardian.objects.filter(user=user_data.last()).last()
|
||||||
|
if not guardian_query:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2070"],
|
||||||
|
response_status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
if not guardian_query.is_active:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2075"],
|
||||||
|
response_status=status.HTTP_404_NOT_FOUND
|
||||||
|
)
|
||||||
serializer = GuardianSerializer(guardian_query)
|
serializer = GuardianSerializer(guardian_query)
|
||||||
|
else:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2069"],
|
||||||
|
response_status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
device_detail, created = UserDeviceDetails.objects.get_or_create(user=user_data.last())
|
||||||
|
if device_detail:
|
||||||
|
device_detail.device_id = device_id
|
||||||
|
device_detail.save()
|
||||||
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)
|
||||||
|
|
||||||
if not User.objects.filter(email__iexact=email).exists():
|
else:
|
||||||
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,
|
||||||
@@ -92,14 +129,26 @@ class GoogleLoginMixin:
|
|||||||
junior_code=generate_code(JUN, user_obj.id),
|
junior_code=generate_code(JUN, user_obj.id),
|
||||||
referral_code=generate_code(ZOD, user_obj.id)
|
referral_code=generate_code(ZOD, user_obj.id)
|
||||||
)
|
)
|
||||||
serializer = JuniorSerializer(junior_query)
|
serializer = JuniorSerializer(junior_query, context={'sign_up': True})
|
||||||
if str(user_type) == '2':
|
position = Junior.objects.all().count()
|
||||||
|
JuniorPoints.objects.create(junior=junior_query, position=position)
|
||||||
|
elif str(user_type) == '2':
|
||||||
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
|
guardian_query = Guardian.objects.create(user=user_obj, is_verified=True, is_active=True,
|
||||||
image=profile_picture,signup_method='2',
|
image=profile_picture,signup_method='2',
|
||||||
guardian_code=generate_code(GRD, user_obj.id),
|
guardian_code=generate_code(GRD, user_obj.id),
|
||||||
referral_code=generate_code(ZOD, user_obj.id)
|
referral_code=generate_code(ZOD, user_obj.id)
|
||||||
)
|
)
|
||||||
serializer = GuardianSerializer(guardian_query)
|
serializer = GuardianSerializer(guardian_query, context={'sign_up': True})
|
||||||
|
else:
|
||||||
|
user_obj.delete()
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2069"],
|
||||||
|
response_status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
device_detail, created = UserDeviceDetails.objects.get_or_create(user=user_obj)
|
||||||
|
if device_detail:
|
||||||
|
device_detail.device_id = device_id
|
||||||
|
device_detail.save()
|
||||||
# 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,
|
||||||
response_status=status.HTTP_200_OK)
|
response_status=status.HTTP_200_OK)
|
||||||
@@ -110,28 +159,57 @@ class GoogleLoginViewSet(GoogleLoginMixin, viewsets.GenericViewSet):
|
|||||||
serializer_class = GoogleLoginSerializer
|
serializer_class = GoogleLoginSerializer
|
||||||
|
|
||||||
def create(self, request):
|
def create(self, request):
|
||||||
"""create method"""
|
"""Payload
|
||||||
|
{
|
||||||
|
"access_token",
|
||||||
|
"user_type": "1"
|
||||||
|
}"""
|
||||||
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)
|
||||||
|
|
||||||
class SigninWithApple(views.APIView):
|
class SigninWithApple(views.APIView):
|
||||||
"""This API is for sign in with Apple for app."""
|
"""This API is for sign in with Apple for app.
|
||||||
|
Payload
|
||||||
|
{
|
||||||
|
"access_token",
|
||||||
|
"user_type": "1"
|
||||||
|
}"""
|
||||||
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.META.get('HTTP_USER_TYPE')
|
||||||
|
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||||
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}
|
||||||
if decoded_data.get("email"):
|
if decoded_data.get("email"):
|
||||||
try:
|
try:
|
||||||
user = User.objects.get(email=decoded_data.get("email"))
|
user = User.objects.get(email__iexact=decoded_data.get("email"))
|
||||||
if str(user_type) == '1':
|
if str(user_type) == '1':
|
||||||
junior_query = Junior.objects.filter(auth=user).last()
|
junior_data = Junior.objects.filter(auth=user).last()
|
||||||
serializer = JuniorSerializer(junior_query)
|
if not junior_data:
|
||||||
if str(user_type) == '2':
|
return custom_error_response(
|
||||||
guardian_query = Guardian.objects.filter(user=user).last()
|
ERROR_CODE["2071"],
|
||||||
serializer = GuardianSerializer(guardian_query)
|
response_status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
serializer = JuniorSerializer(junior_data)
|
||||||
|
elif str(user_type) == '2':
|
||||||
|
guardian_data = Guardian.objects.filter(user=user).last()
|
||||||
|
if not guardian_data:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2070"],
|
||||||
|
response_status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
serializer = GuardianSerializer(guardian_data)
|
||||||
|
else:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2069"],
|
||||||
|
response_status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
device_detail, created = UserDeviceDetails.objects.get_or_create(user=user)
|
||||||
|
if device_detail:
|
||||||
|
device_detail.device_id = device_id
|
||||||
|
device_detail.save()
|
||||||
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)
|
||||||
|
|
||||||
@@ -142,13 +220,25 @@ class SigninWithApple(views.APIView):
|
|||||||
signup_method='3',
|
signup_method='3',
|
||||||
junior_code=generate_code(JUN, user.id),
|
junior_code=generate_code(JUN, user.id),
|
||||||
referral_code=generate_code(ZOD, user.id))
|
referral_code=generate_code(ZOD, user.id))
|
||||||
serializer = JuniorSerializer(junior_query)
|
serializer = JuniorSerializer(junior_query, context={'sign_up': True})
|
||||||
if str(user_type) == '2':
|
position = Junior.objects.all().count()
|
||||||
|
JuniorPoints.objects.create(junior=junior_query, position=position)
|
||||||
|
elif str(user_type) == '2':
|
||||||
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
|
guardian_query = Guardian.objects.create(user=user, is_verified=True, is_active=True,
|
||||||
signup_method='3',
|
signup_method='3',
|
||||||
guardian_code=generate_code(GRD, user.id),
|
guardian_code=generate_code(GRD, user.id),
|
||||||
referral_code=generate_code(ZOD, user.id))
|
referral_code=generate_code(ZOD, user.id))
|
||||||
serializer = GuardianSerializer(guardian_query)
|
serializer = GuardianSerializer(guardian_query, context={'sign_up': True})
|
||||||
|
else:
|
||||||
|
user.delete()
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2069"],
|
||||||
|
response_status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
device_detail, created = UserDeviceDetails.objects.get_or_create(user=user)
|
||||||
|
if device_detail:
|
||||||
|
device_detail.device_id = device_id
|
||||||
|
device_detail.save()
|
||||||
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)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -184,18 +274,42 @@ class UpdateProfileImage(views.APIView):
|
|||||||
return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(ERROR_CODE['2036'],response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
class ChangePasswordAPIView(views.APIView):
|
class ChangePasswordAPIView(views.APIView):
|
||||||
"""change password"""
|
"""
|
||||||
|
change password"
|
||||||
|
"""
|
||||||
serializer_class = ChangePasswordSerializer
|
serializer_class = ChangePasswordSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
serializer = ChangePasswordSerializer(context=request.user, data=request.data)
|
"""
|
||||||
|
POST request to change current login user password
|
||||||
|
Payload
|
||||||
|
{ "current_password":"Demo@123",
|
||||||
|
"new_password":"Demo@123"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
serializer = ChangePasswordSerializer(
|
||||||
|
context=request.user,
|
||||||
|
data=request.data
|
||||||
|
)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return custom_response(SUCCESS_CODE['3007'], response_status=status.HTTP_200_OK)
|
return custom_response(
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
SUCCESS_CODE['3007'],
|
||||||
|
response_status=status.HTTP_200_OK
|
||||||
|
)
|
||||||
|
return custom_error_response(
|
||||||
|
serializer.errors,
|
||||||
|
response_status=status.HTTP_400_BAD_REQUEST
|
||||||
|
)
|
||||||
|
|
||||||
class ResetPasswordAPIView(views.APIView):
|
class ResetPasswordAPIView(views.APIView):
|
||||||
"""Reset password"""
|
"""Reset password
|
||||||
|
Payload
|
||||||
|
{
|
||||||
|
"verification_code":"373770",
|
||||||
|
"password":"Demo@1323"
|
||||||
|
}"""
|
||||||
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():
|
||||||
@@ -204,44 +318,46 @@ class ResetPasswordAPIView(views.APIView):
|
|||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
class ForgotPasswordAPIView(views.APIView):
|
class ForgotPasswordAPIView(views.APIView):
|
||||||
"""Forgot password"""
|
"""
|
||||||
|
Forgot password
|
||||||
|
"""
|
||||||
|
serializer_class = ForgotPasswordSerializer
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
serializer = ForgotPasswordSerializer(data=request.data)
|
|
||||||
if serializer.is_valid():
|
"""
|
||||||
email = serializer.validated_data['email']
|
Payload
|
||||||
try:
|
{
|
||||||
User.objects.get(email=email)
|
"email": "[email protected]"
|
||||||
except User.DoesNotExist:
|
}
|
||||||
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)])
|
serializer = self.serializer_class(data=request.data)
|
||||||
# Send the verification code to the user's email
|
serializer.is_valid(raise_exception=True)
|
||||||
from_email = settings.EMAIL_FROM_ADDRESS
|
email = serializer.validated_data['email']
|
||||||
recipient_list = [email]
|
# generate otp
|
||||||
send_templated_mail(
|
verification_code = generate_otp()
|
||||||
template_name='email_reset_verification.email',
|
# Send the verification code to the user's email
|
||||||
from_email=from_email,
|
send_all_email.delay(
|
||||||
recipient_list=recipient_list,
|
'email_reset_verification.email', email, verification_code
|
||||||
context={
|
)
|
||||||
'verification_code': verification_code
|
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||||
}
|
user_data, created = UserEmailOtp.objects.get_or_create(
|
||||||
)
|
email=email
|
||||||
expiry = timezone.now() + timezone.timedelta(days=1)
|
)
|
||||||
user_data, created = UserEmailOtp.objects.get_or_create(email=email)
|
if created:
|
||||||
if created:
|
user_data.expired_at = expiry
|
||||||
user_data.expired_at = expiry
|
user_data.save()
|
||||||
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.expired_at = expiry
|
user_data.save()
|
||||||
user_data.save()
|
return custom_response(
|
||||||
return custom_response(SUCCESS_CODE['3015'],
|
SUCCESS_CODE['3015'],
|
||||||
response_status=status.HTTP_200_OK)
|
response_status=status.HTTP_200_OK
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
)
|
||||||
|
|
||||||
class SendPhoneOtp(viewsets.ModelViewSet):
|
class SendPhoneOtp(viewsets.ModelViewSet):
|
||||||
"""Send otp on phone"""
|
"""Send otp on phone"""
|
||||||
queryset = UserPhoneOtp.objects.all()
|
|
||||||
serializer_class = UserPhoneOtpSerializer
|
serializer_class = UserPhoneOtpSerializer
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
otp = generate_otp()
|
otp = generate_otp()
|
||||||
@@ -258,7 +374,6 @@ class SendPhoneOtp(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
class UserPhoneVerification(viewsets.ModelViewSet):
|
class UserPhoneVerification(viewsets.ModelViewSet):
|
||||||
"""Send otp on phone"""
|
"""Send otp on phone"""
|
||||||
queryset = UserPhoneOtp.objects.all()
|
|
||||||
serializer_class = UserPhoneOtpSerializer
|
serializer_class = UserPhoneOtpSerializer
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
@@ -274,25 +389,50 @@ class UserPhoneVerification(viewsets.ModelViewSet):
|
|||||||
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(ERROR_CODE["2008"], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserLogin(viewsets.ViewSet):
|
class UserLogin(viewsets.ViewSet):
|
||||||
"""User login"""
|
"""User login"""
|
||||||
@action(methods=['post'], detail=False)
|
@action(methods=['post'], detail=False)
|
||||||
def login(self, request):
|
def login(self, request):
|
||||||
username = request.data.get('username')
|
username = request.data.get('username')
|
||||||
password = request.data.get('password')
|
password = request.data.get('password')
|
||||||
|
user_type = request.META.get('HTTP_USER_TYPE')
|
||||||
device_id = request.META.get('HTTP_DEVICE_ID')
|
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||||
user = authenticate(request, username=username, password=password)
|
user = authenticate(request, username=username, password=password)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
if user is not None:
|
if user is not None:
|
||||||
login(request, user)
|
login(request, user)
|
||||||
guardian_data = Guardian.objects.filter(user__username=username, is_verified=True).last()
|
if str(user_type) == USER_TYPE_FLAG["TWO"]:
|
||||||
if guardian_data:
|
guardian_data = Guardian.objects.filter(user__username=username).last()
|
||||||
serializer = GuardianSerializer(guardian_data).data
|
if guardian_data:
|
||||||
junior_data = Junior.objects.filter(auth__username=username, is_verified=True).last()
|
if guardian_data.is_verified:
|
||||||
if junior_data:
|
serializer = GuardianSerializer(
|
||||||
serializer = JuniorSerializer(junior_data).data
|
guardian_data, context={'user_type': user_type}
|
||||||
|
).data
|
||||||
|
else:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2070"],
|
||||||
|
response_status=status.HTTP_401_UNAUTHORIZED
|
||||||
|
)
|
||||||
|
elif str(user_type) == USER_TYPE_FLAG["FIRST"]:
|
||||||
|
junior_data = Junior.objects.filter(auth__username=username).last()
|
||||||
|
if junior_data:
|
||||||
|
if junior_data.is_verified:
|
||||||
|
serializer = JuniorSerializer(
|
||||||
|
junior_data, context={'user_type': user_type}
|
||||||
|
).data
|
||||||
|
else:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2071"],
|
||||||
|
response_status=status.HTTP_401_UNAUTHORIZED
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
return custom_error_response(
|
||||||
|
ERROR_CODE["2069"],
|
||||||
|
response_status=status.HTTP_401_UNAUTHORIZED
|
||||||
|
)
|
||||||
|
# storing device id in using celery task so the time would be reduced
|
||||||
|
# user_device_details.delay(user.id, device_id)
|
||||||
device_details, created = UserDeviceDetails.objects.get_or_create(user=user)
|
device_details, created = UserDeviceDetails.objects.get_or_create(user=user)
|
||||||
if device_details:
|
if device_details:
|
||||||
device_details.device_id = device_id
|
device_details.device_id = device_id
|
||||||
@@ -306,9 +446,12 @@ class UserLogin(viewsets.ViewSet):
|
|||||||
refresh = RefreshToken.for_user(user)
|
refresh = RefreshToken.for_user(user)
|
||||||
access_token = str(refresh.access_token)
|
access_token = str(refresh.access_token)
|
||||||
refresh_token = str(refresh)
|
refresh_token = str(refresh)
|
||||||
data = {"auth_token":access_token, "refresh_token":refresh_token, "is_profile_complete": False,
|
data = {
|
||||||
"user_type": email_verified.user_type,
|
"auth_token":access_token,
|
||||||
}
|
"refresh_token":refresh_token,
|
||||||
|
"is_profile_complete": False,
|
||||||
|
"user_type": user_type,
|
||||||
|
}
|
||||||
is_verified = False
|
is_verified = False
|
||||||
if email_verified:
|
if email_verified:
|
||||||
is_verified = email_verified.is_verified
|
is_verified = email_verified.is_verified
|
||||||
@@ -317,43 +460,73 @@ class UserLogin(viewsets.ViewSet):
|
|||||||
email_verified.otp = otp
|
email_verified.otp = otp
|
||||||
email_verified.save()
|
email_verified.save()
|
||||||
data.update({"email_otp":otp})
|
data.update({"email_otp":otp})
|
||||||
send_otp_email(username, otp)
|
send_otp_email.delay(username, otp)
|
||||||
return custom_response(ERROR_CODE['2024'], {"email_otp": otp, "is_email_verified": is_verified},
|
return custom_response(
|
||||||
response_status=status.HTTP_200_OK)
|
ERROR_CODE['2024'],
|
||||||
|
{"email_otp": otp, "is_email_verified": is_verified},
|
||||||
|
response_status=status.HTTP_200_OK
|
||||||
|
)
|
||||||
data.update({"is_email_verified": is_verified})
|
data.update({"is_email_verified": is_verified})
|
||||||
return custom_response(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):
|
||||||
username = request.data.get('username')
|
email = request.data.get('email')
|
||||||
password = request.data.get('password')
|
password = request.data.get('password')
|
||||||
user = authenticate(request, username=username, password=password)
|
user = User.objects.filter(email__iexact=email, is_superuser=True
|
||||||
try:
|
).only('id', 'first_name', 'last_name', 'email',
|
||||||
if user is not None:
|
'username', 'is_active', 'is_superuser').first()
|
||||||
login(request, user)
|
|
||||||
if user.is_superuser:
|
if not user or not user.check_password(password):
|
||||||
serializer = SuperUserSerializer(user)
|
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
|
|
||||||
else:
|
serializer = SuperUserSerializer(user)
|
||||||
return custom_error_response(ERROR_CODE["2002"], response_status=status.HTTP_401_UNAUTHORIZED)
|
return custom_response(SUCCESS_CODE['3003'], serializer.data, response_status=status.HTTP_200_OK)
|
||||||
except Exception as e:
|
|
||||||
logging.error(e)
|
|
||||||
refresh = RefreshToken.for_user(user)
|
class AdminLoginViewSet(viewsets.GenericViewSet):
|
||||||
access_token = str(refresh.access_token)
|
"""
|
||||||
refresh_token = str(refresh)
|
admin login api
|
||||||
data = {"auth_token": access_token, "refresh_token":refresh_token, "user_type": '3'}
|
"""
|
||||||
return custom_response(None, data, response_status=status.HTTP_200_OK)
|
serializer_class = AdminLoginSerializer
|
||||||
|
|
||||||
|
@action(methods=['post'], url_name='login', url_path='login', detail=False)
|
||||||
|
def admin_login(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
:param request:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
serializer = self.serializer_class(data=request.data)
|
||||||
|
serializer.is_valid(raise_exception=True)
|
||||||
|
user = serializer.save()
|
||||||
|
refresh = RefreshToken.for_user(user)
|
||||||
|
access_token = str(refresh.access_token)
|
||||||
|
refresh_token = str(refresh)
|
||||||
|
data = {"auth_token": access_token, "refresh_token": refresh_token, "username": user.username,
|
||||||
|
"email": user.email, "first_name": user.first_name, "last_name": user.last_name,
|
||||||
|
"is_active": user.is_active, "user_type": '3', "is_superuser": user.is_superuser}
|
||||||
|
return custom_response(None, data)
|
||||||
|
|
||||||
|
|
||||||
class UserEmailVerification(viewsets.ModelViewSet):
|
class UserEmailVerification(viewsets.ModelViewSet):
|
||||||
"""User Email verification"""
|
"""User Email verification
|
||||||
|
Payload
|
||||||
|
{
|
||||||
|
"email":"[email protected]",
|
||||||
|
"otp":"361123"
|
||||||
|
}"""
|
||||||
serializer_class = EmailVerificationSerializer
|
serializer_class = EmailVerificationSerializer
|
||||||
queryset = UserEmailOtp.objects.all()
|
http_method_names = ('post',)
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
try:
|
try:
|
||||||
user_obj = User.objects.filter(username=self.request.GET.get('email')).last()
|
user_obj = User.objects.filter(username=self.request.data.get('email')).last()
|
||||||
email_data = UserEmailOtp.objects.filter(email=self.request.GET.get('email'),
|
email_data = UserEmailOtp.objects.filter(email=self.request.data.get('email'),
|
||||||
otp=self.request.GET.get('otp')).last()
|
otp=self.request.data.get('otp')).last()
|
||||||
if email_data:
|
if email_data:
|
||||||
input_datetime_str = str(email_data.expired_at)
|
input_datetime_str = str(email_data.expired_at)
|
||||||
input_format = "%Y-%m-%d %H:%M:%S.%f%z"
|
input_format = "%Y-%m-%d %H:%M:%S.%f%z"
|
||||||
@@ -367,12 +540,12 @@ class UserEmailVerification(viewsets.ModelViewSet):
|
|||||||
email_data.is_verified = True
|
email_data.is_verified = True
|
||||||
email_data.save()
|
email_data.save()
|
||||||
if email_data.user_type == '1':
|
if email_data.user_type == '1':
|
||||||
junior_data = Junior.objects.filter(auth__email=self.request.GET.get('email')).last()
|
junior_data = Junior.objects.filter(auth__email=self.request.data.get('email')).last()
|
||||||
if junior_data:
|
if junior_data:
|
||||||
junior_data.is_verified = True
|
junior_data.is_verified = True
|
||||||
junior_data.save()
|
junior_data.save()
|
||||||
else:
|
else:
|
||||||
guardian_data = Guardian.objects.filter(user__email=self.request.GET.get('email')).last()
|
guardian_data = Guardian.objects.filter(user__email=self.request.data.get('email')).last()
|
||||||
if guardian_data:
|
if guardian_data:
|
||||||
guardian_data.is_verified = True
|
guardian_data.is_verified = True
|
||||||
guardian_data.save()
|
guardian_data.save()
|
||||||
@@ -390,11 +563,12 @@ class UserEmailVerification(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
class ReSendEmailOtp(viewsets.ModelViewSet):
|
class ReSendEmailOtp(viewsets.ModelViewSet):
|
||||||
"""Send otp on phone"""
|
"""Send otp on phone"""
|
||||||
queryset = UserEmailOtp.objects.all()
|
|
||||||
serializer_class = EmailVerificationSerializer
|
serializer_class = EmailVerificationSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
http_method_names = ('post',)
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
|
"""Param
|
||||||
|
{"email":"[email protected]"}
|
||||||
|
"""
|
||||||
otp = generate_otp()
|
otp = generate_otp()
|
||||||
if User.objects.filter(email=request.data['email']):
|
if User.objects.filter(email=request.data['email']):
|
||||||
expiry = timezone.now() + timezone.timedelta(days=1)
|
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||||
@@ -406,36 +580,39 @@ class ReSendEmailOtp(viewsets.ModelViewSet):
|
|||||||
email_data.otp = otp
|
email_data.otp = otp
|
||||||
email_data.expired_at = expiry
|
email_data.expired_at = expiry
|
||||||
email_data.save()
|
email_data.save()
|
||||||
send_otp_email(request.data['email'], otp)
|
send_otp_email.delay(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):
|
class ProfileAPIViewSet(viewsets.ModelViewSet):
|
||||||
"""Profile viewset"""
|
"""Profile viewset"""
|
||||||
queryset = User.objects.all()
|
|
||||||
serializer_class = JuniorProfileSerializer
|
serializer_class = JuniorProfileSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('get',)
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
"""profile view"""
|
"""profile view
|
||||||
if str(self.request.GET.get('user_type')) == '1':
|
Params
|
||||||
|
user_type"""
|
||||||
|
user_type = request.META.get('HTTP_USER_TYPE')
|
||||||
|
if str(user_type) == '1':
|
||||||
junior_data = Junior.objects.filter(auth=self.request.user).last()
|
junior_data = Junior.objects.filter(auth=self.request.user).last()
|
||||||
if junior_data:
|
if junior_data:
|
||||||
serializer = JuniorProfileSerializer(junior_data)
|
serializer = JuniorProfileSerializer(junior_data)
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||||
elif str(self.request.GET.get('user_type')) == '2':
|
elif str(user_type) == '2':
|
||||||
guardian_data = Guardian.objects.filter(user=self.request.user).last()
|
guardian_data = Guardian.objects.filter(user=self.request.user).last()
|
||||||
if guardian_data:
|
if guardian_data:
|
||||||
serializer = GuardianProfileSerializer(guardian_data)
|
serializer = GuardianProfileSerializer(guardian_data)
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
return custom_error_response(None, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
class UploadImageAPIViewSet(viewsets.ModelViewSet):
|
class UploadImageAPIViewSet(viewsets.ModelViewSet):
|
||||||
"""Profile viewset"""
|
"""upload task image"""
|
||||||
queryset = DefaultTaskImages.objects.all()
|
|
||||||
serializer_class = DefaultTaskImagesSerializer
|
serializer_class = DefaultTaskImagesSerializer
|
||||||
|
http_method_names = ('post',)
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""profile view"""
|
"""upload images"""
|
||||||
image_data = request.data['image_url']
|
image_data = request.data['image_url']
|
||||||
filename = f"default_task_images/{image_data.name}"
|
filename = f"default_task_images/{image_data.name}"
|
||||||
if image_data.size == NUMBER['zero']:
|
if image_data.size == NUMBER['zero']:
|
||||||
@@ -451,9 +628,9 @@ class UploadImageAPIViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
class DefaultImageAPIViewSet(viewsets.ModelViewSet):
|
class DefaultImageAPIViewSet(viewsets.ModelViewSet):
|
||||||
"""Profile viewset"""
|
"""Profile viewset"""
|
||||||
queryset = DefaultTaskImages.objects.all()
|
|
||||||
serializer_class = DefaultTaskImagesDetailsSerializer
|
serializer_class = DefaultTaskImagesDetailsSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('get',)
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
"""profile view"""
|
"""profile view"""
|
||||||
queryset = DefaultTaskImages.objects.all()
|
queryset = DefaultTaskImages.objects.all()
|
||||||
@@ -462,7 +639,13 @@ class DefaultImageAPIViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
|
class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
|
||||||
""" Delete user API view set """
|
""" Delete user API view set
|
||||||
|
{"user_type":1,
|
||||||
|
"signup_method":"1",
|
||||||
|
"password":"Demo@123"}
|
||||||
|
signup_method 1 for manual
|
||||||
|
2 for google login
|
||||||
|
3 for apple login"""
|
||||||
|
|
||||||
@action(detail=False, methods=['POST'], url_path='user-account',serializer_class=UserDeleteSerializer,
|
@action(detail=False, methods=['POST'], url_path='user-account',serializer_class=UserDeleteSerializer,
|
||||||
permission_classes=[IsAuthenticated])
|
permission_classes=[IsAuthenticated])
|
||||||
@@ -482,24 +665,28 @@ class DeleteUserProfileAPIViewSet(viewsets.GenericViewSet):
|
|||||||
|
|
||||||
class UserNotificationAPIViewSet(viewsets.ModelViewSet):
|
class UserNotificationAPIViewSet(viewsets.ModelViewSet):
|
||||||
"""notification viewset"""
|
"""notification viewset"""
|
||||||
queryset = UserNotification.objects.all()
|
|
||||||
serializer_class = UserNotificationSerializer
|
serializer_class = UserNotificationSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('get',)
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
"""profile view"""
|
"""notification view"""
|
||||||
queryset = self.queryset.filter(user=request.user)
|
queryset = UserNotification.objects.filter(user=request.user)
|
||||||
serializer = UserNotificationSerializer(queryset, many=True)
|
serializer = UserNotificationSerializer(queryset, many=True)
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
|
class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
|
||||||
"""Update notification viewset"""
|
"""Update notification viewset"""
|
||||||
queryset = UserNotification.objects.all()
|
|
||||||
serializer_class = UpdateUserNotificationSerializer
|
serializer_class = UpdateUserNotificationSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('post',)
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""profile view"""
|
"""Payload
|
||||||
|
{"email_notification": false,
|
||||||
|
"sms_notification": false,
|
||||||
|
"push_notification": false}
|
||||||
|
"""
|
||||||
serializer = UpdateUserNotificationSerializer(data=request.data,
|
serializer = UpdateUserNotificationSerializer(data=request.data,
|
||||||
context=request.user)
|
context=request.user)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
@@ -509,17 +696,21 @@ class UpdateUserNotificationAPIViewSet(viewsets.ModelViewSet):
|
|||||||
|
|
||||||
|
|
||||||
class SendSupportEmail(views.APIView):
|
class SendSupportEmail(views.APIView):
|
||||||
"""support email api"""
|
"""support email api
|
||||||
|
payload
|
||||||
|
name
|
||||||
|
email
|
||||||
|
message
|
||||||
|
"""
|
||||||
permission_classes = (IsAuthenticated,)
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
name = request.data.get('name')
|
name = request.data.get('name')
|
||||||
sender = request.data.get('email')
|
sender = request.data.get('email')
|
||||||
subject = request.data.get('subject')
|
|
||||||
message = request.data.get('message')
|
message = request.data.get('message')
|
||||||
if name and sender and subject and message:
|
if name and sender and message:
|
||||||
try:
|
try:
|
||||||
send_support_email(name, sender, subject, message)
|
send_support_email(name, sender, message)
|
||||||
return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3019'], response_status=status.HTTP_200_OK)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
@@ -531,6 +722,10 @@ class LogoutAPIView(views.APIView):
|
|||||||
permission_classes = (IsAuthenticated,)
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
def post(self, request):
|
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)
|
logout(request)
|
||||||
request.session.flush()
|
request.session.flush()
|
||||||
return custom_response(SUCCESS_CODE['3020'], response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3020'], response_status=status.HTTP_200_OK)
|
||||||
@@ -549,3 +744,27 @@ class AccessTokenAPIView(views.APIView):
|
|||||||
data = {"auth_token": access_token}
|
data = {"auth_token": access_token}
|
||||||
return custom_response(None, data, response_status=status.HTTP_200_OK)
|
return custom_response(None, data, response_status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
|
class ForceUpdateViewSet(GenericViewSet, mixins.CreateModelMixin):
|
||||||
|
"""FAQ view set"""
|
||||||
|
|
||||||
|
serializer_class = ForceUpdateSerializer
|
||||||
|
http_method_names = ['post']
|
||||||
|
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
faq create api method
|
||||||
|
:param request:
|
||||||
|
:param args: version, device type
|
||||||
|
:param kwargs:
|
||||||
|
:return: success message
|
||||||
|
"""
|
||||||
|
if ForceUpdate.objects.all().count() >= 2:
|
||||||
|
return custom_error_response(ERROR_CODE['2080'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
obj_data = [ForceUpdate(**item) for item in request.data]
|
||||||
|
try:
|
||||||
|
ForceUpdate.objects.bulk_create(obj_data)
|
||||||
|
return custom_response(SUCCESS_CODE["3046"], response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|||||||
+50
-35
@@ -21,34 +21,13 @@ GRD = 'GRD'
|
|||||||
NUMBER = {
|
NUMBER = {
|
||||||
'point_zero': 0.0, 'zero': 0, 'one': 1, 'two': 2, 'three': 3, 'four': 4, 'five': 5, 'six': 6, 'seven': 7,
|
'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,
|
'eight': 8, 'nine': 9, 'ten': 10, 'eleven': 11, 'twelve': 12, 'thirteen': 13, 'fourteen': 14, 'fifteen': 15,
|
||||||
'sixteen': 16, 'seventeen': 17, 'eighteen': 18, 'nineteen': 19,
|
'sixteen': 16, 'seventeen': 17, 'eighteen': 18, 'nineteen': 19, 'twenty': 20,
|
||||||
'twenty_four': 24,
|
'twenty_one': 21, 'twenty_two': 22,'twenty_three': 23, 'twenty_four': 24, 'twenty_five': 25,
|
||||||
'twenty_one': 21,
|
'thirty': 30, 'forty': 40, 'fifty': 50, 'sixty': 60, 'seventy': 70, 'eighty': 80, 'ninty': 90,
|
||||||
'twenty_two': 22,
|
'ninety_nine': 99, 'hundred': 100, 'thirty_six_hundred': 3600
|
||||||
'twenty_five': 25,
|
|
||||||
'thirty': 30,
|
|
||||||
'thirty_five': 35,
|
|
||||||
'thirty_six': 36,
|
|
||||||
'forty': 40,
|
|
||||||
'fifty': 50,
|
|
||||||
'fifty_nine': 59,
|
|
||||||
'sixty': 60,
|
|
||||||
'seventy_five': 75,
|
|
||||||
'eighty': 80,
|
|
||||||
'ninty_five': 95,
|
|
||||||
'ninty_six': 96,
|
|
||||||
'ninety_nine': 99,
|
|
||||||
'hundred': 100,
|
|
||||||
'one_one_nine': 119,
|
|
||||||
'one_twenty': 120,
|
|
||||||
'four_zero_four': 404,
|
|
||||||
'five_hundred': 500,
|
|
||||||
'minus_one': -1,
|
|
||||||
'point_three': 0.3,
|
|
||||||
'point_seven': 0.7
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
none = "none"
|
||||||
|
|
||||||
# Super Admin string constant for 'role'
|
# Super Admin string constant for 'role'
|
||||||
SUPER_ADMIN = "Super Admin"
|
SUPER_ADMIN = "Super Admin"
|
||||||
@@ -64,37 +43,57 @@ 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
|
||||||
|
# guardian code status tuple
|
||||||
sort_dict = {
|
guardian_code_tuple = ('1','3')
|
||||||
'1': 'name',
|
|
||||||
'2': '-name'
|
|
||||||
}
|
|
||||||
"""user type"""
|
"""user type"""
|
||||||
USER_TYPE = (
|
USER_TYPE = (
|
||||||
('1', 'junior'),
|
('1', 'junior'),
|
||||||
('2', 'guardian'),
|
('2', 'guardian'),
|
||||||
('3', 'superuser')
|
('3', 'superuser')
|
||||||
)
|
)
|
||||||
|
DEVICE_TYPE = (
|
||||||
|
('1', 'android'),
|
||||||
|
('2', 'ios')
|
||||||
|
)
|
||||||
|
USER_TYPE_FLAG = {
|
||||||
|
"FIRST" : "1",
|
||||||
|
"TWO" : "2",
|
||||||
|
"THREE": "3"
|
||||||
|
}
|
||||||
|
|
||||||
"""gender"""
|
"""gender"""
|
||||||
GENDERS = (
|
GENDERS = (
|
||||||
('1', 'Male'),
|
('1', 'Male'),
|
||||||
('2', 'Female')
|
('2', 'Female')
|
||||||
)
|
)
|
||||||
"""Task status"""
|
# Task status"""
|
||||||
TASK_STATUS = (
|
TASK_STATUS = (
|
||||||
('1', 'pending'),
|
('1', 'pending'),
|
||||||
('2', 'in-progress'),
|
('2', 'in-progress'),
|
||||||
('3', 'rejected'),
|
('3', 'rejected'),
|
||||||
('4', 'requested'),
|
('4', 'requested'),
|
||||||
('5', 'completed')
|
('5', 'completed'),
|
||||||
|
('6', 'expired')
|
||||||
)
|
)
|
||||||
"""sign up method"""
|
# sign up method
|
||||||
SIGNUP_METHODS = (
|
SIGNUP_METHODS = (
|
||||||
('1', 'manual'),
|
('1', 'manual'),
|
||||||
('2', 'google'),
|
('2', 'google'),
|
||||||
('3', 'apple')
|
('3', 'apple')
|
||||||
)
|
)
|
||||||
"""relationship"""
|
# guardian code status
|
||||||
|
GUARDIAN_CODE_STATUS = (
|
||||||
|
('1', 'no guardian code'),
|
||||||
|
('2', 'exist guardian code'),
|
||||||
|
('3', 'request for guardian code')
|
||||||
|
)
|
||||||
|
# article status
|
||||||
|
ARTICLE_STATUS = (
|
||||||
|
('1', 'read'),
|
||||||
|
('2', 'in_progress'),
|
||||||
|
('3', 'completed')
|
||||||
|
)
|
||||||
|
# relationship
|
||||||
RELATIONSHIP = (
|
RELATIONSHIP = (
|
||||||
('1', 'parent'),
|
('1', 'parent'),
|
||||||
('2', 'legal_guardian')
|
('2', 'legal_guardian')
|
||||||
@@ -107,9 +106,11 @@ IN_PROGRESS = 2
|
|||||||
REJECTED = 3
|
REJECTED = 3
|
||||||
REQUESTED = 4
|
REQUESTED = 4
|
||||||
COMPLETED = 5
|
COMPLETED = 5
|
||||||
|
EXPIRED = 6
|
||||||
TASK_POINTS = 5
|
TASK_POINTS = 5
|
||||||
# duplicate name used defined in constant PROJECT_NAME
|
# duplicate name used defined in constant PROJECT_NAME
|
||||||
PROJECT_NAME = 'Zod Bank'
|
PROJECT_NAME = 'Zod Bank'
|
||||||
|
# define user type constant
|
||||||
GUARDIAN = 'guardian'
|
GUARDIAN = 'guardian'
|
||||||
JUNIOR = 'junior'
|
JUNIOR = 'junior'
|
||||||
SUPERUSER = 'superuser'
|
SUPERUSER = 'superuser'
|
||||||
@@ -120,3 +121,17 @@ 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
|
||||||
|
|
||||||
|
# already register
|
||||||
|
Already_register_user = "duplicate key value violates unique constraint"
|
||||||
|
|
||||||
|
ARTICLE_CARD_IMAGE_FOLDER = 'article-card-images'
|
||||||
|
|
||||||
|
DATE_FORMAT = '%Y-%m-%d'
|
||||||
|
|||||||
+105
-21
@@ -23,15 +23,15 @@ ERROR_CODE_REQUIRED = {
|
|||||||
|
|
||||||
# Error code
|
# Error code
|
||||||
ERROR_CODE = {
|
ERROR_CODE = {
|
||||||
"2000": "Email not found.",
|
"2000": "Invalid email address. Please enter a registered email.",
|
||||||
"2001": "This is your existing password. Please choose other one",
|
"2001": "This is your existing password. Please choose other one",
|
||||||
"2002": "Invalid login credentials.",
|
"2002": "Invalid username or password.",
|
||||||
"2003": "An account already exists with this email address.",
|
"2003": "An account already exists with this email address.",
|
||||||
"2004": "User not found.",
|
"2004": "User not found.",
|
||||||
"2005": "Your account has been activated.",
|
"2005": "Your account has been activated.",
|
||||||
"2006": "Your account is not activated.",
|
"2006": "Your account is not activated.",
|
||||||
"2007": "Your account already activated.",
|
"2007": "Your account already activated.",
|
||||||
"2008": "Invalid OTP.",
|
"2008": "The OTP entered is not correct.",
|
||||||
"2009": "The user provided cannot be found or the reset password token has become invalid/timed out.",
|
"2009": "The user provided cannot be found or the reset password token has become invalid/timed out.",
|
||||||
"2010": "Invalid Link.",
|
"2010": "Invalid Link.",
|
||||||
"2011": "Your profile has not been completed yet.",
|
"2011": "Your profile has not been completed yet.",
|
||||||
@@ -42,18 +42,21 @@ 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": "User registered",
|
||||||
"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",
|
"2028": "You are not authorized person to sign up on this platform",
|
||||||
"2029": "Validity of otp verification is expired",
|
"2029": "Validity of otp verification has expired. Please request a new one.",
|
||||||
"2030": "Use correct user type and token",
|
"2030": "Use correct user type and token",
|
||||||
|
# invalid password
|
||||||
"2031": "Invalid password",
|
"2031": "Invalid password",
|
||||||
"2032": "Failed to send email",
|
"2032": "Failed to send email",
|
||||||
"2033": "Missing required fields",
|
"2033": "Missing required fields",
|
||||||
@@ -62,44 +65,125 @@ ERROR_CODE = {
|
|||||||
"2035": "Image should not be 0 kb",
|
"2035": "Image should not be 0 kb",
|
||||||
"2036": "Choose valid user",
|
"2036": "Choose valid user",
|
||||||
# log in multiple device msg
|
# log in multiple device msg
|
||||||
"2037": "You are already log in another device"
|
"2037": "You are already log in another device",
|
||||||
|
"2038": "Choose valid action for task",
|
||||||
|
# card length limit
|
||||||
|
"2039": "Add at least one article card or maximum 6",
|
||||||
|
"2040": "Add at least 5 article survey or maximum 10",
|
||||||
|
# add article msg
|
||||||
|
"2041": "Article with given id doesn't exist.",
|
||||||
|
"2042": "Article Card with given id doesn't exist.",
|
||||||
|
"2043": "Article Survey with given id doesn't exist.",
|
||||||
|
"2044": "Task does not exist",
|
||||||
|
"2045": "Invalid guardian",
|
||||||
|
# past due date
|
||||||
|
"2046": "Due date must be future date",
|
||||||
|
# invalid junior id msg
|
||||||
|
"2047": "Invalid Junior ID ",
|
||||||
|
"2048": "Choose right file for image",
|
||||||
|
# task request
|
||||||
|
"2049": "This task is already requested ",
|
||||||
|
"2059": "Already exist junior",
|
||||||
|
# task status
|
||||||
|
"2060": "Task does not exist or not in pending state",
|
||||||
|
"2061": "Please insert image or check the image is valid or not.",
|
||||||
|
# email not null
|
||||||
|
"2062": "Please enter email address",
|
||||||
|
"2063": "Unauthorized access.",
|
||||||
|
"2064": "To change your password first request an OTP and get it verify then change your password.",
|
||||||
|
"2065": "Passwords do not match. Please try again.",
|
||||||
|
"2066": "Task does not exist or not in expired state",
|
||||||
|
"2067": "Action not allowed. User type missing.",
|
||||||
|
"2068": "No guardian associated with this junior",
|
||||||
|
"2069": "Invalid user type",
|
||||||
|
"2070": "You do not find as a guardian",
|
||||||
|
"2071": "You do not find as a junior",
|
||||||
|
"2072": "You can not approve or reject this task because junior does not exist in the system",
|
||||||
|
"2073": "You can not approve or reject this junior because junior does not exist in the system",
|
||||||
|
"2074": "You can not complete this task because you does not exist in the system",
|
||||||
|
# deactivate account
|
||||||
|
"2075": "Your account is deactivated. Please contact with admin",
|
||||||
|
"2076": "This junior already associated with you",
|
||||||
|
"2077": "You can not add guardian",
|
||||||
|
"2078": "This junior is not associated with you",
|
||||||
|
# force update
|
||||||
|
"2079": "Please update your app version for enjoying uninterrupted services",
|
||||||
|
"2080": "Can not add App version",
|
||||||
|
"2081": "A junior can only be associated with a maximum of 3 guardian",
|
||||||
|
# guardian code not exist
|
||||||
|
"2082": "Guardian code does not exist",
|
||||||
|
"2083": "You can not start this task because guardian is not associate with you",
|
||||||
|
"2084": "You can not complete this task because guardian is not associate with you",
|
||||||
|
"2085": "You can not take action on this task because junior is not associate with you"
|
||||||
|
|
||||||
}
|
}
|
||||||
"""Success message code"""
|
"""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 successful",
|
"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 account is deleted successfully.",
|
"3005": "Your account has been deleted successfully.",
|
||||||
# Success code for password reset
|
# Success code for password reset
|
||||||
"3006": "Your password has been reset successfully.",
|
"3006": "Password reset successful. You can now log in with your new password.",
|
||||||
# Success code for password update
|
# Success code for password update
|
||||||
"3007": "Your password has been changed successfully.",
|
"3007": "Your password has been changed successfully.",
|
||||||
# Success code for valid link
|
# Success code for valid link
|
||||||
"3008": "You have a valid link.",
|
"3008": "You have a valid link.",
|
||||||
# Success code for logged out
|
# Success code for logged out
|
||||||
"3009": "You have successfully logged out!",
|
"3009": "You have successfully logged out.",
|
||||||
# Success code for check all fields
|
# Success code for check all fields
|
||||||
"3010": "All fields are valid",
|
"3010": "All fields are valid.",
|
||||||
"3011": "Email OTP Verified successfully",
|
"3011": "Email OTP has been verified successfully.",
|
||||||
"3012": "Phone OTP Verified successfully",
|
"3012": "Phone OTP has been verified successfully.",
|
||||||
"3013": "Valid Guardian code",
|
"3013": "Valid Guardian code",
|
||||||
"3014": "Password has been updated successfully.",
|
"3014": "Password has been updated successfully.",
|
||||||
"3015": "Verification code sent on your email.",
|
"3015": "Verification code has been sent on your email.",
|
||||||
"3016": "Send otp on your Email successfully",
|
"3016": "An OTP has been sent on your email.",
|
||||||
"3017": "Profile image update successfully",
|
"3017": "Profile image update successfully",
|
||||||
"3018": "Task created successfully",
|
"3018": "Task created successfully",
|
||||||
"3019": "Support Email sent successfully",
|
"3019": "Support Email sent successfully",
|
||||||
"3020": "Logged out successfully.",
|
"3020": "Logged out successfully.",
|
||||||
"3021": "Add junior successfully",
|
"3021": "Junior has been added successfully.",
|
||||||
"3022": "Remove junior successfully",
|
"3022": "Junior has been removed successfully.",
|
||||||
"3023": "Approved junior successfully",
|
"3023": "Junior has been approved successfully.",
|
||||||
"3024": "Reject junior request successfully"
|
"3024": "Junior request is rejected successfully.",
|
||||||
|
"3025": "Task is approved successfully.",
|
||||||
|
"3026": "Task is rejected successfully.",
|
||||||
|
"3027": "Article has been created successfully.",
|
||||||
|
"3028": "Article has been updated successfully.",
|
||||||
|
"3029": "Article has been deleted successfully.",
|
||||||
|
"3030": "Article Card has been removed successfully.",
|
||||||
|
"3031": "Article Survey has been removed successfully.",
|
||||||
|
"3032": "Task request sent successfully.",
|
||||||
|
"3033": "Valid Referral code.",
|
||||||
|
"3034": "Invite guardian successfully.",
|
||||||
|
"3035": "Task started successfully.",
|
||||||
|
"3036": "Task reassign successfully.",
|
||||||
|
"3037": "Profile has been updated successfully.",
|
||||||
|
"3038": "Status has been changed successfully.",
|
||||||
|
# notification read
|
||||||
|
"3039": "Notification read successfully.",
|
||||||
|
# start article
|
||||||
|
"3040": "Start article successfully.",
|
||||||
|
# complete article
|
||||||
|
"3041": "Article completed successfully.",
|
||||||
|
# submit assessment successfully
|
||||||
|
"3042": "Assessment completed successfully.",
|
||||||
|
# read article
|
||||||
|
"3043": "Read article card successfully.",
|
||||||
|
# remove guardian code request
|
||||||
|
"3044": "Remove guardian code request successfully.",
|
||||||
|
# create faq
|
||||||
|
"3045": "Create FAQ data.",
|
||||||
|
"3046": "Add App version successfully."
|
||||||
|
|
||||||
}
|
}
|
||||||
"""status code error"""
|
"""status code error"""
|
||||||
STATUS_CODE_ERROR = {
|
STATUS_CODE_ERROR = {
|
||||||
|
|||||||
@@ -0,0 +1,34 @@
|
|||||||
|
"""
|
||||||
|
web_admin pagination file
|
||||||
|
"""
|
||||||
|
# third party imports
|
||||||
|
from collections import OrderedDict
|
||||||
|
from rest_framework.pagination import PageNumberPagination
|
||||||
|
|
||||||
|
from account.utils import custom_response
|
||||||
|
from base.constants import NUMBER
|
||||||
|
|
||||||
|
|
||||||
|
class CustomPageNumberPagination(PageNumberPagination):
|
||||||
|
"""
|
||||||
|
custom paginator class
|
||||||
|
"""
|
||||||
|
# Set the desired page size
|
||||||
|
page_size = NUMBER['ten']
|
||||||
|
page_size_query_param = 'page_size'
|
||||||
|
# Set a maximum page size if needed
|
||||||
|
max_page_size = NUMBER['hundred']
|
||||||
|
|
||||||
|
def get_paginated_response(self, data):
|
||||||
|
"""
|
||||||
|
:param data: queryset to be paginated
|
||||||
|
:return: return a OrderedDict
|
||||||
|
"""
|
||||||
|
return custom_response(None, OrderedDict([
|
||||||
|
('count', self.page.paginator.count),
|
||||||
|
('data', data),
|
||||||
|
('current_page', self.page.number),
|
||||||
|
('total_pages', self.page.paginator.num_pages),
|
||||||
|
|
||||||
|
|
||||||
|
]))
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
"""
|
||||||
|
web_admin tasks file
|
||||||
|
"""
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
# third party imports
|
||||||
|
from celery import shared_task
|
||||||
|
from templated_email import send_templated_mail
|
||||||
|
|
||||||
|
# django imports
|
||||||
|
from django.conf import settings
|
||||||
|
from django.db.models import F, Window
|
||||||
|
from django.db.models.functions.window import Rank
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from base.constants import PENDING, IN_PROGRESS, JUNIOR, GUARDIAN
|
||||||
|
from guardian.models import JuniorTask
|
||||||
|
from junior.models import JuniorPoints
|
||||||
|
from notifications.constants import PENDING_TASK_EXPIRING, IN_PROGRESS_TASK_EXPIRING, NOTIFICATION_DICT, TOP_JUNIOR
|
||||||
|
from notifications.models import Notification
|
||||||
|
from notifications.utils import send_notification, get_from_user_details, send_notification_multiple_user
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task
|
||||||
|
def send_email(recipient_list, template, context: dict = None):
|
||||||
|
"""
|
||||||
|
used to send otp on email
|
||||||
|
:param context:
|
||||||
|
:param recipient_list: e-mail list
|
||||||
|
:param template: email template
|
||||||
|
"""
|
||||||
|
if context is None:
|
||||||
|
context = {}
|
||||||
|
from_email = settings.EMAIL_FROM_ADDRESS
|
||||||
|
send_templated_mail(
|
||||||
|
template_name=template,
|
||||||
|
from_email=from_email,
|
||||||
|
recipient_list=recipient_list,
|
||||||
|
context=context
|
||||||
|
)
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task()
|
||||||
|
def notify_task_expiry():
|
||||||
|
"""
|
||||||
|
task to send notification for those task which expiring soon
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
all_pending_tasks = JuniorTask.objects.filter(
|
||||||
|
junior__is_verified=True,
|
||||||
|
task_status__in=[PENDING, IN_PROGRESS],
|
||||||
|
due_date__range=[datetime.datetime.now().date(),
|
||||||
|
(datetime.datetime.now().date() + datetime.timedelta(days=1))])
|
||||||
|
if pending_tasks := all_pending_tasks.filter(task_status=PENDING):
|
||||||
|
for task in pending_tasks:
|
||||||
|
send_notification(PENDING_TASK_EXPIRING, task.guardian.user_id, GUARDIAN, task.junior.auth_id,
|
||||||
|
{'task_id': task.id})
|
||||||
|
if in_progress_tasks := all_pending_tasks.filter(task_status=IN_PROGRESS):
|
||||||
|
for task in in_progress_tasks:
|
||||||
|
send_notification(IN_PROGRESS_TASK_EXPIRING, task.junior.auth_id, JUNIOR, task.guardian.user_id,
|
||||||
|
{'task_id': task.id})
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task()
|
||||||
|
def notify_top_junior():
|
||||||
|
"""
|
||||||
|
task to send notification for top leaderboard junior to all junior's
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
junior_points_qs = JuniorPoints.objects.filter(
|
||||||
|
junior__is_verified=True
|
||||||
|
).select_related(
|
||||||
|
'junior', 'junior__auth'
|
||||||
|
).annotate(rank=Window(
|
||||||
|
expression=Rank(),
|
||||||
|
order_by=[F('total_points').desc(), 'junior__created_at'])
|
||||||
|
).order_by('-total_points', 'junior__created_at')
|
||||||
|
|
||||||
|
prev_top_position = junior_points_qs.filter(position=1).first()
|
||||||
|
new_top_position = junior_points_qs.filter(rank=1).first()
|
||||||
|
if prev_top_position != new_top_position:
|
||||||
|
send_notification_multiple_user(TOP_JUNIOR, new_top_position.junior.auth_id, JUNIOR,
|
||||||
|
{'points': new_top_position.total_points})
|
||||||
|
for junior_point in junior_points_qs:
|
||||||
|
junior_point.position = junior_point.rank
|
||||||
|
junior_point.save()
|
||||||
|
return True
|
||||||
Binary file not shown.
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
image: nginx:latest
|
||||||
|
container_name: nginx
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
volumes:
|
||||||
|
- ./nginx:/etc/nginx/conf.d
|
||||||
|
- .:/usr/src/app
|
||||||
|
depends_on:
|
||||||
|
- web
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
container_name: prod_django
|
||||||
|
restart: always
|
||||||
|
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
|
||||||
|
volumes:
|
||||||
|
- .:/usr/src/app
|
||||||
|
|
||||||
|
broker:
|
||||||
|
image: rabbitmq:3.7
|
||||||
|
container_name: prod_rabbitmq
|
||||||
|
volumes:
|
||||||
|
- .:/usr/src/app
|
||||||
|
ports:
|
||||||
|
- 5673:5673
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build: .
|
||||||
|
image: celery
|
||||||
|
container_name: prod_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
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
image: nginx:latest
|
||||||
|
container_name: nginx
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
volumes:
|
||||||
|
- ./nginx:/etc/nginx/conf.d
|
||||||
|
- .:/usr/src/app
|
||||||
|
depends_on:
|
||||||
|
- web
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
container_name: qa_django
|
||||||
|
restart: always
|
||||||
|
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
|
||||||
|
volumes:
|
||||||
|
- .:/usr/src/app
|
||||||
|
|
||||||
|
broker:
|
||||||
|
image: rabbitmq:3.7
|
||||||
|
container_name: qa_rabbitmq
|
||||||
|
volumes:
|
||||||
|
- .:/usr/src/app
|
||||||
|
ports:
|
||||||
|
- 5673:5673
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build: .
|
||||||
|
image: celery
|
||||||
|
container_name: qa_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
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
version: '3'
|
||||||
|
services:
|
||||||
|
nginx:
|
||||||
|
image: nginx:latest
|
||||||
|
container_name: nginx
|
||||||
|
restart: always
|
||||||
|
ports:
|
||||||
|
- "8000:8000"
|
||||||
|
volumes:
|
||||||
|
- ./nginx:/etc/nginx/conf.d
|
||||||
|
- .:/usr/src/app
|
||||||
|
depends_on:
|
||||||
|
- web
|
||||||
|
web:
|
||||||
|
build: .
|
||||||
|
container_name: stage_django
|
||||||
|
restart: always
|
||||||
|
command: bash -c "pip install -r requirements.txt && python manage.py collectstatic --noinput && python manage.py migrate && gunicorn zod_bank.wsgi -b 0.0.0.0:8000 -t 300 --log-level=info"
|
||||||
|
volumes:
|
||||||
|
- .:/usr/src/app
|
||||||
|
|
||||||
|
broker:
|
||||||
|
image: rabbitmq:3.7
|
||||||
|
container_name: stage_rabbitmq
|
||||||
|
volumes:
|
||||||
|
- .:/usr/src/app
|
||||||
|
ports:
|
||||||
|
- 5673:5673
|
||||||
|
|
||||||
|
worker:
|
||||||
|
build: .
|
||||||
|
image: celery
|
||||||
|
container_name: stage_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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-17 12:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('guardian', '0020_alter_juniortask_task_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='guardian',
|
||||||
|
name='is_deleted',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-09-08 10:41
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('guardian', '0021_guardian_is_deleted'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='juniortask',
|
||||||
|
name='task_description',
|
||||||
|
field=models.CharField(blank=True, max_length=500, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
+42
-3
@@ -5,9 +5,36 @@ from django.contrib.auth import get_user_model
|
|||||||
"""Import Django app"""
|
"""Import Django app"""
|
||||||
from base.constants import GENDERS, TASK_STATUS, PENDING, TASK_POINTS, SIGNUP_METHODS
|
from base.constants import GENDERS, TASK_STATUS, PENDING, TASK_POINTS, SIGNUP_METHODS
|
||||||
"""import Junior model"""
|
"""import Junior model"""
|
||||||
from junior.models import Junior
|
import junior.models
|
||||||
"""Add user model"""
|
"""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):
|
||||||
@@ -26,6 +53,12 @@ class Guardian(models.Model):
|
|||||||
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"""
|
"""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)
|
||||||
|
# guardian profile deleted or not"""
|
||||||
|
is_deleted = models.BooleanField(default=False)
|
||||||
"""Profile activity"""
|
"""Profile activity"""
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
"""guardian is verified or not"""
|
"""guardian is verified or not"""
|
||||||
@@ -64,7 +97,7 @@ class JuniorTask(models.Model):
|
|||||||
"""task details"""
|
"""task details"""
|
||||||
task_name = models.CharField(max_length=100)
|
task_name = models.CharField(max_length=100)
|
||||||
"""task description"""
|
"""task description"""
|
||||||
task_description = models.CharField(max_length=500)
|
task_description = models.CharField(max_length=500, null=True, blank=True)
|
||||||
"""points of the task"""
|
"""points of the task"""
|
||||||
points = models.IntegerField(default=TASK_POINTS)
|
points = models.IntegerField(default=TASK_POINTS)
|
||||||
"""last date of the task"""
|
"""last date of the task"""
|
||||||
@@ -74,13 +107,19 @@ class JuniorTask(models.Model):
|
|||||||
"""image that is uploaded by junior"""
|
"""image that is uploaded by junior"""
|
||||||
image = models.URLField(null=True, blank=True, default=None)
|
image = models.URLField(null=True, blank=True, default=None)
|
||||||
"""associated junior with the task"""
|
"""associated junior with the task"""
|
||||||
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='junior', verbose_name='Junior')
|
junior = models.ForeignKey('junior.Junior', on_delete=models.CASCADE, related_name='junior', verbose_name='Junior')
|
||||||
"""task status"""
|
"""task status"""
|
||||||
task_status = models.CharField(choices=TASK_STATUS, max_length=15, default=PENDING)
|
task_status = models.CharField(choices=TASK_STATUS, max_length=15, default=PENDING)
|
||||||
"""task is active or not"""
|
"""task is active or not"""
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
"""Task is approved or not"""
|
"""Task is approved or not"""
|
||||||
is_approved = models.BooleanField(default=False)
|
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"""
|
"""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)
|
||||||
|
|||||||
+294
-24
@@ -1,23 +1,95 @@
|
|||||||
"""Serializer of Guardian"""
|
"""Serializer of Guardian"""
|
||||||
"""Third party Django app"""
|
# third party imports
|
||||||
import logging
|
import logging
|
||||||
import random
|
from django.contrib.auth import password_validation
|
||||||
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
|
||||||
"""Import Django app"""
|
from datetime import datetime, time
|
||||||
|
import pytz
|
||||||
|
from django.utils import timezone
|
||||||
|
# Import guardian's model,
|
||||||
|
# Import junior's model,
|
||||||
|
# Import account's model,
|
||||||
|
# Import constant from
|
||||||
|
# base package,
|
||||||
|
# Import messages from
|
||||||
|
# base package,
|
||||||
|
# Import some functions
|
||||||
|
# local imports
|
||||||
from .models import Guardian, JuniorTask
|
from .models import Guardian, JuniorTask
|
||||||
from account.models import UserProfile, UserEmailOtp, UserNotification
|
from account.models import UserProfile, UserEmailOtp, UserNotification
|
||||||
from account.utils import generate_code
|
from account.utils import generate_code
|
||||||
from account.serializers import JuniorSerializer
|
|
||||||
from junior.serializers import JuniorDetailSerializer
|
from junior.serializers import JuniorDetailSerializer
|
||||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
from base.constants import NUMBER, JUN, ZOD, GRD
|
from base.constants import NUMBER, JUN, ZOD, GRD, Already_register_user, GUARDIAN
|
||||||
from junior.models import Junior, JuniorPoints
|
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
|
||||||
|
from .utils import real_time, convert_timedelta_into_datetime, update_referral_points
|
||||||
|
# notification's constant
|
||||||
|
from notifications.constants import TASK_APPROVED, TASK_REJECTED, TASK_ASSIGNED
|
||||||
|
# send notification function
|
||||||
|
from notifications.utils import send_notification
|
||||||
|
from django.core.exceptions import ValidationError
|
||||||
|
from django.utils.translation import gettext as _
|
||||||
|
|
||||||
|
# In this serializer file
|
||||||
|
# define user serializer,
|
||||||
|
# define password validation
|
||||||
|
# create guardian serializer,
|
||||||
|
# task serializer,
|
||||||
|
# guardian serializer,
|
||||||
|
# task details serializer,
|
||||||
|
# top junior serializer,
|
||||||
|
# guardian profile serializer,
|
||||||
|
# approve junior serializer,
|
||||||
|
# approve task serializer,
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
class PasswordValidator:
|
||||||
|
"""Password validation"""
|
||||||
|
def __init__(self, min_length=8, max_length=None, require_uppercase=True, require_numbers=True):
|
||||||
|
self.min_length = min_length
|
||||||
|
self.max_length = max_length
|
||||||
|
self.require_uppercase = require_uppercase
|
||||||
|
self.require_numbers = require_numbers
|
||||||
|
|
||||||
|
def __call__(self, value):
|
||||||
|
self.enforce_password_policy(value)
|
||||||
|
|
||||||
|
def enforce_password_policy(self, password):
|
||||||
|
# add validation for password
|
||||||
|
special_characters = "!@#$%^&*()_-+=<>?/[]{}|"
|
||||||
|
if len(password) < self.min_length:
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
_("Password must be at least %(min_length)d characters long.") % {'min_length': self.min_length}
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.max_length is not None and len(password) > self.max_length:
|
||||||
|
# must be 8 character
|
||||||
|
raise serializers.ValidationError(
|
||||||
|
_("Password must be at most %(max_length)d characters long.") % {'max_length': self.max_length}
|
||||||
|
)
|
||||||
|
|
||||||
|
if self.require_uppercase and not any(char.isupper() for char in password):
|
||||||
|
# must contain upper case letter
|
||||||
|
raise serializers.ValidationError(_("Password must contain at least one uppercase letter."))
|
||||||
|
|
||||||
|
if self.require_numbers and not any(char.isdigit() for char in password):
|
||||||
|
# must contain digit
|
||||||
|
raise serializers.ValidationError(_("Password must contain at least one digit."))
|
||||||
|
if self.require_numbers and not any(char in special_characters for char in password):
|
||||||
|
# must contain special character
|
||||||
|
raise serializers.ValidationError(_("Password must contain at least one special character."))
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class UserSerializer(serializers.ModelSerializer):
|
class UserSerializer(serializers.ModelSerializer):
|
||||||
"""User serializer"""
|
"""User serializer"""
|
||||||
auth_token = serializers.SerializerMethodField('get_auth_token')
|
auth_token = serializers.SerializerMethodField('get_auth_token')
|
||||||
|
password = serializers.CharField(write_only=True, validators=[PasswordValidator()])
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
@@ -37,11 +109,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)
|
||||||
UserNotification.objects.create(user=user)
|
UserNotification.objects.get_or_create(user=user)
|
||||||
if user_type == '1':
|
if user_type == str(NUMBER['one']):
|
||||||
Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
|
# create junior profile
|
||||||
referral_code=generate_code(ZOD, user.id))
|
junior = Junior.objects.create(auth=user, junior_code=generate_code(JUN, user.id),
|
||||||
if user_type == '2':
|
referral_code=generate_code(ZOD, user.id))
|
||||||
|
position = Junior.objects.all().count()
|
||||||
|
JuniorPoints.objects.create(junior=junior, position=position)
|
||||||
|
if user_type == str(NUMBER['two']):
|
||||||
|
# create guardian profile
|
||||||
Guardian.objects.create(user=user, guardian_code=generate_code(GRD, user.id),
|
Guardian.objects.create(user=user, guardian_code=generate_code(GRD, user.id),
|
||||||
referral_code=generate_code(ZOD, user.id))
|
referral_code=generate_code(ZOD, user.id))
|
||||||
return user
|
return user
|
||||||
@@ -52,10 +128,11 @@ class UserSerializer(serializers.ModelSerializer):
|
|||||||
otp_verified = False
|
otp_verified = False
|
||||||
if otp and otp.is_verified:
|
if otp and otp.is_verified:
|
||||||
otp_verified = True
|
otp_verified = True
|
||||||
raise serializers.ValidationError({"details":ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
|
raise serializers.ValidationError({"details": ERROR_CODE['2021'], "otp_verified":bool(otp_verified),
|
||||||
"code": 400, "status":"failed",
|
"code": 400, "status":"failed",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
# update guardian profile
|
||||||
class CreateGuardianSerializer(serializers.ModelSerializer):
|
class CreateGuardianSerializer(serializers.ModelSerializer):
|
||||||
"""Create guardian serializer"""
|
"""Create guardian serializer"""
|
||||||
"""Basic info"""
|
"""Basic info"""
|
||||||
@@ -65,9 +142,12 @@ 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 info
|
||||||
image = serializers.URLField(required=False)
|
image = serializers.URLField(required=False)
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
@@ -111,7 +191,6 @@ 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)
|
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.gender, guardian.country_name,
|
complete_profile_field = all([guardian.gender, guardian.country_name,
|
||||||
@@ -120,6 +199,9 @@ class CreateGuardianSerializer(serializers.ModelSerializer):
|
|||||||
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
|
||||||
|
referral_code_used = validated_data.get('referral_code_used')
|
||||||
|
update_referral_points(guardian.referral_code, referral_code_used)
|
||||||
|
guardian.referral_code_used = validated_data.get('referral_code_used', guardian.referral_code_used)
|
||||||
guardian.save()
|
guardian.save()
|
||||||
return guardian
|
return guardian
|
||||||
|
|
||||||
@@ -136,13 +218,33 @@ class TaskSerializer(serializers.ModelSerializer):
|
|||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = JuniorTask
|
model = JuniorTask
|
||||||
fields = ['id', 'task_name','task_description','points', 'due_date', 'junior', 'default_image']
|
fields = ['id', 'task_name','task_description','points', 'due_date','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):
|
def create(self, validated_data):
|
||||||
"""create default task image data"""
|
"""create default task image data"""
|
||||||
validated_data['guardian'] = Guardian.objects.filter(user=self.context['user']).last()
|
guardian = self.context['guardian']
|
||||||
|
# update image of the task
|
||||||
images = self.context['image']
|
images = self.context['image']
|
||||||
validated_data['default_image'] = images
|
junior_data = self.context['junior_data']
|
||||||
instance = JuniorTask.objects.create(**validated_data)
|
tasks_created = []
|
||||||
|
|
||||||
|
for junior in junior_data:
|
||||||
|
# create task
|
||||||
|
task_data = validated_data.copy()
|
||||||
|
task_data['guardian'] = guardian
|
||||||
|
task_data['default_image'] = images
|
||||||
|
task_data['junior'] = junior
|
||||||
|
instance = JuniorTask.objects.create(**task_data)
|
||||||
|
tasks_created.append(instance)
|
||||||
|
send_notification.delay(TASK_ASSIGNED, guardian.user_id, GUARDIAN,
|
||||||
|
junior.auth_id, {'task_id': instance.id})
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
class GuardianDetailSerializer(serializers.ModelSerializer):
|
class GuardianDetailSerializer(serializers.ModelSerializer):
|
||||||
@@ -167,35 +269,92 @@ class GuardianDetailSerializer(serializers.ModelSerializer):
|
|||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = Guardian
|
model = Guardian
|
||||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
|
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
|
||||||
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
|
'guardian_code','is_active', 'is_complete_profile', 'created_at', 'image', 'is_deleted',
|
||||||
'updated_at']
|
'updated_at']
|
||||||
class TaskDetailsSerializer(serializers.ModelSerializer):
|
class TaskDetailsSerializer(serializers.ModelSerializer):
|
||||||
"""Task detail serializer"""
|
"""Task detail serializer"""
|
||||||
|
|
||||||
junior = JuniorDetailSerializer()
|
junior = JuniorDetailSerializer()
|
||||||
|
remaining_time = serializers.SerializerMethodField('get_remaining_time')
|
||||||
|
is_expired = serializers.SerializerMethodField('get_is_expired')
|
||||||
|
|
||||||
|
def get_remaining_time(self, obj):
|
||||||
|
""" remaining time to complete task"""
|
||||||
|
due_date_datetime = datetime.combine(obj.due_date, datetime.max.time())
|
||||||
|
# fetch real time
|
||||||
|
# current_datetime = real_time()
|
||||||
|
# new code
|
||||||
|
due_date_datetime = due_date_datetime.astimezone(pytz.utc)
|
||||||
|
current_datetime = timezone.now().astimezone(pytz.utc)
|
||||||
|
# Perform the subtraction
|
||||||
|
if due_date_datetime > current_datetime:
|
||||||
|
time_difference = due_date_datetime - current_datetime
|
||||||
|
time_only = convert_timedelta_into_datetime(time_difference)
|
||||||
|
return str(time_difference.days) + ' days ' + str(time_only)
|
||||||
|
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
|
||||||
|
|
||||||
|
def get_is_expired(self, obj):
|
||||||
|
""" task expired or not"""
|
||||||
|
if obj.due_date < datetime.today().date():
|
||||||
|
return True
|
||||||
|
return False
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = JuniorTask
|
model = JuniorTask
|
||||||
fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image',
|
fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image',
|
||||||
'junior', 'task_status', 'is_active', 'created_at','updated_at']
|
'requested_on', 'rejected_on', 'completed_on', 'is_expired',
|
||||||
|
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
|
||||||
|
|
||||||
|
class TaskDetailsjuniorSerializer(serializers.ModelSerializer):
|
||||||
|
"""Task detail serializer"""
|
||||||
|
|
||||||
|
guardian = GuardianDetailSerializer()
|
||||||
|
remaining_time = serializers.SerializerMethodField('get_remaining_time')
|
||||||
|
|
||||||
|
def get_remaining_time(self, obj):
|
||||||
|
""" remaining time to complete task"""
|
||||||
|
due_date_datetime = datetime.combine(obj.due_date, datetime.max.time())
|
||||||
|
# fetch real time
|
||||||
|
# current_datetime = real_time()
|
||||||
|
# new code
|
||||||
|
due_date_datetime = due_date_datetime.astimezone(pytz.utc)
|
||||||
|
current_datetime = timezone.now().astimezone(pytz.utc)
|
||||||
|
# Perform the subtraction
|
||||||
|
if due_date_datetime > current_datetime:
|
||||||
|
time_difference = due_date_datetime - current_datetime
|
||||||
|
time_only = convert_timedelta_into_datetime(time_difference)
|
||||||
|
return str(time_difference.days) + ' days ' + str(time_only)
|
||||||
|
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
|
||||||
|
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""Meta info"""
|
||||||
|
model = JuniorTask
|
||||||
|
fields = ['id', 'guardian', 'task_name', 'task_description', 'points', 'due_date','default_image', 'image',
|
||||||
|
'requested_on', 'rejected_on', 'completed_on',
|
||||||
|
'junior', 'task_status', 'is_active', 'remaining_time', 'created_at','updated_at']
|
||||||
|
|
||||||
|
|
||||||
class TopJuniorSerializer(serializers.ModelSerializer):
|
class TopJuniorSerializer(serializers.ModelSerializer):
|
||||||
"""Top junior serializer"""
|
"""Top junior serializer"""
|
||||||
junior = JuniorDetailSerializer()
|
junior = JuniorDetailSerializer()
|
||||||
position = serializers.IntegerField()
|
position = serializers.SerializerMethodField()
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = JuniorPoints
|
model = JuniorPoints
|
||||||
fields = ['id', 'junior', 'total_task_points', 'position', 'created_at', 'updated_at']
|
fields = ['id', 'junior', 'total_points', 'position', 'created_at', 'updated_at']
|
||||||
|
|
||||||
def to_representation(self, instance):
|
def to_representation(self, instance):
|
||||||
"""Convert instance to representation"""
|
"""Convert instance to representation"""
|
||||||
representation = super().to_representation(instance)
|
representation = super().to_representation(instance)
|
||||||
representation['position'] = instance.position
|
|
||||||
return representation
|
return representation
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_position(obj):
|
||||||
|
""" get position/rank """
|
||||||
|
return obj.rank
|
||||||
|
|
||||||
|
|
||||||
class GuardianProfileSerializer(serializers.ModelSerializer):
|
class GuardianProfileSerializer(serializers.ModelSerializer):
|
||||||
"""junior serializer"""
|
"""junior serializer"""
|
||||||
@@ -227,6 +386,7 @@ class GuardianProfileSerializer(serializers.ModelSerializer):
|
|||||||
"""total filled fields count"""
|
"""total filled fields count"""
|
||||||
total_field_list = [obj.user.first_name, obj.country_name,
|
total_field_list = [obj.user.first_name, obj.country_name,
|
||||||
obj.gender, obj.dob, obj.image]
|
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]
|
total_complete_field = [data for data in total_field_list if data != '' and data is not None]
|
||||||
return len(total_complete_field)
|
return len(total_complete_field)
|
||||||
|
|
||||||
@@ -240,7 +400,8 @@ class GuardianProfileSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_name','country_code', 'phone', 'gender', 'dob',
|
fields = ['id', 'email', 'first_name', 'last_name', 'country_name','country_code', 'phone', 'gender', 'dob',
|
||||||
'guardian_code', 'notification_count', 'total_count', 'complete_field_count', 'referral_code',
|
'guardian_code', 'notification_count', 'total_count', 'complete_field_count', 'referral_code',
|
||||||
'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method',
|
'is_active', 'is_complete_profile', 'created_at', 'image', 'signup_method',
|
||||||
'updated_at', 'passcode']
|
'updated_at', 'passcode','is_deleted']
|
||||||
|
|
||||||
|
|
||||||
class ApproveJuniorSerializer(serializers.ModelSerializer):
|
class ApproveJuniorSerializer(serializers.ModelSerializer):
|
||||||
"""approve junior serializer"""
|
"""approve junior serializer"""
|
||||||
@@ -252,8 +413,117 @@ class ApproveJuniorSerializer(serializers.ModelSerializer):
|
|||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
"""update guardian code"""
|
"""update guardian code"""
|
||||||
instance = self.context['junior']
|
instance = self.context['junior']
|
||||||
instance.guardian_code = [self.context['guardian_code']]
|
guardian_code = self.context['guardian_code']
|
||||||
|
index = instance.guardian_code.index(guardian_code)
|
||||||
|
instance.guardian_code_status[index] = str(NUMBER['two'])
|
||||||
instance.save()
|
instance.save()
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class ApproveTaskSerializer(serializers.ModelSerializer):
|
||||||
|
"""approve task serializer"""
|
||||||
|
class Meta(object):
|
||||||
|
"""Meta info"""
|
||||||
|
model = JuniorTask
|
||||||
|
fields = ['id', 'task_status', 'is_approved']
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
"""update task status """
|
||||||
|
with transaction.atomic():
|
||||||
|
instance = self.context['task_instance']
|
||||||
|
junior = self.context['junior']
|
||||||
|
junior_details = Junior.objects.filter(id=junior).last()
|
||||||
|
junior_data, created = JuniorPoints.objects.get_or_create(junior=junior_details)
|
||||||
|
if self.context['action'] == str(NUMBER['one']):
|
||||||
|
# approve the task
|
||||||
|
instance.task_status = str(NUMBER['five'])
|
||||||
|
instance.is_approved = True
|
||||||
|
# update total task point
|
||||||
|
junior_data.total_points = junior_data.total_points + instance.points
|
||||||
|
# update complete time of task
|
||||||
|
# instance.completed_on = real_time()
|
||||||
|
instance.completed_on = timezone.now().astimezone(pytz.utc)
|
||||||
|
send_notification.delay(TASK_APPROVED, instance.guardian.user_id, GUARDIAN,
|
||||||
|
junior_details.auth_id, {'task_id': instance.id})
|
||||||
|
else:
|
||||||
|
# reject the task
|
||||||
|
instance.task_status = str(NUMBER['three'])
|
||||||
|
instance.is_approved = False
|
||||||
|
# update reject time of task
|
||||||
|
# instance.rejected_on = real_time()
|
||||||
|
instance.rejected_on = timezone.now().astimezone(pytz.utc)
|
||||||
|
send_notification.delay(TASK_REJECTED, instance.guardian.user_id, GUARDIAN,
|
||||||
|
junior_details.auth_id, {'task_id': instance.id})
|
||||||
|
instance.save()
|
||||||
|
junior_data.save()
|
||||||
|
return junior_details
|
||||||
|
|
||||||
|
class GuardianDetailListSerializer(serializers.ModelSerializer):
|
||||||
|
"""Guardian serializer"""
|
||||||
|
|
||||||
|
first_name = serializers.SerializerMethodField('get_first_name')
|
||||||
|
last_name = serializers.SerializerMethodField('get_last_name')
|
||||||
|
email = serializers.SerializerMethodField('get_email')
|
||||||
|
image = serializers.SerializerMethodField('get_image')
|
||||||
|
guardian_id = serializers.SerializerMethodField('get_guardian_id')
|
||||||
|
guardian_code = serializers.SerializerMethodField('get_guardian_code')
|
||||||
|
gender = serializers.SerializerMethodField('get_gender')
|
||||||
|
phone = serializers.SerializerMethodField('get_phone')
|
||||||
|
country_name = serializers.SerializerMethodField('get_country_name')
|
||||||
|
dob = serializers.SerializerMethodField('get_dob')
|
||||||
|
guardian_code_status = serializers.SerializerMethodField('get_guardian_code_status')
|
||||||
|
# code info
|
||||||
|
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""Meta info"""
|
||||||
|
model = JuniorGuardianRelationship
|
||||||
|
fields = ['guardian_id', 'first_name', 'last_name', 'email', 'relationship', 'image', 'dob',
|
||||||
|
'guardian_code', 'gender', 'phone', 'country_name', 'created_at', 'guardian_code_status',
|
||||||
|
'updated_at']
|
||||||
|
|
||||||
|
def get_guardian_id(self,obj):
|
||||||
|
"""first name of guardian"""
|
||||||
|
return obj.guardian.id
|
||||||
|
def get_first_name(self,obj):
|
||||||
|
"""first name of guardian"""
|
||||||
|
return obj.guardian.user.first_name
|
||||||
|
|
||||||
|
def get_last_name(self,obj):
|
||||||
|
"""last name of guardian"""
|
||||||
|
return obj.guardian.user.last_name
|
||||||
|
|
||||||
|
def get_email(self,obj):
|
||||||
|
"""email of guardian"""
|
||||||
|
return obj.guardian.user.email
|
||||||
|
|
||||||
|
def get_image(self,obj):
|
||||||
|
"""guardian image"""
|
||||||
|
return obj.guardian.image
|
||||||
|
|
||||||
|
def get_guardian_code(self,obj):
|
||||||
|
""" guardian code"""
|
||||||
|
return obj.guardian.guardian_code
|
||||||
|
|
||||||
|
def get_gender(self,obj):
|
||||||
|
""" guardian gender"""
|
||||||
|
return obj.guardian.gender
|
||||||
|
|
||||||
|
def get_phone(self,obj):
|
||||||
|
"""guardian phone"""
|
||||||
|
return obj.guardian.phone
|
||||||
|
|
||||||
|
def get_country_name(self,obj):
|
||||||
|
""" guardian country name """
|
||||||
|
return obj.guardian.country_name
|
||||||
|
|
||||||
|
def get_dob(self,obj):
|
||||||
|
"""guardian dob """
|
||||||
|
return obj.guardian.dob
|
||||||
|
|
||||||
|
def get_guardian_code_status(self,obj):
|
||||||
|
"""guardian code status"""
|
||||||
|
if obj.guardian.guardian_code in obj.junior.guardian_code:
|
||||||
|
index = obj.junior.guardian_code.index(obj.guardian.guardian_code)
|
||||||
|
data = obj.junior.guardian_code_status[index]
|
||||||
|
return data
|
||||||
|
|||||||
+10
-4
@@ -1,6 +1,12 @@
|
|||||||
"""task files"""
|
"""task files"""
|
||||||
"""Django import"""
|
|
||||||
import random
|
# Django import
|
||||||
|
import secrets
|
||||||
|
|
||||||
|
|
||||||
def generate_otp():
|
def generate_otp():
|
||||||
"""generate random otp"""
|
"""
|
||||||
return ''.join([str(random.randrange(9)) for _ in range(6)])
|
generate random otp
|
||||||
|
"""
|
||||||
|
digits = "0123456789"
|
||||||
|
return "".join(secrets.choice(digits) for _ in range(6))
|
||||||
|
|||||||
+7
-4
@@ -1,8 +1,9 @@
|
|||||||
""" 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, AllTaskListAPIView, CreateTaskAPIView, TaskListAPIView,
|
from .views import (SignupViewset, UpdateGuardianProfile, CreateTaskAPIView, TaskListAPIView,
|
||||||
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView)
|
SearchTaskListAPIView, TopJuniorListAPIView, ApproveJuniorAPIView, ApproveTaskAPIView,
|
||||||
|
GuardianListAPIView)
|
||||||
"""Third party import"""
|
"""Third party import"""
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
|
||||||
@@ -24,8 +25,6 @@ router.register('sign-up', SignupViewset, basename='sign-up')
|
|||||||
router.register('create-guardian-profile', UpdateGuardianProfile, basename='update-guardian-profile')
|
router.register('create-guardian-profile', UpdateGuardianProfile, basename='update-guardian-profile')
|
||||||
# Create Task API"""
|
# Create Task API"""
|
||||||
router.register('create-task', CreateTaskAPIView, basename='create-task')
|
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"""
|
# Task list bases on the status API"""
|
||||||
router.register('task-list', TaskListAPIView, basename='task-list')
|
router.register('task-list', TaskListAPIView, basename='task-list')
|
||||||
# Leaderboard API"""
|
# Leaderboard API"""
|
||||||
@@ -34,6 +33,10 @@ router.register('top-junior', TopJuniorListAPIView, basename='top-junior')
|
|||||||
router.register('filter-task', SearchTaskListAPIView, basename='filter-task')
|
router.register('filter-task', SearchTaskListAPIView, basename='filter-task')
|
||||||
# Approve junior API"""
|
# Approve junior API"""
|
||||||
router.register('approve-junior', ApproveJuniorAPIView, basename='approve-junior')
|
router.register('approve-junior', ApproveJuniorAPIView, basename='approve-junior')
|
||||||
|
# Approve junior API"""
|
||||||
|
router.register('approve-task', ApproveTaskAPIView, basename='approve-task')
|
||||||
|
# guardian list API"""
|
||||||
|
router.register('guardian-list', GuardianListAPIView, basename='guardian-list')
|
||||||
# Define Url pattern"""
|
# Define Url pattern"""
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('api/v1/', include(router.urls)),
|
path('api/v1/', include(router.urls)),
|
||||||
|
|||||||
+110
-8
@@ -3,8 +3,26 @@
|
|||||||
import oss2
|
import oss2
|
||||||
"""Import setting"""
|
"""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 tempfile
|
||||||
|
# Import date time module's function
|
||||||
|
from datetime import datetime, time
|
||||||
|
# import Number constant
|
||||||
|
from base.constants import NUMBER, GUARDIAN
|
||||||
|
# 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
|
# Define upload image on
|
||||||
# ali baba cloud
|
# ali baba cloud
|
||||||
@@ -13,6 +31,12 @@ import tempfile
|
|||||||
# then check bucket name
|
# then check bucket name
|
||||||
# then upload on ali baba
|
# then upload on ali baba
|
||||||
# bucket and reform the image url"""
|
# 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"""
|
"""upload image on oss alibaba bucket"""
|
||||||
@@ -20,13 +44,91 @@ def upload_image_to_alibaba(image, filename):
|
|||||||
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
||||||
"""write image in temporary file"""
|
"""write image in temporary file"""
|
||||||
temp_file.write(image.read())
|
temp_file.write(image.read())
|
||||||
"""auth of bucket"""
|
return upload_file_to_alibaba(temp_file, filename)
|
||||||
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
|
|
||||||
"""fetch bucket details"""
|
|
||||||
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
|
def upload_base64_image_to_alibaba(image, filename):
|
||||||
# Upload the temporary file to Alibaba OSS
|
"""
|
||||||
bucket.put_object_from_file(filename, temp_file.name)
|
upload image on oss alibaba bucket
|
||||||
"""create perfect url for image"""
|
"""
|
||||||
new_filename = filename.replace(' ', '%20')
|
# Save the image object to a temporary file
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
||||||
|
# write image in temporary file
|
||||||
|
temp_file.write(image)
|
||||||
|
return upload_file_to_alibaba(temp_file, filename)
|
||||||
|
|
||||||
|
|
||||||
|
def upload_excel_file_to_alibaba(response, filename):
|
||||||
|
"""
|
||||||
|
upload excel file on oss alibaba bucket
|
||||||
|
"""
|
||||||
|
# Save the image object to a temporary file
|
||||||
|
with tempfile.NamedTemporaryFile(delete=False) as temp_file:
|
||||||
|
# write image in temporary file
|
||||||
|
temp_file.write(response.content)
|
||||||
|
return upload_file_to_alibaba(temp_file, filename)
|
||||||
|
|
||||||
|
|
||||||
|
def upload_file_to_alibaba(temp_file, filename):
|
||||||
|
"""auth of bucket"""
|
||||||
|
auth = oss2.Auth(settings.ALIYUN_OSS_ACCESS_KEY_ID, settings.ALIYUN_OSS_ACCESS_KEY_SECRET)
|
||||||
|
"""fetch bucket details"""
|
||||||
|
bucket = oss2.Bucket(auth, settings.ALIYUN_OSS_ENDPOINT, settings.ALIYUN_OSS_BUCKET_NAME)
|
||||||
|
# Upload the temporary file to Alibaba OSS
|
||||||
|
bucket.put_object_from_file(filename, temp_file.name)
|
||||||
|
"""create perfect url for image"""
|
||||||
|
new_filename = filename.replace(' ', '%20')
|
||||||
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}"
|
return f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{new_filename}"
|
||||||
|
|
||||||
|
|
||||||
|
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, GUARDIAN, 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
-102
@@ -1,203 +1,410 @@
|
|||||||
"""Views of Guardian"""
|
"""Views of Guardian"""
|
||||||
"""Third party Django app"""
|
import math
|
||||||
|
|
||||||
|
# django imports
|
||||||
|
# Import IsAuthenticated
|
||||||
|
# Import viewsets and status
|
||||||
|
# Import PageNumberPagination
|
||||||
|
# Import User
|
||||||
|
# Import timezone
|
||||||
|
from django.db.models import F, Window
|
||||||
|
from django.db.models.functions.window import Rank
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework import viewsets, status
|
from rest_framework import viewsets, status
|
||||||
from rest_framework.pagination import PageNumberPagination
|
from rest_framework.pagination import PageNumberPagination
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
|
from base.constants import guardian_code_tuple
|
||||||
|
from rest_framework.filters import SearchFilter
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from PIL import Image
|
|
||||||
from datetime import datetime, timedelta
|
from base.pagination import CustomPageNumberPagination
|
||||||
"""Import Django app"""
|
# 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,
|
from .serializers import (UserSerializer, CreateGuardianSerializer, TaskSerializer, TaskDetailsSerializer,
|
||||||
TopJuniorSerializer, ApproveJuniorSerializer)
|
TopJuniorSerializer, ApproveJuniorSerializer, ApproveTaskSerializer,
|
||||||
|
GuardianDetailListSerializer)
|
||||||
from .models import Guardian, JuniorTask
|
from .models import Guardian, JuniorTask
|
||||||
from junior.models import Junior, JuniorPoints
|
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship
|
||||||
from junior.serializers import JuniorDetailSerializer
|
from account.models import UserEmailOtp, UserNotification, UserDeviceDetails
|
||||||
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, send_otp_email, task_status_fun
|
||||||
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 base.constants import NUMBER, GUARDIAN_CODE_STATUS, GUARDIAN
|
||||||
from .utils import upload_image_to_alibaba
|
from .utils import upload_image_to_alibaba
|
||||||
from django.db.models import Sum
|
from notifications.constants import REGISTRATION, TASK_ASSIGNED, ASSOCIATE_APPROVED, ASSOCIATE_REJECTED
|
||||||
|
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()
|
queryset = User.objects.all()
|
||||||
serializer_class = UserSerializer
|
serializer_class = UserSerializer
|
||||||
|
http_method_names = ('post',)
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Create user profile"""
|
"""Create user profile"""
|
||||||
if request.data['user_type'] in ['1', '2']:
|
device_id = request.META.get('HTTP_DEVICE_ID')
|
||||||
|
if request.data['user_type'] in [str(NUMBER['one']), str(NUMBER['two'])]:
|
||||||
serializer = UserSerializer(context=request.data['user_type'], data=request.data)
|
serializer = UserSerializer(context=request.data['user_type'], data=request.data)
|
||||||
if serializer.is_valid():
|
if serializer.is_valid():
|
||||||
serializer.save()
|
user = serializer.save()
|
||||||
"""Generate otp"""
|
"""Generate otp"""
|
||||||
otp = generate_otp()
|
otp = generate_otp()
|
||||||
|
# expire otp after 1 day
|
||||||
expiry = timezone.now() + timezone.timedelta(days=1)
|
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||||
|
# create user email otp object
|
||||||
UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
|
UserEmailOtp.objects.create(email=request.data['email'], otp=otp,
|
||||||
user_type=str(request.data['user_type']), expired_at=expiry)
|
user_type=str(request.data['user_type']), expired_at=expiry)
|
||||||
"""Send email to the register user"""
|
"""Send email to the register user"""
|
||||||
send_otp_email(request.data['email'], otp)
|
send_otp_email.delay(request.data['email'], otp)
|
||||||
return custom_response(SUCCESS_CODE['3001'], {"email_otp": otp},
|
UserDeviceDetails.objects.create(user=user, device_id=device_id)
|
||||||
|
return custom_response(SUCCESS_CODE['3001'],
|
||||||
response_status=status.HTTP_200_OK)
|
response_status=status.HTTP_200_OK)
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
else:
|
else:
|
||||||
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(ERROR_CODE['2028'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
class UpdateGuardianProfile(viewsets.ViewSet):
|
|
||||||
|
class UpdateGuardianProfile(viewsets.ModelViewSet):
|
||||||
"""Update guardian profile"""
|
"""Update guardian profile"""
|
||||||
queryset = Guardian.objects.all()
|
|
||||||
serializer_class = CreateGuardianSerializer
|
serializer_class = CreateGuardianSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('post',)
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Create guardian profile"""
|
"""Create guardian profile"""
|
||||||
data = request.data
|
try:
|
||||||
image = request.data.get('image')
|
data = request.data
|
||||||
image_url = ''
|
image = request.data.get('image')
|
||||||
if image:
|
image_url = ''
|
||||||
if image and image.size == NUMBER['zero']:
|
if image:
|
||||||
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
|
if image and image.size == NUMBER['zero']:
|
||||||
filename = f"images/{image.name}"
|
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
image_url = upload_image_to_alibaba(image, filename)
|
filename = f"images/{image.name}"
|
||||||
data = {"image":image_url}
|
# upload image on ali baba
|
||||||
serializer = CreateGuardianSerializer(context={"user":request.user,
|
image_url = upload_image_to_alibaba(image, filename)
|
||||||
"first_name":request.data.get('first_name'),
|
data = {"image":image_url}
|
||||||
"last_name": request.data.get('last_name'),
|
serializer = CreateGuardianSerializer(context={"user":request.user,
|
||||||
"image":image_url},
|
"first_name":request.data.get('first_name'),
|
||||||
data=data)
|
"last_name": request.data.get('last_name'),
|
||||||
if serializer.is_valid():
|
"image":image_url},
|
||||||
"""save serializer"""
|
data=data)
|
||||||
serializer.save()
|
if serializer.is_valid():
|
||||||
return custom_response(None, serializer.data,response_status=status.HTTP_200_OK)
|
"""save serializer"""
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
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):
|
class AllTaskListAPIView(viewsets.ModelViewSet):
|
||||||
"""Update guardian profile"""
|
"""Update guardian profile"""
|
||||||
serializer_class = TaskDetailsSerializer
|
serializer_class = TaskDetailsSerializer
|
||||||
queryset = JuniorTask.objects.all()
|
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
"""Create guardian profile"""
|
"""Create guardian profile"""
|
||||||
queryset = JuniorTask.objects.filter(guardian__user=request.user)
|
queryset = JuniorTask.objects.filter(guardian__user=request.user)
|
||||||
|
# use TaskDetailsSerializer serializer
|
||||||
serializer = TaskDetailsSerializer(queryset, many=True)
|
serializer = TaskDetailsSerializer(queryset, many=True)
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|
||||||
class TaskListAPIView(viewsets.ModelViewSet):
|
class TaskListAPIView(viewsets.ModelViewSet):
|
||||||
"""Update guardian profile"""
|
"""Task list
|
||||||
|
Params
|
||||||
|
status
|
||||||
|
search
|
||||||
|
page
|
||||||
|
junior"""
|
||||||
serializer_class = TaskDetailsSerializer
|
serializer_class = TaskDetailsSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
filter_backends = (SearchFilter,)
|
||||||
|
search_fields = ['task_name', ]
|
||||||
pagination_class = PageNumberPagination
|
pagination_class = PageNumberPagination
|
||||||
queryset = JuniorTask.objects.all()
|
http_method_names = ('get',)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = JuniorTask.objects.filter(guardian__user=self.request.user
|
||||||
|
).select_related('junior', 'junior__auth'
|
||||||
|
).order_by('-created_at')
|
||||||
|
|
||||||
|
queryset = self.filter_queryset(queryset)
|
||||||
|
return queryset
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
"""Create guardian profile"""
|
"""Create guardian profile"""
|
||||||
status_value = self.request.GET.get('status')
|
status_value = self.request.GET.get('status')
|
||||||
if str(status_value) == '0':
|
junior = self.request.GET.get('junior')
|
||||||
queryset = JuniorTask.objects.filter(guardian__user=request.user).order_by('due_date', 'created_at')
|
queryset = self.get_queryset()
|
||||||
else:
|
task_status = task_status_fun(status_value)
|
||||||
queryset = JuniorTask.objects.filter(guardian__user=request.user,
|
if status_value:
|
||||||
task_status=status_value).order_by('due_date','created_at')
|
queryset = queryset.filter(task_status__in=task_status)
|
||||||
paginator = self.pagination_class()
|
if junior:
|
||||||
|
queryset = queryset.filter(junior=int(junior))
|
||||||
|
paginator = CustomPageNumberPagination()
|
||||||
|
# use Pagination
|
||||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||||
serializer = TaskDetailsSerializer(paginated_queryset, many=True)
|
# use TaskDetailsSerializer serializer
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||||
|
return paginator.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
|
|
||||||
class CreateTaskAPIView(viewsets.ModelViewSet):
|
class CreateTaskAPIView(viewsets.ModelViewSet):
|
||||||
"""create task for junior"""
|
"""create task for junior"""
|
||||||
serializer_class = TaskSerializer
|
serializer_class = TaskSerializer
|
||||||
queryset = JuniorTask.objects.all()
|
queryset = JuniorTask.objects.all()
|
||||||
|
http_method_names = ('post', )
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
image = request.data['default_image']
|
"""
|
||||||
data = request.data
|
image should be in form data
|
||||||
if 'https' in str(image):
|
"""
|
||||||
image_data = image
|
try:
|
||||||
else:
|
image = request.data['default_image']
|
||||||
filename = f"images/{image}"
|
junior_ids = request.data['junior'].split(',')
|
||||||
if image and image.size == NUMBER['zero']:
|
|
||||||
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
|
invalid_junior_ids = [junior_id for junior_id in junior_ids if not junior_id.isnumeric()]
|
||||||
image_url = upload_image_to_alibaba(image, filename)
|
if invalid_junior_ids:
|
||||||
image_data = image_url
|
# At least one junior value is not an integer
|
||||||
data.pop('default_image')
|
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
serializer = TaskSerializer(context={"user":request.user, "image":image_data}, data=data)
|
|
||||||
if serializer.is_valid():
|
allowed_extensions = ['.jpg', '.jpeg', '.png']
|
||||||
serializer.save()
|
if not any(extension in str(image) for extension in allowed_extensions):
|
||||||
return custom_response(SUCCESS_CODE['3018'], serializer.data, response_status=status.HTTP_200_OK)
|
return custom_error_response(ERROR_CODE['2048'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
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_data = upload_image_to_alibaba(image, filename)
|
||||||
|
request.data.pop('default_image')
|
||||||
|
|
||||||
|
guardian = Guardian.objects.filter(user=request.user).select_related('user').last()
|
||||||
|
junior_data = Junior.objects.filter(id__in=junior_ids,
|
||||||
|
guardian_code__contains=[guardian.guardian_code]
|
||||||
|
).select_related('auth')
|
||||||
|
if not junior_data:
|
||||||
|
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
for junior in junior_data:
|
||||||
|
index = junior.guardian_code.index(guardian.guardian_code)
|
||||||
|
status_index = junior.guardian_code_status[index]
|
||||||
|
if status_index == str(NUMBER['three']):
|
||||||
|
return custom_error_response(ERROR_CODE['2078'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
# use TaskSerializer serializer
|
||||||
|
serializer = TaskSerializer(context={"guardian": guardian, "image": image_data,
|
||||||
|
"junior_data": junior_data}, data=request.data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
# save serializer
|
||||||
|
serializer.save()
|
||||||
|
# removed send notification method and used in serializer
|
||||||
|
return custom_response(SUCCESS_CODE['3018'], 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):
|
class SearchTaskListAPIView(viewsets.ModelViewSet):
|
||||||
"""Update guardian profile"""
|
"""Filter task"""
|
||||||
serializer_class = TaskDetailsSerializer
|
serializer_class = TaskDetailsSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
pagination_class = PageNumberPagination
|
pagination_class = PageNumberPagination
|
||||||
queryset = JuniorTask.objects.all()
|
http_method_names = ('get',)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Get the queryset for the view"""
|
"""Get the queryset for the view"""
|
||||||
title = self.request.GET.get('title')
|
title = self.request.GET.get('title')
|
||||||
|
# fetch junior query
|
||||||
junior_queryset = JuniorTask.objects.filter(guardian__user=self.request.user, task_name__icontains=title)\
|
junior_queryset = JuniorTask.objects.filter(guardian__user=self.request.user, task_name__icontains=title)\
|
||||||
.order_by('due_date', 'created_at')
|
.order_by('due_date', 'created_at')
|
||||||
return junior_queryset
|
return junior_queryset
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
"""Create guardian profile"""
|
"""Filter task"""
|
||||||
queryset = self.get_queryset()
|
try:
|
||||||
|
queryset = self.get_queryset()
|
||||||
paginator = self.pagination_class()
|
paginator = self.pagination_class()
|
||||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
# use pagination
|
||||||
|
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||||
serializer = TaskDetailsSerializer(paginated_queryset, many=True)
|
# use TaskSerializer serializer
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
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):
|
class TopJuniorListAPIView(viewsets.ModelViewSet):
|
||||||
"""Top juniors list"""
|
"""Top juniors list
|
||||||
|
No Params"""
|
||||||
serializer_class = TopJuniorSerializer
|
serializer_class = TopJuniorSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
queryset = JuniorPoints.objects.all()
|
http_method_names = ('get',)
|
||||||
|
|
||||||
def get_serializer_context(self):
|
def get_serializer_context(self):
|
||||||
|
# context list
|
||||||
context = super().get_serializer_context()
|
context = super().get_serializer_context()
|
||||||
context.update({'view': self})
|
context.update({'view': self})
|
||||||
return context
|
return context
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = JuniorPoints.objects.filter(
|
||||||
|
junior__is_verified=True
|
||||||
|
).select_related(
|
||||||
|
'junior', 'junior__auth'
|
||||||
|
).annotate(rank=Window(
|
||||||
|
expression=Rank(),
|
||||||
|
order_by=[F('total_points').desc(), 'junior__created_at'])
|
||||||
|
).order_by('-total_points', 'junior__created_at')
|
||||||
|
return queryset
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
"""Fetch junior list of those who complete their tasks"""
|
"""Fetch junior list of those who complete their tasks"""
|
||||||
junior_total_points = self.get_queryset().order_by('-total_task_points')
|
try:
|
||||||
|
junior_total_points = self.get_queryset()[:15]
|
||||||
# Update the position field for each JuniorPoints object
|
serializer = self.get_serializer(junior_total_points, many=True)
|
||||||
for index, junior in enumerate(junior_total_points):
|
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||||
junior.position = index + 1
|
except Exception as e:
|
||||||
junior.save()
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
serializer = self.get_serializer(junior_total_points, many=True)
|
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
|
|
||||||
class ApproveJuniorAPIView(viewsets.ViewSet):
|
class ApproveJuniorAPIView(viewsets.ModelViewSet):
|
||||||
"""approve junior by guardian"""
|
"""approve junior by guardian"""
|
||||||
serializer_class = ApproveJuniorSerializer
|
serializer_class = ApproveJuniorSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('post',)
|
||||||
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(id=self.request.data.get('junior_id')).last()
|
|
||||||
return guardian, junior_queryset
|
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
""" junior list"""
|
""" Use below param
|
||||||
queryset = self.get_queryset()
|
{"junior_id":"75",
|
||||||
if request.data['action'] == '1':
|
"action":"1"}
|
||||||
serializer = ApproveJuniorSerializer(context={"guardian_code": queryset[0].guardian_code,
|
"""
|
||||||
"junior": queryset[1], "action": request.data['action']},
|
try:
|
||||||
data=request.data)
|
relation_obj = JuniorGuardianRelationship.objects.filter(
|
||||||
if serializer.is_valid():
|
guardian__user__email=self.request.user,
|
||||||
|
junior__id=self.request.data.get('junior_id')
|
||||||
|
).select_related('guardian', 'junior').first()
|
||||||
|
guardian = relation_obj.guardian
|
||||||
|
# fetch junior query
|
||||||
|
junior_queryset = relation_obj.junior
|
||||||
|
if junior_queryset and (junior_queryset.is_deleted or not junior_queryset.is_active):
|
||||||
|
return custom_error_response(ERROR_CODE['2073'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
# action 1 is use for approve and 2 for reject
|
||||||
|
if request.data['action'] == '1':
|
||||||
|
# use ApproveJuniorSerializer serializer
|
||||||
|
serializer = ApproveJuniorSerializer(context={"guardian_code": guardian.guardian_code,
|
||||||
|
"junior": junior_queryset,
|
||||||
|
"action": request.data['action']},
|
||||||
|
data=request.data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
# save serializer
|
||||||
|
serializer.save()
|
||||||
|
send_notification.delay(ASSOCIATE_APPROVED, guardian.user_id, GUARDIAN,
|
||||||
|
junior_queryset.auth_id, {})
|
||||||
|
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
else:
|
||||||
|
if junior_queryset.guardian_code and ('-' in junior_queryset.guardian_code):
|
||||||
|
junior_queryset.guardian_code.remove('-')
|
||||||
|
if junior_queryset.guardian_code_status and ('-' in junior_queryset.guardian_code_status):
|
||||||
|
junior_queryset.guardian_code_status.remove('-')
|
||||||
|
index = junior_queryset.guardian_code.index(guardian.guardian_code)
|
||||||
|
junior_queryset.guardian_code.remove(guardian.guardian_code)
|
||||||
|
junior_queryset.guardian_code_status.pop(index)
|
||||||
|
junior_queryset.save()
|
||||||
|
send_notification.delay(ASSOCIATE_REJECTED, guardian.user_id, GUARDIAN, junior_queryset.auth_id, {})
|
||||||
|
relation_obj.delete()
|
||||||
|
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.ModelViewSet):
|
||||||
|
"""approve junior by guardian"""
|
||||||
|
serializer_class = ApproveTaskSerializer
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('post',)
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
""" Params
|
||||||
|
{"junior_id":"82",
|
||||||
|
"task_id":"43",
|
||||||
|
"action":"1"}
|
||||||
|
action 1 for approve
|
||||||
|
2 for reject
|
||||||
|
"""
|
||||||
|
# action 1 is use for approve and 2 for reject
|
||||||
|
try:
|
||||||
|
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
||||||
|
# task query
|
||||||
|
task_queryset = JuniorTask.objects.filter(id=self.request.data.get('task_id'),
|
||||||
|
guardian=guardian,
|
||||||
|
junior=self.request.data.get('junior_id')).last()
|
||||||
|
if task_queryset and guardian.guardian_code not in task_queryset.junior.guardian_code:
|
||||||
|
return custom_error_response(ERROR_CODE['2084'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
if task_queryset and (task_queryset.junior.is_deleted or not task_queryset.junior.is_active):
|
||||||
|
return custom_error_response(ERROR_CODE['2072'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
# use ApproveJuniorSerializer serializer
|
||||||
|
serializer = ApproveTaskSerializer(context={"guardian_code": guardian.guardian_code,
|
||||||
|
"task_instance": task_queryset,
|
||||||
|
"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 task_queryset and task_queryset.task_status not in unexpected_task_status):
|
||||||
|
# save serializer
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return custom_response(SUCCESS_CODE['3023'], serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3025'], response_status=status.HTTP_200_OK)
|
||||||
else:
|
elif (str(request.data['action']) == str(NUMBER['two']) and serializer.is_valid()
|
||||||
return custom_response(SUCCESS_CODE['3024'], response_status=status.HTTP_200_OK)
|
and task_queryset and task_queryset.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_error_response(ERROR_CODE['2038'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
#
|
||||||
|
class GuardianListAPIView(viewsets.ModelViewSet):
|
||||||
|
"""Guardian list of assosicated junior"""
|
||||||
|
|
||||||
|
serializer_class = GuardianDetailListSerializer
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('get',)
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
""" Guardian list of assosicated junior
|
||||||
|
No Params"""
|
||||||
|
try:
|
||||||
|
guardian_data = JuniorGuardianRelationship.objects.filter(junior__auth__email=self.request.user)
|
||||||
|
# fetch junior object
|
||||||
|
if guardian_data:
|
||||||
|
# use GuardianDetailListSerializer serializer
|
||||||
|
serializer = GuardianDetailListSerializer(guardian_data, many=True)
|
||||||
|
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
return custom_response({"status": GUARDIAN_CODE_STATUS[1][0]}, response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|||||||
+31
-2
@@ -2,8 +2,27 @@
|
|||||||
"""Third party Django app"""
|
"""Third party Django app"""
|
||||||
from django.contrib import admin
|
from django.contrib import admin
|
||||||
"""Import Django app"""
|
"""Import Django app"""
|
||||||
from .models import Junior, JuniorPoints
|
from .models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
|
||||||
|
JuniorArticleCard, FAQ)
|
||||||
# Register your models here.
|
# Register your models here.
|
||||||
|
admin.site.register(FAQ)
|
||||||
|
@admin.register(JuniorArticle)
|
||||||
|
class JuniorArticleAdmin(admin.ModelAdmin):
|
||||||
|
"""Junior Admin"""
|
||||||
|
list_display = ['junior', 'article', 'status', 'is_completed']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return email id"""
|
||||||
|
return self.junior__auth__email
|
||||||
|
|
||||||
|
@admin.register(JuniorArticleCard)
|
||||||
|
class JuniorArticleCardAdmin(admin.ModelAdmin):
|
||||||
|
"""Junior Admin"""
|
||||||
|
list_display = ['junior', 'article', 'article_card', 'is_read']
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return email id"""
|
||||||
|
return self.junior__auth__email
|
||||||
@admin.register(Junior)
|
@admin.register(Junior)
|
||||||
class JuniorAdmin(admin.ModelAdmin):
|
class JuniorAdmin(admin.ModelAdmin):
|
||||||
"""Junior Admin"""
|
"""Junior Admin"""
|
||||||
@@ -16,8 +35,18 @@ class JuniorAdmin(admin.ModelAdmin):
|
|||||||
@admin.register(JuniorPoints)
|
@admin.register(JuniorPoints)
|
||||||
class JuniorPointsAdmin(admin.ModelAdmin):
|
class JuniorPointsAdmin(admin.ModelAdmin):
|
||||||
"""Junior Points Admin"""
|
"""Junior Points Admin"""
|
||||||
list_display = ['junior', 'total_task_points', 'position']
|
list_display = ['junior', 'total_points', 'position']
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return email id"""
|
"""Return email id"""
|
||||||
return self.junior.auth.email
|
return self.junior.auth.email
|
||||||
|
|
||||||
|
@admin.register(JuniorGuardianRelationship)
|
||||||
|
class JuniorGuardianRelationshipAdmin(admin.ModelAdmin):
|
||||||
|
"""Junior Admin"""
|
||||||
|
list_display = ['guardian', 'junior', 'relationship']
|
||||||
|
|
||||||
|
@admin.register(JuniorArticlePoints)
|
||||||
|
class JuniorArticlePointsAdmin(admin.ModelAdmin):
|
||||||
|
"""Junior Admin"""
|
||||||
|
list_display = ['junior', 'article', 'question', 'submitted_answer', 'is_answer_correct']
|
||||||
|
|||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-07-18 09:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0013_alter_junior_options_alter_juniorpoints_options'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='junior',
|
||||||
|
name='is_password_set',
|
||||||
|
field=models.BooleanField(default=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-07-19 09:40
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0014_junior_is_password_set'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RenameField(
|
||||||
|
model_name='juniorpoints',
|
||||||
|
old_name='total_task_points',
|
||||||
|
new_name='total_points',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-07-19 11:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0015_rename_total_task_points_juniorpoints_total_points'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='juniorpoints',
|
||||||
|
name='referral_points',
|
||||||
|
field=models.IntegerField(blank=True, default=0, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-07-25 07:44
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('guardian', '0020_alter_juniortask_task_status'),
|
||||||
|
('junior', '0016_juniorpoints_referral_points'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='JuniorGuardianRelationship',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('relationship', models.CharField(blank=True, choices=[('1', 'parent'), ('2', 'legal_guardian')], default='1', max_length=31, null=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('guardian', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='guardian_relation', to='guardian.guardian', verbose_name='Guardian')),
|
||||||
|
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_relation', to='junior.junior', verbose_name='Junior')),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'Junior Guardian Relation',
|
||||||
|
'verbose_name_plural': 'Junior Guardian Relation',
|
||||||
|
'db_table': 'junior_guardian_relation',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-02 11:27
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0017_juniorguardianrelationship'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='junior',
|
||||||
|
name='relationship',
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='junior',
|
||||||
|
name='guardian_code_approved',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-07 13:29
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('web_admin', '0004_alter_surveyoption_survey'),
|
||||||
|
('junior', '0018_remove_junior_relationship_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='JuniorArticlePoints',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('earn_points', models.IntegerField(blank=True, default=5, null=True)),
|
||||||
|
('is_attempt', models.BooleanField(default=False)),
|
||||||
|
('is_answer_correct', models.BooleanField(default=False)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles', to='web_admin.article')),
|
||||||
|
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_details', to='junior.junior', verbose_name='Junior')),
|
||||||
|
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='questions', to='web_admin.articlesurvey')),
|
||||||
|
('submitted_answer', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='submitted_answer', to='web_admin.surveyoption')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-08 05:43
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0019_juniorarticlepoints'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='junior',
|
||||||
|
name='guardian_code_status',
|
||||||
|
field=models.CharField(blank=True, choices=[('1', 'no guardian code'), ('2', 'exist guardian code'), ('3', 'request for guardian code')], default='1', max_length=31, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-08 09:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('web_admin', '0004_alter_surveyoption_survey'),
|
||||||
|
('junior', '0020_junior_guardian_code_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='juniorarticlepoints',
|
||||||
|
name='submitted_answer',
|
||||||
|
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='submitted_answer', to='web_admin.surveyoption'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-09 09:34
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('web_admin', '0004_alter_surveyoption_survey'),
|
||||||
|
('junior', '0021_alter_juniorarticlepoints_submitted_answer'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='JuniorArticle',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('is_completed', models.BooleanField(default=False)),
|
||||||
|
('status', models.CharField(blank=True, choices=[('1', 'read'), ('2', 'in_progress'), ('3', 'completed')], default='1', max_length=10, null=True)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles_details', to='web_admin.article')),
|
||||||
|
('junior', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article', to='junior.junior', verbose_name='Junior')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-09 10:47
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('web_admin', '0004_alter_surveyoption_survey'),
|
||||||
|
('junior', '0022_juniorarticle'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='JuniorArticleCard',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('is_read', models.BooleanField(default=False)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
('article', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_articles_detail', to='web_admin.article')),
|
||||||
|
('article_card', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='junior_article_card', to='web_admin.articlecard')),
|
||||||
|
('junior', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article_card', to='junior.junior', verbose_name='Junior')),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-10 08:53
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0023_juniorarticlecard'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='juniorarticle',
|
||||||
|
name='current_card_page',
|
||||||
|
field=models.IntegerField(blank=True, default=0, null=True),
|
||||||
|
),
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='juniorarticle',
|
||||||
|
name='current_que_page',
|
||||||
|
field=models.IntegerField(blank=True, default=0, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-10 14:46
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0024_juniorarticle_current_card_page_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='juniorarticle',
|
||||||
|
name='junior',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='juniors_article', to='junior.junior', verbose_name='Junior'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-17 09:04
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0025_alter_juniorarticle_junior'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.CreateModel(
|
||||||
|
name='FAQ',
|
||||||
|
fields=[
|
||||||
|
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||||
|
('question', models.IntegerField(max_length=100)),
|
||||||
|
('description', models.CharField(max_length=500)),
|
||||||
|
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||||
|
('updated_at', models.DateTimeField(auto_now=True)),
|
||||||
|
],
|
||||||
|
options={
|
||||||
|
'verbose_name': 'FAQ',
|
||||||
|
'verbose_name_plural': 'FAQ',
|
||||||
|
},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='juniorarticle',
|
||||||
|
options={'verbose_name': 'Junior Article', 'verbose_name_plural': 'Junior Article'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='juniorarticlecard',
|
||||||
|
options={'verbose_name': 'Junior Article Card', 'verbose_name_plural': 'Junior Article Card'},
|
||||||
|
),
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='juniorarticlepoints',
|
||||||
|
options={'verbose_name': 'Junior Article Points', 'verbose_name_plural': 'Junior Article Points'},
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-17 09:37
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0026_faq_alter_juniorarticle_options_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='faq',
|
||||||
|
name='question',
|
||||||
|
field=models.CharField(max_length=100),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-17 10:28
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0027_alter_faq_question'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='faq',
|
||||||
|
name='status',
|
||||||
|
field=models.IntegerField(blank=True, default=1, null=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-17 12:45
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0028_faq_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='junior',
|
||||||
|
name='is_deleted',
|
||||||
|
field=models.BooleanField(default=False),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-26 08:59
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0029_junior_is_deleted'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.RemoveField(
|
||||||
|
model_name='junior',
|
||||||
|
name='guardian_code_status',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-26 08:59
|
||||||
|
|
||||||
|
import django.contrib.postgres.fields
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('junior', '0030_remove_junior_guardian_code_status'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='junior',
|
||||||
|
name='guardian_code_status',
|
||||||
|
field=django.contrib.postgres.fields.ArrayField(base_field=models.CharField(blank=True, default=None, max_length=10, null=True), null=True, size=None),
|
||||||
|
),
|
||||||
|
]
|
||||||
+144
-7
@@ -6,7 +6,11 @@ from django.contrib.auth import get_user_model
|
|||||||
"""Import ArrayField"""
|
"""Import ArrayField"""
|
||||||
from django.contrib.postgres.fields import ArrayField
|
from django.contrib.postgres.fields import ArrayField
|
||||||
"""Import django app"""
|
"""Import django app"""
|
||||||
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP
|
from base.constants import GENDERS, SIGNUP_METHODS, RELATIONSHIP, GUARDIAN_CODE_STATUS, ARTICLE_STATUS
|
||||||
|
# Import guardian's model
|
||||||
|
from guardian.models import Guardian
|
||||||
|
# Import web admin's model
|
||||||
|
from web_admin.models import SurveyOption, ArticleSurvey, Article, ArticleCard
|
||||||
"""Define User model"""
|
"""Define User model"""
|
||||||
User = get_user_model()
|
User = get_user_model()
|
||||||
# Create your models here.
|
# Create your models here.
|
||||||
@@ -31,7 +35,7 @@ User = get_user_model()
|
|||||||
"""Define junior points model"""
|
"""Define junior points model"""
|
||||||
# points of the junior
|
# points of the junior
|
||||||
# position 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')
|
||||||
@@ -46,9 +50,6 @@ class Junior(models.Model):
|
|||||||
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 of the junior"""
|
# Image of the junior"""
|
||||||
image = models.URLField(null=True, blank=True, default=None)
|
image = models.URLField(null=True, blank=True, default=None)
|
||||||
# relationship"""
|
|
||||||
relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True,
|
|
||||||
default='1')
|
|
||||||
# Sign up method"""
|
# Sign up method"""
|
||||||
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
|
signup_method = models.CharField(max_length=31, choices=SIGNUP_METHODS, default='1')
|
||||||
# Codes"""
|
# Codes"""
|
||||||
@@ -63,12 +64,21 @@ class Junior(models.Model):
|
|||||||
is_invited = models.BooleanField(default=False)
|
is_invited = models.BooleanField(default=False)
|
||||||
# Profile activity"""
|
# 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"""
|
# junior profile is complete or not"""
|
||||||
is_complete_profile = models.BooleanField(default=False)
|
is_complete_profile = models.BooleanField(default=False)
|
||||||
|
# junior profile deleted or not"""
|
||||||
|
is_deleted = models.BooleanField(default=False)
|
||||||
# passcode of the junior profile"""
|
# passcode of the junior profile"""
|
||||||
passcode = models.IntegerField(null=True, blank=True, default=None)
|
passcode = models.IntegerField(null=True, blank=True, default=None)
|
||||||
# junior is verified or not"""
|
# junior is verified or not"""
|
||||||
is_verified = models.BooleanField(default=False)
|
is_verified = models.BooleanField(default=False)
|
||||||
|
"""guardian code is approved or not"""
|
||||||
|
guardian_code_approved = models.BooleanField(default=False)
|
||||||
|
# # guardian code status"""
|
||||||
|
guardian_code_status = ArrayField(models.CharField(max_length=10, null=True, blank=True, default=None), null=True,
|
||||||
|
)
|
||||||
# Profile created and updated time"""
|
# Profile created and updated time"""
|
||||||
created_at = models.DateTimeField(auto_now_add=True)
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
updated_at = models.DateTimeField(auto_now=True)
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
@@ -87,8 +97,10 @@ class Junior(models.Model):
|
|||||||
class JuniorPoints(models.Model):
|
class JuniorPoints(models.Model):
|
||||||
"""Junior model"""
|
"""Junior model"""
|
||||||
junior = models.OneToOneField(Junior, on_delete=models.CASCADE, related_name='junior_points')
|
junior = models.OneToOneField(Junior, on_delete=models.CASCADE, related_name='junior_points')
|
||||||
# Contact details"""
|
# Total earned points"""
|
||||||
total_task_points = models.IntegerField(blank=True, null=True, default=0)
|
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 of the junior"""
|
||||||
position = models.IntegerField(blank=True, null=True, default=99999)
|
position = models.IntegerField(blank=True, null=True, default=99999)
|
||||||
# Profile created and updated time"""
|
# Profile created and updated time"""
|
||||||
@@ -105,3 +117,128 @@ class JuniorPoints(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
"""Return email id"""
|
"""Return email id"""
|
||||||
return f'{self.junior.auth}'
|
return f'{self.junior.auth}'
|
||||||
|
|
||||||
|
class JuniorGuardianRelationship(models.Model):
|
||||||
|
"""Junior Guardian relationship model"""
|
||||||
|
guardian = models.ForeignKey(Guardian, on_delete=models.CASCADE, related_name='guardian_relation',
|
||||||
|
verbose_name='Guardian')
|
||||||
|
# associated junior with the task
|
||||||
|
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='junior_relation', verbose_name='Junior')
|
||||||
|
# relation between guardian and junior"""
|
||||||
|
relationship = models.CharField(max_length=31, choices=RELATIONSHIP, null=True, blank=True,
|
||||||
|
default='1')
|
||||||
|
"""Profile created and updated time"""
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
""" Meta class """
|
||||||
|
db_table = 'junior_guardian_relation'
|
||||||
|
"""verbose name of the model"""
|
||||||
|
verbose_name = 'Junior Guardian Relation'
|
||||||
|
verbose_name_plural = 'Junior Guardian Relation'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return email id"""
|
||||||
|
return f'{self.guardian.user}'
|
||||||
|
|
||||||
|
|
||||||
|
class JuniorArticlePoints(models.Model):
|
||||||
|
"""
|
||||||
|
Survey Options model
|
||||||
|
"""
|
||||||
|
# associated junior with the task
|
||||||
|
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_details', verbose_name='Junior')
|
||||||
|
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles')
|
||||||
|
question = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='questions')
|
||||||
|
submitted_answer = models.ForeignKey(SurveyOption, on_delete=models.SET_NULL, null=True,
|
||||||
|
related_name='submitted_answer')
|
||||||
|
# earn points"""
|
||||||
|
earn_points = models.IntegerField(blank=True, null=True, default=5)
|
||||||
|
is_attempt = models.BooleanField(default=False)
|
||||||
|
is_answer_correct = models.BooleanField(default=False)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
""" Meta class """
|
||||||
|
verbose_name = 'Junior Article Points'
|
||||||
|
# another name of the model"""
|
||||||
|
verbose_name_plural = 'Junior Article Points'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return title"""
|
||||||
|
return f'{self.id} | {self.question}'
|
||||||
|
|
||||||
|
class JuniorArticle(models.Model):
|
||||||
|
"""
|
||||||
|
Survey Options model
|
||||||
|
"""
|
||||||
|
# associated junior with the task
|
||||||
|
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_article',
|
||||||
|
verbose_name='Junior')
|
||||||
|
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles_details')
|
||||||
|
# article completed"""
|
||||||
|
is_completed = models.BooleanField(default=False)
|
||||||
|
status = models.CharField(max_length=10, choices=ARTICLE_STATUS, null=True, blank=True, default='1')
|
||||||
|
current_card_page = models.IntegerField(blank=True, null=True, default=0)
|
||||||
|
current_que_page = models.IntegerField(blank=True, null=True, default=0)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
""" Meta class """
|
||||||
|
verbose_name = 'Junior Article'
|
||||||
|
# another name of the model"""
|
||||||
|
verbose_name_plural = 'Junior Article'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return title"""
|
||||||
|
return f'{self.id} | {self.article}'
|
||||||
|
|
||||||
|
class JuniorArticleCard(models.Model):
|
||||||
|
"""
|
||||||
|
Survey Options model
|
||||||
|
"""
|
||||||
|
# associated junior with the task
|
||||||
|
junior = models.ForeignKey(Junior, on_delete=models.CASCADE, related_name='juniors_article_card',
|
||||||
|
verbose_name='Junior')
|
||||||
|
article = models.ForeignKey(Article, on_delete=models.CASCADE, related_name='junior_articles_detail')
|
||||||
|
article_card = models.ForeignKey(ArticleCard, on_delete=models.CASCADE, related_name='junior_article_card')
|
||||||
|
|
||||||
|
# article card read"""
|
||||||
|
is_read = models.BooleanField(default=False)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
""" Meta class """
|
||||||
|
verbose_name = 'Junior Article Card'
|
||||||
|
# another name of the model"""
|
||||||
|
verbose_name_plural = 'Junior Article Card'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return title"""
|
||||||
|
return f'{self.id} | {self.article}'
|
||||||
|
|
||||||
|
|
||||||
|
class FAQ(models.Model):
|
||||||
|
"""FAQ model"""
|
||||||
|
# questions"""
|
||||||
|
question = models.CharField(max_length=100)
|
||||||
|
# answer"""
|
||||||
|
description = models.CharField(max_length=500)
|
||||||
|
# status
|
||||||
|
status = models.IntegerField(default=1, null=True, blank=True)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
""" Meta class """
|
||||||
|
verbose_name = 'FAQ'
|
||||||
|
# another name of the model"""
|
||||||
|
verbose_name_plural = 'FAQ'
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return email id"""
|
||||||
|
return f'{self.question}'
|
||||||
|
|||||||
+288
-45
@@ -1,22 +1,30 @@
|
|||||||
"""Serializer file for junior"""
|
"""Serializer file for junior"""
|
||||||
"""Import Django 3rd party app"""
|
# third party imports
|
||||||
|
import pytz
|
||||||
|
|
||||||
|
# django imports
|
||||||
from rest_framework import serializers
|
from rest_framework import serializers
|
||||||
from django.contrib.auth.models import User
|
from django.contrib.auth.models import User
|
||||||
from django.db import transaction
|
from django.db import transaction
|
||||||
import random
|
from datetime import datetime
|
||||||
from django.utils import timezone
|
from django.utils import timezone
|
||||||
from rest_framework_simplejwt.tokens import RefreshToken
|
from rest_framework_simplejwt.tokens import RefreshToken
|
||||||
|
|
||||||
"""Import django app"""
|
# local imports
|
||||||
from account.utils import send_otp_email, generate_code
|
from account.utils import send_otp_email, generate_code, make_special_password
|
||||||
from junior.models import Junior, JuniorPoints
|
from junior.models import Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, FAQ
|
||||||
from guardian.tasks import generate_otp
|
from guardian.tasks import generate_otp
|
||||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
from base.constants import PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD
|
from base.constants import (PENDING, IN_PROGRESS, REJECTED, REQUESTED, COMPLETED, NUMBER, JUN, ZOD, EXPIRED,
|
||||||
|
GUARDIAN_CODE_STATUS, JUNIOR, GUARDIAN)
|
||||||
from guardian.models import Guardian, JuniorTask
|
from guardian.models import Guardian, JuniorTask
|
||||||
from account.models import UserEmailOtp
|
from account.models import UserEmailOtp, UserNotification
|
||||||
from junior.utils import junior_notification_email, junior_approval_mail
|
from junior.utils import junior_notification_email, junior_approval_mail, get_junior_leaderboard_rank
|
||||||
|
from guardian.utils import real_time, update_referral_points, convert_timedelta_into_datetime
|
||||||
|
from notifications.utils import send_notification
|
||||||
|
from notifications.constants import (ASSOCIATE_REQUEST, ASSOCIATE_JUNIOR, TASK_ACTION,
|
||||||
|
)
|
||||||
|
from web_admin.models import ArticleCard
|
||||||
|
|
||||||
class ListCharField(serializers.ListField):
|
class ListCharField(serializers.ListField):
|
||||||
"""Serializer for Array field"""
|
"""Serializer for Array field"""
|
||||||
@@ -80,18 +88,33 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
|
|||||||
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)
|
# condition for guardian code
|
||||||
"""condition for guardian code"""
|
|
||||||
if guardian_code:
|
if guardian_code:
|
||||||
junior.guardian_code = guardian_code
|
if junior.guardian_code and guardian_code:
|
||||||
junior.dob = validated_data.get('dob',junior.dob)
|
if guardian_code[0] in junior.guardian_code:
|
||||||
|
raise serializers.ValidationError({"error":ERROR_CODE['2076'],"code":"400", "status":"failed"})
|
||||||
|
if not junior.guardian_code:
|
||||||
|
junior.guardian_code = []
|
||||||
|
junior.guardian_code_status = []
|
||||||
|
junior.guardian_code.extend(guardian_code)
|
||||||
|
junior.guardian_code_status.extend(str(NUMBER['three']))
|
||||||
|
elif len(junior.guardian_code) < 3 and len(guardian_code) < 3:
|
||||||
|
junior.guardian_code.extend(guardian_code)
|
||||||
|
junior.guardian_code_status.extend(str(NUMBER['three']))
|
||||||
|
else:
|
||||||
|
raise serializers.ValidationError({"error":ERROR_CODE['2081'],"code":"400", "status":"failed"})
|
||||||
|
guardian_data = Guardian.objects.filter(guardian_code=guardian_code[0]).last()
|
||||||
|
if guardian_data:
|
||||||
|
JuniorGuardianRelationship.objects.get_or_create(guardian=guardian_data, junior=junior)
|
||||||
|
send_notification.delay(ASSOCIATE_REQUEST, junior.auth_id, JUNIOR, guardian_data.user_id, {})
|
||||||
|
# junior_approval_mail.delay(user.email, user.first_name) removed as per changes
|
||||||
|
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)
|
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.gender, junior.country_name, junior.image,
|
complete_profile_field = all([junior.gender, junior.country_name, junior.image,
|
||||||
@@ -99,6 +122,9 @@ class CreateJuniorSerializer(serializers.ModelSerializer):
|
|||||||
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
|
||||||
|
referral_code_used = validated_data.get('referral_code_used')
|
||||||
|
update_referral_points(junior.referral_code, referral_code_used)
|
||||||
|
junior.referral_code_used = validated_data.get('referral_code_used', junior.referral_code_used)
|
||||||
junior.save()
|
junior.save()
|
||||||
return junior
|
return junior
|
||||||
|
|
||||||
@@ -130,8 +156,8 @@ class JuniorDetailSerializer(serializers.ModelSerializer):
|
|||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = Junior
|
model = Junior
|
||||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
|
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
|
||||||
'guardian_code', 'is_invited', 'referral_code','is_active', 'is_complete_profile', 'created_at',
|
'guardian_code', 'image', 'is_invited', 'referral_code','is_active', 'is_complete_profile',
|
||||||
'image', 'updated_at']
|
'created_at', 'image', 'is_deleted', 'updated_at']
|
||||||
|
|
||||||
class JuniorDetailListSerializer(serializers.ModelSerializer):
|
class JuniorDetailListSerializer(serializers.ModelSerializer):
|
||||||
"""junior serializer"""
|
"""junior serializer"""
|
||||||
@@ -147,6 +173,7 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
|
|||||||
rejected_task = serializers.SerializerMethodField('get_rejected_task')
|
rejected_task = serializers.SerializerMethodField('get_rejected_task')
|
||||||
pending_task = serializers.SerializerMethodField('get_pending_task')
|
pending_task = serializers.SerializerMethodField('get_pending_task')
|
||||||
position = serializers.SerializerMethodField('get_position')
|
position = serializers.SerializerMethodField('get_position')
|
||||||
|
guardian_code_status = serializers.SerializerMethodField('get_guardian_code_status')
|
||||||
|
|
||||||
|
|
||||||
def get_auth(self, obj):
|
def get_auth(self, obj):
|
||||||
@@ -163,13 +190,13 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
|
|||||||
return data
|
return data
|
||||||
|
|
||||||
def get_position(self, obj):
|
def get_position(self, obj):
|
||||||
|
return get_junior_leaderboard_rank(obj)
|
||||||
|
|
||||||
|
def get_points(self, obj):
|
||||||
data = JuniorPoints.objects.filter(junior=obj).last()
|
data = JuniorPoints.objects.filter(junior=obj).last()
|
||||||
if data:
|
if data:
|
||||||
return data.position
|
return data.total_points
|
||||||
return 99999
|
return NUMBER['zero']
|
||||||
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):
|
def get_in_progress_task(self, obj):
|
||||||
data = JuniorTask.objects.filter(junior=obj, task_status=IN_PROGRESS).count()
|
data = JuniorTask.objects.filter(junior=obj, task_status=IN_PROGRESS).count()
|
||||||
@@ -191,13 +218,21 @@ class JuniorDetailListSerializer(serializers.ModelSerializer):
|
|||||||
def get_pending_task(self, obj):
|
def get_pending_task(self, obj):
|
||||||
data = JuniorTask.objects.filter(junior=obj, task_status=PENDING).count()
|
data = JuniorTask.objects.filter(junior=obj, task_status=PENDING).count()
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
def get_guardian_code_status(self, obj):
|
||||||
|
if self.context['guardian_code'] in obj.guardian_code:
|
||||||
|
index = obj.guardian_code.index(self.context['guardian_code'])
|
||||||
|
if obj.guardian_code_status:
|
||||||
|
data = obj.guardian_code_status[index]
|
||||||
|
return data
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = Junior
|
model = Junior
|
||||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'phone', 'gender', 'dob',
|
fields = ['id', 'email', 'first_name', 'last_name', 'country_code', 'country_name', 'phone', 'gender', 'dob',
|
||||||
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
|
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
|
||||||
'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task',
|
'updated_at', 'assigned_task','points', 'pending_task', 'in_progress_task', 'completed_task',
|
||||||
'requested_task', 'rejected_task', 'position', 'is_invited']
|
'requested_task', 'rejected_task', 'position', 'is_invited', 'guardian_code_status',
|
||||||
|
'is_deleted']
|
||||||
|
|
||||||
class JuniorProfileSerializer(serializers.ModelSerializer):
|
class JuniorProfileSerializer(serializers.ModelSerializer):
|
||||||
"""junior serializer"""
|
"""junior serializer"""
|
||||||
@@ -240,22 +275,14 @@ class JuniorProfileSerializer(serializers.ModelSerializer):
|
|||||||
fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob',
|
fields = ['id', 'email', 'first_name', 'last_name', 'country_name', 'country_code', 'phone', 'gender', 'dob',
|
||||||
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
|
'guardian_code', 'referral_code','is_active', 'is_complete_profile', 'created_at', 'image',
|
||||||
'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method',
|
'updated_at', 'notification_count', 'total_count', 'complete_field_count', 'signup_method',
|
||||||
'is_invited', 'passcode']
|
'is_invited', 'passcode', 'guardian_code_approved', 'is_deleted']
|
||||||
|
|
||||||
class AddJuniorSerializer(serializers.ModelSerializer):
|
class AddJuniorSerializer(serializers.ModelSerializer):
|
||||||
"""Add junior serializer"""
|
"""Add junior serializer"""
|
||||||
auth_token = serializers.SerializerMethodField('get_auth_token')
|
|
||||||
|
|
||||||
def get_auth_token(self, obj):
|
|
||||||
"""auth token"""
|
|
||||||
refresh = RefreshToken.for_user(obj)
|
|
||||||
access_token = str(refresh.access_token)
|
|
||||||
return access_token
|
|
||||||
|
|
||||||
class Meta(object):
|
class Meta(object):
|
||||||
"""Meta info"""
|
"""Meta info"""
|
||||||
model = Junior
|
model = Junior
|
||||||
fields = ['id', 'gender','dob', 'relationship', 'auth_token', 'is_invited']
|
fields = ['id', 'gender', 'dob', 'is_invited']
|
||||||
|
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
@@ -263,31 +290,40 @@ class AddJuniorSerializer(serializers.ModelSerializer):
|
|||||||
with transaction.atomic():
|
with transaction.atomic():
|
||||||
email = self.context['email']
|
email = self.context['email']
|
||||||
guardian = self.context['user']
|
guardian = self.context['user']
|
||||||
|
relationship = self.context['relationship']
|
||||||
|
profile_image = self.context['image']
|
||||||
full_name = self.context['first_name'] + ' ' + self.context['last_name']
|
full_name = self.context['first_name'] + ' ' + self.context['last_name']
|
||||||
guardian_data = Guardian.objects.filter(user__username=guardian).last()
|
guardian_data = Guardian.objects.filter(user__username=guardian).last()
|
||||||
user_data = User.objects.create(username=email, email=email,
|
user_data = User.objects.create(username=email, email=email,
|
||||||
first_name=self.context['first_name'],
|
first_name=self.context['first_name'],
|
||||||
last_name=self.context['last_name'])
|
last_name=self.context['last_name'])
|
||||||
|
special_password = make_special_password()
|
||||||
password = User.objects.make_random_password()
|
user_data.set_password(special_password)
|
||||||
user_data.set_password(password)
|
|
||||||
user_data.save()
|
user_data.save()
|
||||||
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
|
junior_data = Junior.objects.create(auth=user_data, gender=validated_data.get('gender'),
|
||||||
|
image=profile_image,
|
||||||
dob=validated_data.get('dob'), is_invited=True,
|
dob=validated_data.get('dob'), is_invited=True,
|
||||||
relationship=validated_data.get('relationship'),
|
guardian_code=[guardian_data.guardian_code],
|
||||||
junior_code=generate_code(JUN, user_data.id),
|
junior_code=generate_code(JUN, user_data.id),
|
||||||
referral_code=generate_code(ZOD, user_data.id),
|
referral_code=generate_code(ZOD, user_data.id),
|
||||||
referral_code_used=guardian_data.referral_code)
|
referral_code_used=guardian_data.referral_code,
|
||||||
|
is_password_set=False, is_verified=True,
|
||||||
|
guardian_code_status=[str(NUMBER['two'])])
|
||||||
|
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
|
||||||
|
relationship=relationship)
|
||||||
|
total_junior = Junior.objects.all().count()
|
||||||
|
JuniorPoints.objects.create(junior=junior_data, position=total_junior)
|
||||||
"""Generate otp"""
|
"""Generate otp"""
|
||||||
otp_value = generate_otp()
|
otp_value = generate_otp()
|
||||||
expiry_time = timezone.now() + timezone.timedelta(days=1)
|
expiry_time = timezone.now() + timezone.timedelta(days=1)
|
||||||
UserEmailOtp.objects.create(email=email, otp=otp_value,
|
UserEmailOtp.objects.create(email=email, otp=otp_value,
|
||||||
user_type='1', expired_at=expiry_time)
|
user_type='1', expired_at=expiry_time, is_verified=True)
|
||||||
"""Send email to the register user"""
|
# add push notification
|
||||||
send_otp_email(email, otp_value)
|
UserNotification.objects.get_or_create(user=user_data)
|
||||||
"""Notification email"""
|
"""Notification email"""
|
||||||
junior_notification_email(email, full_name, email, password)
|
junior_notification_email.delay(email, full_name, email, special_password)
|
||||||
junior_approval_mail(guardian, full_name)
|
# push notification
|
||||||
|
send_notification.delay(ASSOCIATE_JUNIOR, None, GUARDIAN, junior_data.auth_id, {})
|
||||||
return junior_data
|
return junior_data
|
||||||
|
|
||||||
|
|
||||||
@@ -300,8 +336,215 @@ class RemoveJuniorSerializer(serializers.ModelSerializer):
|
|||||||
fields = ('id', 'is_invited')
|
fields = ('id', 'is_invited')
|
||||||
def update(self, instance, validated_data):
|
def update(self, instance, validated_data):
|
||||||
if instance:
|
if instance:
|
||||||
|
guardian_code = self.context['guardian_code']
|
||||||
instance.is_invited = False
|
instance.is_invited = False
|
||||||
instance.guardian_code = '{}'
|
if instance.guardian_code and ('-' in instance.guardian_code):
|
||||||
|
instance.guardian_code.remove('-')
|
||||||
|
index = instance.guardian_code.index(guardian_code)
|
||||||
|
instance.guardian_code.remove(guardian_code)
|
||||||
|
instance.guardian_code_status.pop(index)
|
||||||
instance.save()
|
instance.save()
|
||||||
return instance
|
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 = timezone.now().astimezone(pytz.utc)
|
||||||
|
instance.task_status = str(NUMBER['four'])
|
||||||
|
instance.is_approved = False
|
||||||
|
instance.save()
|
||||||
|
send_notification.delay(TASK_ACTION, instance.junior.auth_id, JUNIOR,
|
||||||
|
instance.guardian.user_id, {'task_id': instance.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')
|
||||||
|
expired_task = serializers.SerializerMethodField('get_expired_task')
|
||||||
|
position = serializers.SerializerMethodField('get_position')
|
||||||
|
|
||||||
|
def get_junior_id(self, obj):
|
||||||
|
"""junior id"""
|
||||||
|
return obj.junior.id
|
||||||
|
|
||||||
|
def get_position(self, obj):
|
||||||
|
return get_junior_leaderboard_rank(obj.junior)
|
||||||
|
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()
|
||||||
|
|
||||||
|
def get_expired_task(self, obj):
|
||||||
|
return JuniorTask.objects.filter(junior=obj.junior, task_status=EXPIRED).count()
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""Meta info"""
|
||||||
|
model = Junior
|
||||||
|
fields = ['junior_id', 'total_points', 'position', 'pending_task', 'in_progress_task', 'completed_task',
|
||||||
|
'requested_task', 'rejected_task', 'expired_task', 'is_deleted']
|
||||||
|
|
||||||
|
class AddGuardianSerializer(serializers.ModelSerializer):
|
||||||
|
"""Add guardian serializer"""
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""Meta info"""
|
||||||
|
model = Guardian
|
||||||
|
fields = ['id']
|
||||||
|
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
""" invite and create guardian"""
|
||||||
|
with transaction.atomic():
|
||||||
|
email = self.context['email']
|
||||||
|
junior = self.context['user']
|
||||||
|
relationship = self.context['relationship']
|
||||||
|
full_name = self.context['first_name'] + ' ' + self.context['last_name']
|
||||||
|
junior_data = Junior.objects.filter(auth__username=junior).last()
|
||||||
|
junior_data.guardian_code_status = GUARDIAN_CODE_STATUS[2][0]
|
||||||
|
junior_data.save()
|
||||||
|
instance = User.objects.filter(username=email).last()
|
||||||
|
if instance:
|
||||||
|
guardian_data = Guardian.objects.filter(user=instance).update(is_invited=True,
|
||||||
|
referral_code=generate_code(ZOD,
|
||||||
|
instance.id),
|
||||||
|
referral_code_used=junior_data.referral_code,
|
||||||
|
is_verified=True)
|
||||||
|
UserNotification.objects.get_or_create(user=instance)
|
||||||
|
return guardian_data
|
||||||
|
else:
|
||||||
|
user = User.objects.create(username=email, email=email,
|
||||||
|
first_name=self.context['first_name'],
|
||||||
|
last_name=self.context['last_name'])
|
||||||
|
|
||||||
|
password = User.objects.make_random_password()
|
||||||
|
user.set_password(password)
|
||||||
|
user.save()
|
||||||
|
guardian_data = Guardian.objects.create(user=user, is_invited=True,
|
||||||
|
referral_code=generate_code(ZOD, user.id),
|
||||||
|
referral_code_used=junior_data.referral_code,
|
||||||
|
is_password_set=False, is_verified=True)
|
||||||
|
"""Generate otp"""
|
||||||
|
otp_value = generate_otp()
|
||||||
|
expiry_time = timezone.now() + timezone.timedelta(days=1)
|
||||||
|
UserEmailOtp.objects.create(email=email, otp=otp_value,
|
||||||
|
user_type=str(NUMBER['two']), expired_at=expiry_time,
|
||||||
|
is_verified=True)
|
||||||
|
UserNotification.objects.get_or_create(user=user)
|
||||||
|
JuniorGuardianRelationship.objects.create(guardian=guardian_data, junior=junior_data,
|
||||||
|
relationship=relationship)
|
||||||
|
|
||||||
|
"""Notification email"""
|
||||||
|
junior_notification_email(email, full_name, email, password)
|
||||||
|
# junior_approval_mail.delay(email, full_name) removed as per changes
|
||||||
|
send_notification.delay(ASSOCIATE_REQUEST, junior_data.auth_id, JUNIOR, guardian_data.user_id, {})
|
||||||
|
return guardian_data
|
||||||
|
|
||||||
|
class StartTaskSerializer(serializers.ModelSerializer):
|
||||||
|
"""User task Serializer"""
|
||||||
|
task_duration = serializers.SerializerMethodField('get_task_duration')
|
||||||
|
|
||||||
|
def get_task_duration(self, obj):
|
||||||
|
""" remaining time to complete task"""
|
||||||
|
due_date = datetime.combine(obj.due_date, datetime.max.time())
|
||||||
|
# fetch real time
|
||||||
|
# real_datetime = real_time()
|
||||||
|
# new code
|
||||||
|
due_date = due_date.astimezone(pytz.utc)
|
||||||
|
real_datetime = timezone.now().astimezone(pytz.utc)
|
||||||
|
# Perform the subtraction
|
||||||
|
if due_date > real_datetime:
|
||||||
|
time_difference = due_date - real_datetime
|
||||||
|
time_only = convert_timedelta_into_datetime(time_difference)
|
||||||
|
return str(time_difference.days) + ' days ' + str(time_only)
|
||||||
|
return str(NUMBER['zero']) + ' days ' + '00:00:00:00000'
|
||||||
|
class Meta(object):
|
||||||
|
"""Meta class"""
|
||||||
|
model = JuniorTask
|
||||||
|
fields = ('id', 'task_duration')
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
instance.task_status = str(NUMBER['two'])
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
class ReAssignTaskSerializer(serializers.ModelSerializer):
|
||||||
|
"""User task Serializer"""
|
||||||
|
class Meta(object):
|
||||||
|
"""Meta class"""
|
||||||
|
model = JuniorTask
|
||||||
|
fields = ('id', 'due_date')
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
instance.task_status = str(NUMBER['one'])
|
||||||
|
instance.due_date = validated_data.get('due_date')
|
||||||
|
instance.is_approved = False
|
||||||
|
instance.requested_on = None
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
class RemoveGuardianCodeSerializer(serializers.ModelSerializer):
|
||||||
|
"""User task Serializer"""
|
||||||
|
class Meta(object):
|
||||||
|
"""Meta class"""
|
||||||
|
model = Junior
|
||||||
|
fields = ('id', )
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
guardian_code = self.context['guardian_code']
|
||||||
|
if guardian_code in instance.guardian_code:
|
||||||
|
if instance.guardian_code and ('-' in instance.guardian_code):
|
||||||
|
instance.guardian_code.remove('-')
|
||||||
|
if instance.guardian_code_status and ('-' in instance.guardian_code_status):
|
||||||
|
instance.guardian_code_status.remove('-')
|
||||||
|
index = instance.guardian_code.index(guardian_code)
|
||||||
|
instance.guardian_code.remove(guardian_code)
|
||||||
|
instance.guardian_code_status.pop(index)
|
||||||
|
else:
|
||||||
|
raise serializers.ValidationError({"error":ERROR_CODE['2082'],"code":"400", "status":"failed"})
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
class FAQSerializer(serializers.ModelSerializer):
|
||||||
|
"""FAQ Serializer"""
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""meta info"""
|
||||||
|
model = FAQ
|
||||||
|
fields = ('id', 'question', 'description')
|
||||||
|
|
||||||
|
class CreateArticleCardSerializer(serializers.ModelSerializer):
|
||||||
|
"""Article card Serializer"""
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""meta info"""
|
||||||
|
model = ArticleCard
|
||||||
|
fields = ('id', 'article')
|
||||||
|
|
||||||
|
|||||||
+34
-3
@@ -2,7 +2,11 @@
|
|||||||
"""Django import"""
|
"""Django import"""
|
||||||
from django.urls import path, include
|
from django.urls import path, include
|
||||||
from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView,
|
from .views import (UpdateJuniorProfile, ValidateGuardianCode, JuniorListAPIView, AddJuniorAPIView,
|
||||||
InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView)
|
InvitedJuniorAPIView, FilterJuniorAPIView, RemoveJuniorAPIView, JuniorTaskListAPIView,
|
||||||
|
CompleteJuniorTaskAPIView, JuniorPointsListAPIView, ValidateReferralCode,
|
||||||
|
InviteGuardianAPIView, StartTaskAPIView, ReAssignJuniorTaskAPIView, StartArticleAPIView,
|
||||||
|
StartAssessmentAPIView, CheckAnswerAPIView, CompleteArticleAPIView, ReadArticleCardAPIView,
|
||||||
|
CreateArticleCardAPIView, RemoveGuardianCodeAPIView, FAQViewSet, CheckJuniorApiViewSet)
|
||||||
"""Third party import"""
|
"""Third party import"""
|
||||||
from rest_framework import routers
|
from rest_framework import routers
|
||||||
|
|
||||||
@@ -17,21 +21,48 @@ router = routers.SimpleRouter()
|
|||||||
# junior list,
|
# junior list,
|
||||||
# add junior list, invited junior,
|
# add junior list, invited junior,
|
||||||
# filter-junior,
|
# filter-junior,
|
||||||
# remove junior"""
|
# remove junior,
|
||||||
|
# junior task list
|
||||||
"""API End points with router"""
|
"""API End points with router"""
|
||||||
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')
|
||||||
# junior list API"""
|
# junior list API"""
|
||||||
router.register('junior-list', JuniorListAPIView, basename='junior-list')
|
router.register('junior-list', JuniorListAPIView, basename='junior-list')
|
||||||
|
|
||||||
|
router.register('check-junior', CheckJuniorApiViewSet, basename='check-junior')
|
||||||
# Add junior list API"""
|
# Add junior list API"""
|
||||||
router.register('add-junior', AddJuniorAPIView, basename='add-junior')
|
router.register('add-junior', AddJuniorAPIView, basename='add-junior')
|
||||||
# Invited junior list API"""
|
# Invited junior list API"""
|
||||||
router.register('invited-junior', InvitedJuniorAPIView, basename='invited-junior')
|
router.register('invited-junior', InvitedJuniorAPIView, basename='invited-junior')
|
||||||
# Filter junior list API"""
|
# Filter junior list API"""
|
||||||
router.register('filter-junior', FilterJuniorAPIView, basename='filter-junior')
|
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')
|
||||||
|
# start article"""
|
||||||
|
router.register('start-article', StartArticleAPIView, basename='start-article')
|
||||||
|
# start assessment api"""
|
||||||
|
router.register('start-assessment', StartAssessmentAPIView, basename='start-assessment')
|
||||||
|
# check answer api"""
|
||||||
|
router.register('check-answer', CheckAnswerAPIView, basename='check-answer')
|
||||||
|
# start article"""
|
||||||
|
router.register('create-article-card', CreateArticleCardAPIView, basename='create-article-card')
|
||||||
|
# FAQ API
|
||||||
|
router.register('faq', FAQViewSet, basename='faq')
|
||||||
# Define url pattern"""
|
# Define url pattern"""
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('api/v1/', include(router.urls)),
|
path('api/v1/', include(router.urls)),
|
||||||
path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view())
|
path('api/v1/remove-junior/', RemoveJuniorAPIView.as_view()),
|
||||||
|
path('api/v1/complete-task/', CompleteJuniorTaskAPIView.as_view()),
|
||||||
|
path('api/v1/start-task/', StartTaskAPIView.as_view()),
|
||||||
|
path('api/v1/reassign-task/', ReAssignJuniorTaskAPIView.as_view()),
|
||||||
|
path('api/v1/complete-article/', CompleteArticleAPIView.as_view()),
|
||||||
|
path('api/v1/read-article-card/', ReadArticleCardAPIView.as_view()),
|
||||||
|
path('api/v1/remove-guardian-code-request/', RemoveGuardianCodeAPIView.as_view())
|
||||||
]
|
]
|
||||||
|
|||||||
+37
-2
@@ -3,7 +3,10 @@
|
|||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
"""Third party Django app"""
|
"""Third party Django app"""
|
||||||
from templated_email import send_templated_mail
|
from templated_email import send_templated_mail
|
||||||
|
from .models import JuniorPoints
|
||||||
|
from base.constants import NUMBER
|
||||||
|
from django.db.models import F, Window
|
||||||
|
from django.db.models.functions.window import Rank
|
||||||
# junior notification
|
# junior notification
|
||||||
# email for sending email
|
# email for sending email
|
||||||
# when guardian create junior profile
|
# when guardian create junior profile
|
||||||
@@ -12,6 +15,8 @@ from templated_email import send_templated_mail
|
|||||||
# being part of the zod bank and access the platform
|
# being part of the zod bank and access the platform
|
||||||
# define junior notification email
|
# define junior notification email
|
||||||
# junior approval email
|
# junior approval email
|
||||||
|
from celery import shared_task
|
||||||
|
@shared_task()
|
||||||
def junior_notification_email(recipient_email, full_name, email, password):
|
def junior_notification_email(recipient_email, full_name, email, password):
|
||||||
"""Notification email"""
|
"""Notification email"""
|
||||||
from_email = settings.EMAIL_FROM_ADDRESS
|
from_email = settings.EMAIL_FROM_ADDRESS
|
||||||
@@ -30,7 +35,7 @@ def junior_notification_email(recipient_email, full_name, email, password):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
return full_name
|
return full_name
|
||||||
|
@shared_task()
|
||||||
def junior_approval_mail(guardian, full_name):
|
def junior_approval_mail(guardian, full_name):
|
||||||
"""junior approval mail"""
|
"""junior approval mail"""
|
||||||
from_email = settings.EMAIL_FROM_ADDRESS
|
from_email = settings.EMAIL_FROM_ADDRESS
|
||||||
@@ -45,3 +50,33 @@ def junior_approval_mail(guardian, full_name):
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
return full_name
|
return full_name
|
||||||
|
|
||||||
|
def update_positions_based_on_points():
|
||||||
|
"""Update position of the junior"""
|
||||||
|
# First, retrieve all the JuniorPoints instances ordered by total_points in descending order.
|
||||||
|
juniors_points = JuniorPoints.objects.order_by('-total_points', 'created_at')
|
||||||
|
|
||||||
|
# Now, iterate through the queryset and update the position field based on the order.
|
||||||
|
position = NUMBER['one']
|
||||||
|
for junior_point in juniors_points:
|
||||||
|
junior_point.position = position
|
||||||
|
junior_point.save()
|
||||||
|
position += NUMBER['one']
|
||||||
|
|
||||||
|
|
||||||
|
def get_junior_leaderboard_rank(junior_obj):
|
||||||
|
"""
|
||||||
|
to get junior's position/rank
|
||||||
|
:param junior_obj:
|
||||||
|
:return: junior's position/rank
|
||||||
|
"""
|
||||||
|
queryset = JuniorPoints.objects.filter(
|
||||||
|
junior__is_verified=True
|
||||||
|
).select_related('junior', 'junior__auth').annotate(rank=Window(
|
||||||
|
expression=Rank(),
|
||||||
|
order_by=[F('total_points').desc(), 'junior__created_at']
|
||||||
|
)).order_by('-total_points', 'junior__created_at')
|
||||||
|
|
||||||
|
junior = next((query for query in queryset if query.junior == junior_obj), None)
|
||||||
|
|
||||||
|
return junior.rank if junior else None
|
||||||
|
|||||||
+774
-81
@@ -1,61 +1,136 @@
|
|||||||
"""Junior view file"""
|
"""Junior view file"""
|
||||||
|
import os
|
||||||
|
|
||||||
from rest_framework import viewsets, status, generics,views
|
from rest_framework import viewsets, status, generics,views
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
from rest_framework.pagination import PageNumberPagination
|
from rest_framework.pagination import PageNumberPagination
|
||||||
from rest_framework.response import Response
|
from django.contrib.auth.models import User
|
||||||
from PIL import Image
|
from rest_framework.filters import SearchFilter
|
||||||
|
from django.db.models import F
|
||||||
|
|
||||||
|
import datetime
|
||||||
|
import requests
|
||||||
|
|
||||||
|
from rest_framework.viewsets import GenericViewSet, mixins
|
||||||
|
import math
|
||||||
|
|
||||||
|
from base.pagination import CustomPageNumberPagination
|
||||||
|
|
||||||
"""Django app import"""
|
"""Django app import"""
|
||||||
from junior.models import Junior
|
from drf_yasg.utils import swagger_auto_schema
|
||||||
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,\
|
from drf_yasg import openapi
|
||||||
RemoveJuniorSerializer)
|
from drf_yasg.views import get_schema_view
|
||||||
from guardian.models import Guardian
|
# 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 junior serializer
|
||||||
|
# Import update_positions_based_on_points
|
||||||
|
# Import upload_image_to_alibaba
|
||||||
|
# Import custom_response, custom_error_response
|
||||||
|
# Import constants
|
||||||
|
from django.db.models import Sum
|
||||||
|
from junior.models import (Junior, JuniorPoints, JuniorGuardianRelationship, JuniorArticlePoints, JuniorArticle,
|
||||||
|
JuniorArticleCard, FAQ)
|
||||||
|
from .serializers import (CreateJuniorSerializer, JuniorDetailListSerializer, AddJuniorSerializer,
|
||||||
|
RemoveJuniorSerializer, CompleteTaskSerializer, JuniorPointsSerializer,
|
||||||
|
AddGuardianSerializer, StartTaskSerializer, ReAssignTaskSerializer,
|
||||||
|
RemoveGuardianCodeSerializer, FAQSerializer, CreateArticleCardSerializer)
|
||||||
|
from guardian.models import Guardian, JuniorTask
|
||||||
|
from guardian.serializers import TaskDetailsSerializer, TaskDetailsjuniorSerializer
|
||||||
from base.messages import ERROR_CODE, SUCCESS_CODE
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
from base.constants import NUMBER
|
from base.constants import NUMBER, ARTICLE_STATUS, none, GUARDIAN
|
||||||
from account.utils import custom_response, custom_error_response
|
from account.utils import custom_response, custom_error_response, task_status_fun
|
||||||
from guardian.utils import upload_image_to_alibaba
|
from guardian.utils import upload_image_to_alibaba
|
||||||
|
from .utils import update_positions_based_on_points
|
||||||
|
from notifications.utils import send_notification
|
||||||
|
from notifications.constants import REMOVE_JUNIOR, ARTICLE_REWARD_POINTS, ASSOCIATE_EXISTING_JUNIOR
|
||||||
|
from web_admin.models import Article, ArticleSurvey, SurveyOption, ArticleCard
|
||||||
|
from web_admin.serializers.article_serializer import (ArticleSerializer, ArticleListSerializer,
|
||||||
|
StartAssessmentSerializer)
|
||||||
|
""" Define APIs """
|
||||||
|
# Define validate guardian code API,
|
||||||
|
# update junior profile,
|
||||||
|
# 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.ModelViewSet):
|
||||||
"""Update junior profile"""
|
"""Update junior profile"""
|
||||||
queryset = Junior.objects.all()
|
|
||||||
serializer_class = CreateJuniorSerializer
|
serializer_class = CreateJuniorSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('post',)
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
"""Use CreateJuniorSerializer"""
|
"""Create Junior Profile"""
|
||||||
request_data = request.data
|
try:
|
||||||
image = request.data.get('image')
|
request_data = request.data
|
||||||
image_url = ''
|
image = request.data.get('image')
|
||||||
if image:
|
image_url = ''
|
||||||
if image.size == NUMBER['zero']:
|
if image:
|
||||||
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
|
# check image size
|
||||||
filename = f"images/{image.name}"
|
if image.size == NUMBER['zero']:
|
||||||
image_url = upload_image_to_alibaba(image, filename)
|
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
request_data = {"image": image_url}
|
# convert into file
|
||||||
serializer = CreateJuniorSerializer(context={"user":request.user, "image":image_url,
|
filename = f"images/{image.name}"
|
||||||
"first_name": request.data.get('first_name'),
|
# upload image on ali baba
|
||||||
"last_name": request.data.get('last_name')
|
image_url = upload_image_to_alibaba(image, filename)
|
||||||
},
|
request_data = {"image": image_url}
|
||||||
data=request_data)
|
serializer = CreateJuniorSerializer(context={"user":request.user, "image":image_url,
|
||||||
if serializer.is_valid():
|
"first_name": request.data.get('first_name'),
|
||||||
"""save serializer"""
|
"last_name": request.data.get('last_name')
|
||||||
serializer.save()
|
},
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
data=request_data)
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
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:
|
||||||
|
if e.detail:
|
||||||
|
error_detail = e.detail.get('error', None)
|
||||||
|
else:
|
||||||
|
error_detail = str(e)
|
||||||
|
return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
class ValidateGuardianCode(viewsets.ViewSet):
|
class ValidateGuardianCode(viewsets.ModelViewSet):
|
||||||
"""Check guardian code exist or not"""
|
"""Check guardian code exist or not"""
|
||||||
queryset = Guardian.objects.all()
|
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('get',)
|
||||||
|
|
||||||
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(',')
|
Params
|
||||||
for code in guardian_code:
|
"guardian_code"
|
||||||
guardian_data = Guardian.objects.filter(guardian_code=code).exists()
|
"""
|
||||||
if guardian_data:
|
try:
|
||||||
return custom_response(SUCCESS_CODE['3013'], response_status=status.HTTP_200_OK)
|
guardian_code = self.request.GET.get('guardian_code').split(',')
|
||||||
else:
|
for code in guardian_code:
|
||||||
return custom_error_response(ERROR_CODE["2022"], response_status=status.HTTP_400_BAD_REQUEST)
|
# 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):
|
class JuniorListAPIView(viewsets.ModelViewSet):
|
||||||
"""Junior list of assosicated guardian"""
|
"""Junior list of assosicated guardian"""
|
||||||
@@ -63,92 +138,710 @@ class JuniorListAPIView(viewsets.ModelViewSet):
|
|||||||
serializer_class = JuniorDetailListSerializer
|
serializer_class = JuniorDetailListSerializer
|
||||||
queryset = Junior.objects.all()
|
queryset = Junior.objects.all()
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
filter_backends = (SearchFilter,)
|
||||||
|
search_fields = ['auth__first_name', 'auth__last_name']
|
||||||
|
http_method_names = ('get',)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = self.filter_queryset(self.queryset)
|
||||||
|
return queryset
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
""" junior list"""
|
""" junior list"""
|
||||||
guardian_data = Guardian.objects.filter(user__email=request.user).last()
|
try:
|
||||||
queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code))
|
# update_positions_based_on_points, function removed
|
||||||
serializer = JuniorDetailListSerializer(queryset, many=True)
|
guardian_data = Guardian.objects.filter(user__email=request.user).last()
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
# fetch junior object
|
||||||
|
if guardian_data:
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
queryset = queryset.filter(guardian_code__icontains=str(guardian_data.guardian_code))
|
||||||
|
# use JuniorDetailListSerializer serializer
|
||||||
|
serializer = JuniorDetailListSerializer(queryset, context={"guardian_code":
|
||||||
|
guardian_data.guardian_code}, 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 CheckJuniorApiViewSet(viewsets.GenericViewSet):
|
||||||
|
"""
|
||||||
|
api to check whether given user exist or not
|
||||||
|
"""
|
||||||
|
serializer_class = None
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
junior = Junior.objects.filter(auth__email=self.request.data.get('email')).first()
|
||||||
|
return junior
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
:param request:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
junior = self.get_queryset()
|
||||||
|
data = {
|
||||||
|
'junior_exist': True if junior else False
|
||||||
|
}
|
||||||
|
return custom_response(None, data)
|
||||||
|
|
||||||
|
|
||||||
class AddJuniorAPIView(viewsets.ModelViewSet):
|
class AddJuniorAPIView(viewsets.ModelViewSet):
|
||||||
"""Add Junior by guardian"""
|
"""Add Junior by guardian"""
|
||||||
queryset = Junior.objects.all()
|
|
||||||
serializer_class = AddJuniorSerializer
|
serializer_class = AddJuniorSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('post',)
|
||||||
|
|
||||||
def create(self, request, *args, **kwargs):
|
def create(self, request, *args, **kwargs):
|
||||||
""" junior list"""
|
""" add junior
|
||||||
info = {'user': request.user, 'email': request.data['email'], 'first_name': request.data['first_name'],
|
{ "gender":"1",
|
||||||
'last_name': request.data['last_name']}
|
"first_name":"abc",
|
||||||
serializer = AddJuniorSerializer(data=request.data, context=info)
|
"last_name":"xyz",
|
||||||
if serializer.is_valid():
|
"dob":"2023-12-12",
|
||||||
serializer.save()
|
"relationship":"2",
|
||||||
return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK)
|
"email":"[email protected]"
|
||||||
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
|
}"""
|
||||||
|
try:
|
||||||
|
if user := User.objects.filter(username=request.data['email']).first():
|
||||||
|
data = self.associate_guardian(user)
|
||||||
|
if data == none:
|
||||||
|
return custom_error_response(ERROR_CODE['2077'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
elif not data:
|
||||||
|
return custom_error_response(ERROR_CODE['2076'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
elif data == "Max":
|
||||||
|
return custom_error_response(ERROR_CODE['2081'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
return custom_response(SUCCESS_CODE['3021'], response_status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
info_data = {'user': request.user, 'relationship': str(request.data['relationship']),
|
||||||
|
'email': request.data['email'], 'first_name': request.data['first_name'],
|
||||||
|
'last_name': request.data['last_name'], 'image':None}
|
||||||
|
profile_image = request.data.get('image')
|
||||||
|
if profile_image:
|
||||||
|
# check image size
|
||||||
|
if profile_image.size == NUMBER['zero']:
|
||||||
|
return custom_error_response(ERROR_CODE['2035'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
# convert into file
|
||||||
|
filename = f"images/{profile_image.name}"
|
||||||
|
# upload image on ali baba
|
||||||
|
image_url = upload_image_to_alibaba(profile_image, filename)
|
||||||
|
info_data.update({"image": image_url})
|
||||||
|
|
||||||
|
# use AddJuniorSerializer serializer
|
||||||
|
serializer = AddJuniorSerializer(data=request.data, context=info_data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
# save serializer
|
||||||
|
serializer.save()
|
||||||
|
return custom_response(SUCCESS_CODE['3021'], serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def associate_guardian(self, user):
|
||||||
|
junior = Junior.objects.filter(auth__email=self.request.data['email']).first()
|
||||||
|
guardian = Guardian.objects.filter(user=self.request.user).first()
|
||||||
|
if junior is None:
|
||||||
|
return none
|
||||||
|
if junior.guardian_code and (guardian.guardian_code in junior.guardian_code):
|
||||||
|
return False
|
||||||
|
if junior.guardian_code and ('-' in junior.guardian_code):
|
||||||
|
junior.guardian_code.remove('-')
|
||||||
|
if not junior.guardian_code:
|
||||||
|
junior.guardian_code = [guardian.guardian_code]
|
||||||
|
elif type(junior.guardian_code) is list and len(junior.guardian_code) < 3:
|
||||||
|
junior.guardian_code.append(guardian.guardian_code)
|
||||||
|
else:
|
||||||
|
return "Max"
|
||||||
|
if junior.guardian_code_status and ('-' in junior.guardian_code_status):
|
||||||
|
junior.guardian_code_status.remove('-')
|
||||||
|
if not junior.guardian_code_status:
|
||||||
|
junior.guardian_code_status = [str(NUMBER['two'])]
|
||||||
|
else:
|
||||||
|
junior.guardian_code_status.append(str(NUMBER['two']))
|
||||||
|
junior.save()
|
||||||
|
jun_data, created = JuniorGuardianRelationship.objects.get_or_create(guardian=guardian, junior=junior)
|
||||||
|
if jun_data:
|
||||||
|
jun_data.relationship = str(self.request.data['relationship'])
|
||||||
|
jun_data.save()
|
||||||
|
send_notification.delay(ASSOCIATE_EXISTING_JUNIOR, guardian.user_id, GUARDIAN, junior.auth_id, {})
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
class InvitedJuniorAPIView(viewsets.ModelViewSet):
|
class InvitedJuniorAPIView(viewsets.ModelViewSet):
|
||||||
"""Junior list of assosicated guardian"""
|
"""Invited Junior list of assosicated guardian"""
|
||||||
|
|
||||||
serializer_class = JuniorDetailListSerializer
|
serializer_class = JuniorDetailListSerializer
|
||||||
queryset = Junior.objects.all()
|
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
pagination_class = PageNumberPagination
|
pagination_class = PageNumberPagination
|
||||||
|
http_method_names = ('get',)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Get the queryset for the view"""
|
"""Get the queryset for the view"""
|
||||||
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
||||||
junior_queryset = Junior.objects.filter(guardian_code__icontains=str(guardian.guardian_code),
|
junior_queryset = Junior.objects.filter(guardian_code__icontains=str(guardian.guardian_code),
|
||||||
is_invited=True)
|
is_invited=True)
|
||||||
return junior_queryset
|
return junior_queryset
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
""" junior list"""
|
""" Invited Junior list of assosicated guardian
|
||||||
queryset = self.get_queryset()
|
No Params"""
|
||||||
paginator = self.pagination_class()
|
try:
|
||||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
queryset = self.get_queryset()
|
||||||
serializer = JuniorDetailListSerializer(paginated_queryset, many=True)
|
paginator = self.pagination_class()
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
# 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):
|
class FilterJuniorAPIView(viewsets.ModelViewSet):
|
||||||
"""Update guardian profile"""
|
"""filter junior profile"""
|
||||||
serializer_class = JuniorDetailListSerializer
|
serializer_class = JuniorDetailListSerializer
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
pagination_class = PageNumberPagination
|
pagination_class = PageNumberPagination
|
||||||
queryset = Junior.objects.all()
|
http_method_names = ('get',)
|
||||||
|
|
||||||
|
@swagger_auto_schema(
|
||||||
|
manual_parameters=[
|
||||||
|
# Example of a query parameter
|
||||||
|
openapi.Parameter(
|
||||||
|
'title',
|
||||||
|
openapi.IN_QUERY,
|
||||||
|
description='title of the name',
|
||||||
|
type=openapi.TYPE_STRING,
|
||||||
|
),
|
||||||
|
# Add more parameters as needed
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
"""Get the queryset for the view"""
|
"""Get the queryset for the view"""
|
||||||
title = self.request.GET.get('title')
|
title = self.request.GET.get('title')
|
||||||
guardian_data = Guardian.objects.filter(user__email=self.request.user).last()
|
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),
|
queryset = Junior.objects.filter(guardian_code__icontains=str(guardian_data.guardian_code),
|
||||||
is_invited=True, auth__first_name=title)
|
is_invited=True, auth__first_name=title)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def list(self, request, *args, **kwargs):
|
def list(self, request, *args, **kwargs):
|
||||||
"""Create guardian profile"""
|
"""Filter junior"""
|
||||||
queryset = self.get_queryset()
|
try:
|
||||||
paginator = self.pagination_class()
|
queryset = self.get_queryset()
|
||||||
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
paginator = self.pagination_class()
|
||||||
serializer = JuniorDetailListSerializer(paginated_queryset, many=True)
|
# use Pagination
|
||||||
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
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):
|
class RemoveJuniorAPIView(views.APIView):
|
||||||
"""Eater Update API"""
|
"""Remove junior API
|
||||||
|
Params
|
||||||
|
id=37"""
|
||||||
serializer_class = RemoveJuniorSerializer
|
serializer_class = RemoveJuniorSerializer
|
||||||
model = Junior
|
model = Junior
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
def put(self, request, format=None):
|
def put(self, request, format=None):
|
||||||
junior_id = self.request.GET.get('id')
|
try:
|
||||||
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
junior_id = self.request.GET.get('id')
|
||||||
junior_queryset = Junior.objects.filter(id=junior_id, guardian_code__icontains=str(guardian.guardian_code),
|
guardian = Guardian.objects.filter(user__email=self.request.user).last()
|
||||||
is_invited=True).last()
|
# fetch junior query
|
||||||
if junior_queryset:
|
junior_queryset = Junior.objects.filter(id=junior_id,
|
||||||
serializer = RemoveJuniorSerializer(junior_queryset, data=request.data, partial=True)
|
guardian_code__icontains=str(guardian.guardian_code)).last()
|
||||||
|
if junior_queryset:
|
||||||
|
# use RemoveJuniorSerializer serializer
|
||||||
|
serializer = RemoveJuniorSerializer(junior_queryset, context={"guardian_code":guardian.guardian_code},
|
||||||
|
data=request.data, partial=True)
|
||||||
|
if serializer.is_valid():
|
||||||
|
# save serializer
|
||||||
|
serializer.save()
|
||||||
|
JuniorGuardianRelationship.objects.filter(guardian=guardian, junior=junior_queryset).delete()
|
||||||
|
send_notification.delay(REMOVE_JUNIOR, None, GUARDIAN, junior_queryset.auth_id, {})
|
||||||
|
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
else:
|
||||||
|
return custom_error_response(ERROR_CODE['2034'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class JuniorTaskListAPIView(viewsets.ModelViewSet):
|
||||||
|
"""Junior task list"""
|
||||||
|
serializer_class = TaskDetailsjuniorSerializer
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
filter_backends = (SearchFilter,)
|
||||||
|
search_fields = ['task_name', ]
|
||||||
|
pagination_class = PageNumberPagination
|
||||||
|
http_method_names = ('get',)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
queryset = JuniorTask.objects.filter(junior__auth=self.request.user
|
||||||
|
).select_related('junior', 'junior__auth'
|
||||||
|
).order_by('-created_at')
|
||||||
|
|
||||||
|
queryset = self.filter_queryset(queryset)
|
||||||
|
return queryset
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
"""Junior task list
|
||||||
|
status=0
|
||||||
|
search='title'
|
||||||
|
page=1"""
|
||||||
|
try:
|
||||||
|
status_value = self.request.GET.get('status')
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
task_status = task_status_fun(status_value)
|
||||||
|
if status_value:
|
||||||
|
queryset = queryset.filter(task_status__in=task_status)
|
||||||
|
paginator = CustomPageNumberPagination()
|
||||||
|
# use Pagination
|
||||||
|
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||||
|
# use TaskDetails juniorSerializer serializer
|
||||||
|
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||||
|
return paginator.get_paginated_response(serializer.data)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class CompleteJuniorTaskAPIView(views.APIView):
|
||||||
|
"""Payload
|
||||||
|
task_id
|
||||||
|
image"""
|
||||||
|
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
|
||||||
|
).select_related('guardian', 'junior').last()
|
||||||
|
if task_queryset:
|
||||||
|
if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code:
|
||||||
|
return custom_error_response(ERROR_CODE['2085'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
elif task_queryset.junior.is_deleted or not task_queryset.junior.is_active:
|
||||||
|
return custom_error_response(ERROR_CODE['2074'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
# use CompleteTaskSerializer serializer
|
||||||
|
elif 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 list(self, request, *args, **kwargs):
|
||||||
|
"""Junior Points
|
||||||
|
No Params"""
|
||||||
|
try:
|
||||||
|
# update_positions_based_on_points, function removed
|
||||||
|
queryset = JuniorPoints.objects.filter(junior__auth__email=self.request.user).last()
|
||||||
|
# update position of junior
|
||||||
|
serializer = JuniorPointsSerializer(queryset)
|
||||||
|
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class ValidateReferralCode(viewsets.ModelViewSet):
|
||||||
|
"""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):
|
||||||
|
""" add guardian
|
||||||
|
{
|
||||||
|
"first_name":"abc",
|
||||||
|
"last_name":"xyz",
|
||||||
|
"email":"[email protected]",
|
||||||
|
"relationship":2
|
||||||
|
}"""
|
||||||
|
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():
|
if serializer.is_valid():
|
||||||
|
# save serializer
|
||||||
serializer.save()
|
serializer.save()
|
||||||
return custom_response(SUCCESS_CODE['3022'], serializer.data, response_status=status.HTTP_200_OK)
|
return custom_response(SUCCESS_CODE['3034'], serializer.data, response_status=status.HTTP_200_OK)
|
||||||
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(serializer.error, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
else:
|
except Exception as e:
|
||||||
return custom_error_response(ERROR_CODE['2034'], response_status=status.HTTP_400_BAD_REQUEST)
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class StartTaskAPIView(views.APIView):
|
||||||
|
"""Update junior task API
|
||||||
|
Paylod
|
||||||
|
{
|
||||||
|
"task_id":28
|
||||||
|
}"""
|
||||||
|
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()
|
||||||
|
print("task_queryset==>",task_queryset)
|
||||||
|
if task_queryset and task_queryset.task_status == str(NUMBER['one']):
|
||||||
|
if task_queryset.guardian.guardian_code not in task_queryset.junior.guardian_code:
|
||||||
|
return custom_error_response(ERROR_CODE['2083'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
# use StartTaskSerializer serializer
|
||||||
|
serializer = StartTaskSerializer(task_queryset, data=request.data, partial=True)
|
||||||
|
if serializer.is_valid():
|
||||||
|
# save serializer
|
||||||
|
serializer.save()
|
||||||
|
return custom_response(SUCCESS_CODE['3035'], serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
else:
|
||||||
|
# task in another state
|
||||||
|
return custom_error_response(ERROR_CODE['2060'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
class ReAssignJuniorTaskAPIView(views.APIView):
|
||||||
|
"""Update junior task API
|
||||||
|
Payload
|
||||||
|
{
|
||||||
|
"task_id":34,
|
||||||
|
"due_date":"2023-08-22"
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
serializer_class = ReAssignTaskSerializer
|
||||||
|
model = JuniorTask
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def put(self, request, format=None):
|
||||||
|
try:
|
||||||
|
task_id = self.request.data.get('task_id')
|
||||||
|
task_queryset = JuniorTask.objects.filter(id=task_id, guardian__user__email=self.request.user).last()
|
||||||
|
if task_queryset and task_queryset.task_status == str(NUMBER['six']):
|
||||||
|
# use StartTaskSerializer serializer
|
||||||
|
serializer = ReAssignTaskSerializer(task_queryset, data=request.data, partial=True)
|
||||||
|
if serializer.is_valid():
|
||||||
|
# save serializer
|
||||||
|
serializer.save()
|
||||||
|
return custom_response(SUCCESS_CODE['3036'], response_status=status.HTTP_200_OK)
|
||||||
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
else:
|
||||||
|
# task in another state
|
||||||
|
return custom_error_response(ERROR_CODE['2066'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
class StartArticleAPIView(viewsets.ModelViewSet):
|
||||||
|
"""Start article"""
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('post',)
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
""" Payload
|
||||||
|
{
|
||||||
|
"article_id":"2"
|
||||||
|
}"""
|
||||||
|
try:
|
||||||
|
junior_instance = Junior.objects.filter(auth=self.request.user).last()
|
||||||
|
article_id = request.data.get('article_id')
|
||||||
|
article_data = Article.objects.filter(id=article_id).last()
|
||||||
|
if not JuniorArticle.objects.filter(junior=junior_instance, article=article_data).last():
|
||||||
|
JuniorArticle.objects.create(junior=junior_instance, article=article_data, status=str(NUMBER['two']),
|
||||||
|
current_card_page=NUMBER['zero'], current_que_page=NUMBER['zero'])
|
||||||
|
if article_data:
|
||||||
|
question_query = ArticleSurvey.objects.filter(article=article_id)
|
||||||
|
for question in question_query:
|
||||||
|
if not JuniorArticlePoints.objects.filter(junior=junior_instance,
|
||||||
|
article=article_data,
|
||||||
|
question=question):
|
||||||
|
JuniorArticlePoints.objects.create(junior=junior_instance,
|
||||||
|
article=article_data,
|
||||||
|
question=question)
|
||||||
|
return custom_response(SUCCESS_CODE['3040'], response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
class StartAssessmentAPIView(viewsets.ModelViewSet):
|
||||||
|
"""Question answer viewset"""
|
||||||
|
serializer_class = StartAssessmentSerializer
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('get',)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
article_id = self.request.GET.get('article_id')
|
||||||
|
# if referral_code:
|
||||||
|
article = Article.objects.filter(id=article_id, is_deleted=False).prefetch_related(
|
||||||
|
'article_survey'
|
||||||
|
)
|
||||||
|
return article
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
"""Params
|
||||||
|
article_id
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
paginator = self.pagination_class()
|
||||||
|
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||||
|
serializer = self.serializer_class(paginated_queryset, context={"user":request.user}, many=True)
|
||||||
|
return custom_response(None, serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
class CheckAnswerAPIView(viewsets.ModelViewSet):
|
||||||
|
"""Params
|
||||||
|
question_id=1
|
||||||
|
answer_id=1"""
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('get',)
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
question_id = self.request.GET.get('question_id')
|
||||||
|
article = ArticleSurvey.objects.filter(id=question_id).last()
|
||||||
|
return article
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
""" Params
|
||||||
|
question_id=1
|
||||||
|
answer_id=1
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
answer_id = self.request.GET.get('answer_id')
|
||||||
|
current_page = self.request.GET.get('current_page')
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
submit_ans = SurveyOption.objects.filter(id=answer_id).last()
|
||||||
|
junior_article_points = JuniorArticlePoints.objects.filter(junior__auth=self.request.user,
|
||||||
|
question=queryset)
|
||||||
|
if submit_ans.is_answer:
|
||||||
|
junior_article_points.update(submitted_answer=submit_ans, is_attempt=True, is_answer_correct=True)
|
||||||
|
JuniorPoints.objects.filter(junior__auth=self.request.user).update(total_points=
|
||||||
|
F('total_points') + queryset.points)
|
||||||
|
else:
|
||||||
|
junior_article_points.update(submitted_answer=submit_ans, is_attempt=True, earn_points=0,
|
||||||
|
is_answer_correct=False)
|
||||||
|
JuniorArticle.objects.filter(junior__auth=self.request.user,
|
||||||
|
article=queryset.article).update(
|
||||||
|
current_que_page=int(current_page) + NUMBER['one'])
|
||||||
|
return custom_response(None, response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
class CompleteArticleAPIView(views.APIView):
|
||||||
|
"""Params
|
||||||
|
article_id
|
||||||
|
"""
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('put', 'get',)
|
||||||
|
def put(self, request, format=None):
|
||||||
|
try:
|
||||||
|
article_id = self.request.data.get('article_id')
|
||||||
|
JuniorArticle.objects.filter(junior__auth=request.user, article__id=article_id).update(
|
||||||
|
is_completed=True, status=str(NUMBER['three'])
|
||||||
|
)
|
||||||
|
return custom_response(SUCCESS_CODE['3041'], response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def get(self, request, *args, **kwargs):
|
||||||
|
""" Params
|
||||||
|
article_id=1"""
|
||||||
|
try:
|
||||||
|
article_id = self.request.GET.get('article_id')
|
||||||
|
total_earn_points = JuniorArticlePoints.objects.filter(junior__auth=request.user,
|
||||||
|
article__id=article_id,
|
||||||
|
is_answer_correct=True).aggregate(
|
||||||
|
total_earn_points=Sum('earn_points'))['total_earn_points']
|
||||||
|
data = {"total_earn_points":total_earn_points}
|
||||||
|
if total_earn_points:
|
||||||
|
send_notification.delay(ARTICLE_REWARD_POINTS, None, GUARDIAN,
|
||||||
|
request.user.id, {'points': total_earn_points})
|
||||||
|
return custom_response(SUCCESS_CODE['3042'], data, response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class ReadArticleCardAPIView(views.APIView):
|
||||||
|
"""Read article card API"""
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('put',)
|
||||||
|
|
||||||
|
def put(self, request, *args, **kwargs):
|
||||||
|
""" Read article card
|
||||||
|
Payload
|
||||||
|
{"article_id":"1",
|
||||||
|
"article_card":"2",
|
||||||
|
"current_page":"2"}"""
|
||||||
|
try:
|
||||||
|
junior_instance = Junior.objects.filter(auth=self.request.user).last()
|
||||||
|
article = self.request.data.get('article_id')
|
||||||
|
article_card = self.request.data.get('article_card')
|
||||||
|
current_page = self.request.data.get('current_page')
|
||||||
|
JuniorArticleCard.objects.filter(junior=junior_instance,
|
||||||
|
article__id=article,
|
||||||
|
article_card__id=article_card).update(is_read=True)
|
||||||
|
JuniorArticle.objects.filter(junior=junior_instance,
|
||||||
|
article__id=article).update(current_card_page=int(current_page)+NUMBER['one'])
|
||||||
|
return custom_response(SUCCESS_CODE['3043'], response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class CreateArticleCardAPIView(viewsets.ModelViewSet):
|
||||||
|
"""Start article"""
|
||||||
|
serializer_class = CreateArticleCardSerializer
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ('post',)
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
""" create article card
|
||||||
|
Params
|
||||||
|
{"article_id":1}"""
|
||||||
|
try:
|
||||||
|
junior_instance = Junior.objects.filter(auth=self.request.user).last()
|
||||||
|
article_id = request.data.get('article_id')
|
||||||
|
article_data = Article.objects.filter(id=article_id).last()
|
||||||
|
if article_data:
|
||||||
|
article_cards = ArticleCard.objects.filter(article=article_id)
|
||||||
|
for article_card in article_cards:
|
||||||
|
if not JuniorArticleCard.objects.filter(junior=junior_instance,
|
||||||
|
article=article_data,
|
||||||
|
article_card=article_card):
|
||||||
|
JuniorArticleCard.objects.create(junior=junior_instance,
|
||||||
|
article=article_data,
|
||||||
|
article_card=article_card)
|
||||||
|
return custom_response(None, response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
class RemoveGuardianCodeAPIView(views.APIView):
|
||||||
|
"""Remove guardian code request API
|
||||||
|
Payload
|
||||||
|
{"guardian_code"
|
||||||
|
:"GRD037"
|
||||||
|
}"""
|
||||||
|
serializer_class = RemoveGuardianCodeSerializer
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
|
def put(self, request, format=None):
|
||||||
|
try:
|
||||||
|
guardian_code = self.request.data.get("guardian_code")
|
||||||
|
guardian_data = Guardian.objects.filter(guardian_code=guardian_code).last()
|
||||||
|
junior_queryset = Junior.objects.filter(auth=self.request.user).last()
|
||||||
|
if junior_queryset:
|
||||||
|
# use RemoveGuardianCodeSerializer serializer
|
||||||
|
serializer = RemoveGuardianCodeSerializer(junior_queryset, context = {"guardian_code":guardian_code},
|
||||||
|
data=request.data, partial=True)
|
||||||
|
if serializer.is_valid():
|
||||||
|
# save serializer
|
||||||
|
serializer.save()
|
||||||
|
JuniorGuardianRelationship.objects.filter(guardian=guardian_data, junior=junior_queryset).delete()
|
||||||
|
return custom_response(SUCCESS_CODE['3044'], response_status=status.HTTP_200_OK)
|
||||||
|
return custom_error_response(serializer.errors, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
else:
|
||||||
|
# task in another state
|
||||||
|
return custom_error_response(ERROR_CODE['2047'], response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
except Exception as e:
|
||||||
|
error_detail = e.detail.get('error', None)
|
||||||
|
return custom_error_response(error_detail, response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class FAQViewSet(GenericViewSet, mixins.CreateModelMixin,
|
||||||
|
mixins.ListModelMixin):
|
||||||
|
"""FAQ view set"""
|
||||||
|
|
||||||
|
serializer_class = FAQSerializer
|
||||||
|
permission_classes = [IsAuthenticated]
|
||||||
|
http_method_names = ['get', 'post']
|
||||||
|
|
||||||
|
def get_queryset(self):
|
||||||
|
return FAQ.objects.all()
|
||||||
|
|
||||||
|
def create(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
faq create api method
|
||||||
|
:param request:
|
||||||
|
:param args: question, description
|
||||||
|
:param kwargs:
|
||||||
|
:return: success message
|
||||||
|
"""
|
||||||
|
obj_data = [FAQ(**item) for item in request.data]
|
||||||
|
try:
|
||||||
|
FAQ.objects.bulk_create(obj_data)
|
||||||
|
return custom_response(SUCCESS_CODE["3045"], response_status=status.HTTP_200_OK)
|
||||||
|
except Exception as e:
|
||||||
|
return custom_error_response(str(e), response_status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
article list api method
|
||||||
|
:param request:
|
||||||
|
:param args:
|
||||||
|
:param kwargs:
|
||||||
|
:return: list of article
|
||||||
|
"""
|
||||||
|
queryset = self.get_queryset()
|
||||||
|
paginator = self.pagination_class()
|
||||||
|
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||||
|
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||||
|
return custom_response(None, data=serializer.data, response_status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +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 module
|
||||||
import os
|
import os
|
||||||
"""Import sys module"""
|
# 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"""
|
||||||
|
|||||||
@@ -0,0 +1,14 @@
|
|||||||
|
"""
|
||||||
|
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']
|
||||||
|
list_filter = ['notification_type']
|
||||||
|
search_fields = ['notification_to']
|
||||||
@@ -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'
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
"""
|
||||||
|
notification constants file
|
||||||
|
"""
|
||||||
|
REGISTRATION = 1
|
||||||
|
ASSOCIATE_REQUEST = 3
|
||||||
|
ASSOCIATE_REJECTED = 4
|
||||||
|
ASSOCIATE_APPROVED = 5
|
||||||
|
REFERRAL_POINTS = 6
|
||||||
|
ASSOCIATE_JUNIOR = 7
|
||||||
|
ASSOCIATE_EXISTING_JUNIOR = 8
|
||||||
|
|
||||||
|
TASK_ASSIGNED = 9
|
||||||
|
TASK_ACTION = 10
|
||||||
|
TASK_REJECTED = 11
|
||||||
|
TASK_APPROVED = 12
|
||||||
|
PENDING_TASK_EXPIRING = 13
|
||||||
|
IN_PROGRESS_TASK_EXPIRING = 14
|
||||||
|
TOP_JUNIOR = 15
|
||||||
|
|
||||||
|
NEW_ARTICLE_PUBLISHED = 16
|
||||||
|
ARTICLE_REWARD_POINTS = 17
|
||||||
|
REMOVE_JUNIOR = 18
|
||||||
|
|
||||||
|
TEST_NOTIFICATION = 99
|
||||||
|
|
||||||
|
# notification dictionary
|
||||||
|
NOTIFICATION_DICT = {
|
||||||
|
REGISTRATION: {
|
||||||
|
"notification_type": REGISTRATION,
|
||||||
|
"title": "Successfully registered!",
|
||||||
|
"body": "You have registered successfully. Now login and complete your profile."
|
||||||
|
},
|
||||||
|
# user will receive notification as soon junior
|
||||||
|
# sign up application using their guardian code for association
|
||||||
|
ASSOCIATE_REQUEST: {
|
||||||
|
"notification_type": ASSOCIATE_REQUEST,
|
||||||
|
"title": "Associate request!",
|
||||||
|
"body": "You have request from {from_user} to associate with you."
|
||||||
|
},
|
||||||
|
# Juniors will receive notification when
|
||||||
|
# custodians reject their request for associate
|
||||||
|
ASSOCIATE_REJECTED: {
|
||||||
|
"notification_type": ASSOCIATE_REJECTED,
|
||||||
|
"title": "Associate request rejected!",
|
||||||
|
"body": "Your request to associate has been rejected by {from_user}."
|
||||||
|
},
|
||||||
|
# Juniors will receive notification when
|
||||||
|
# custodians approve their request for associate
|
||||||
|
ASSOCIATE_APPROVED: {
|
||||||
|
"notification_type": ASSOCIATE_APPROVED,
|
||||||
|
"title": "Associate request approved!",
|
||||||
|
"body": "Your request to associate has been approved by {from_user}."
|
||||||
|
},
|
||||||
|
# Juniors will receive Notifications
|
||||||
|
# for every Points earned by referrals
|
||||||
|
REFERRAL_POINTS: {
|
||||||
|
"notification_type": REFERRAL_POINTS,
|
||||||
|
"title": "Earn Referral points!",
|
||||||
|
"body": "You earn 5 points for referral."
|
||||||
|
},
|
||||||
|
# Juniors will receive notification
|
||||||
|
# once any custodians add them in their account
|
||||||
|
ASSOCIATE_JUNIOR: {
|
||||||
|
"notification_type": ASSOCIATE_JUNIOR,
|
||||||
|
"title": "Profile already setup!",
|
||||||
|
"body": "Your guardian has already setup your profile."
|
||||||
|
},
|
||||||
|
ASSOCIATE_EXISTING_JUNIOR: {
|
||||||
|
"notification_type": ASSOCIATE_EXISTING_JUNIOR,
|
||||||
|
"title": "Associated to guardian",
|
||||||
|
"body": "Your are associated to your guardian {from_user}."
|
||||||
|
},
|
||||||
|
# Juniors will receive Notification
|
||||||
|
# for every Task Assign by Custodians
|
||||||
|
TASK_ASSIGNED: {
|
||||||
|
"notification_type": TASK_ASSIGNED,
|
||||||
|
"title": "New task assigned!",
|
||||||
|
"body": "{from_user} has assigned you a new task."
|
||||||
|
},
|
||||||
|
# Guardian will receive notification as soon
|
||||||
|
# as junior send task for approval
|
||||||
|
TASK_ACTION: {
|
||||||
|
"notification_type": TASK_ACTION,
|
||||||
|
"title": "Task completion approval!",
|
||||||
|
"body": "{from_user} completed their task {task_name}."
|
||||||
|
},
|
||||||
|
# Juniors will receive notification as soon
|
||||||
|
# as their task is rejected by custodians
|
||||||
|
TASK_REJECTED: {
|
||||||
|
"notification_type": TASK_REJECTED,
|
||||||
|
"title": "Task completion rejected!",
|
||||||
|
"body": "Your task completion request has been rejected by {from_user}."
|
||||||
|
},
|
||||||
|
# Juniors will receive notification as soon as their task is approved by custodians
|
||||||
|
# and for every Points earned by Task completion
|
||||||
|
TASK_APPROVED: {
|
||||||
|
"notification_type": TASK_APPROVED,
|
||||||
|
"title": "Task completion approved!",
|
||||||
|
"body": "Your task completion request has been approved by {from_user}. "
|
||||||
|
"Also you earned 5 points for successful completion."
|
||||||
|
},
|
||||||
|
# Juniors will receive notification when their task end date about to end
|
||||||
|
PENDING_TASK_EXPIRING: {
|
||||||
|
"notification_type": PENDING_TASK_EXPIRING,
|
||||||
|
"title": "Task expiring soon!",
|
||||||
|
"body": "Your task {task_name} is expiring soon. Please complete it."
|
||||||
|
},
|
||||||
|
# User will receive notification when their assigned task is about to end
|
||||||
|
# and juniors have not performed any action
|
||||||
|
IN_PROGRESS_TASK_EXPIRING: {
|
||||||
|
"notification_type": IN_PROGRESS_TASK_EXPIRING,
|
||||||
|
"title": "Task expiring soon!",
|
||||||
|
"body": "{from_user} didn't take any action on assigned task {task_name} and it's expiring soon. "
|
||||||
|
"Please assist to complete it."
|
||||||
|
},
|
||||||
|
# Juniors will receive Notification
|
||||||
|
# related to Leaderboard progress
|
||||||
|
TOP_JUNIOR: {
|
||||||
|
"notification_type": TOP_JUNIOR,
|
||||||
|
"title": "Leaderboard topper!",
|
||||||
|
"body": "{from_user} is on top in leaderboard with {points} points."
|
||||||
|
},
|
||||||
|
# Juniors will receive notification
|
||||||
|
# when admin add any new financial learnings
|
||||||
|
NEW_ARTICLE_PUBLISHED: {
|
||||||
|
"notification_type": NEW_ARTICLE_PUBLISHED,
|
||||||
|
"title": "Time to read!",
|
||||||
|
"body": "A new article has been published."
|
||||||
|
},
|
||||||
|
# Juniors will receive notification when they earn points by reading financial Learning
|
||||||
|
ARTICLE_REWARD_POINTS: {
|
||||||
|
"notification_type": ARTICLE_REWARD_POINTS,
|
||||||
|
"title": "Article reward points!",
|
||||||
|
"body": "You are rewarded with {points} points for reading article and answering questions. "
|
||||||
|
},
|
||||||
|
# Juniors will receive notification as soon as their custodians remove them from account
|
||||||
|
REMOVE_JUNIOR: {
|
||||||
|
"notification_type": REMOVE_JUNIOR,
|
||||||
|
"title": "Disassociate by guardian!",
|
||||||
|
"body": "Your guardian has disassociated you."
|
||||||
|
},
|
||||||
|
# Test notification
|
||||||
|
TEST_NOTIFICATION: {
|
||||||
|
"notification_type": TEST_NOTIFICATION,
|
||||||
|
"title": "Test Notification",
|
||||||
|
"body": "This notification is for testing purpose from {from_user}."
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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)),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-09-29 07:00
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('notifications', '0001_initial'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='notification',
|
||||||
|
name='updated_at',
|
||||||
|
field=models.DateTimeField(auto_now=True),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
"""
|
||||||
|
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)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
""" string representation """
|
||||||
|
return f"{self.notification_to.id} | {self.notification_to.email}"
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
"""
|
||||||
|
notification serializer file
|
||||||
|
"""
|
||||||
|
# third party imports
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from notifications.utils import register_fcm_token
|
||||||
|
from notifications.models import Notification
|
||||||
|
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationListSerializer(serializers.ModelSerializer):
|
||||||
|
"""List of notification"""
|
||||||
|
badge = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""meta info"""
|
||||||
|
model = Notification
|
||||||
|
fields = ['id', 'notification_type', 'data', 'badge', 'is_read', 'updated_at']
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_badge(obj):
|
||||||
|
return Notification.objects.filter(notification_to=obj.notification_to, is_read=False).count()
|
||||||
|
|
||||||
|
|
||||||
|
class ReadNotificationSerializer(serializers.ModelSerializer):
|
||||||
|
"""User task Serializer"""
|
||||||
|
id = serializers.ListSerializer(child=serializers.IntegerField())
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""Meta class"""
|
||||||
|
model = Notification
|
||||||
|
fields = ('id',)
|
||||||
@@ -0,0 +1,98 @@
|
|||||||
|
"""
|
||||||
|
notification test file
|
||||||
|
"""
|
||||||
|
# third party imports
|
||||||
|
from fcm_django.models import FCMDevice
|
||||||
|
|
||||||
|
# django imports
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from account.models import UserNotification
|
||||||
|
# local imports
|
||||||
|
from account.serializers import GuardianSerializer
|
||||||
|
from notifications.models import Notification
|
||||||
|
from web_admin.tests.test_set_up import AnalyticsSetUp
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationTestCase(AnalyticsSetUp):
|
||||||
|
"""
|
||||||
|
test notification
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
test data up
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(NotificationTestCase, self).setUp()
|
||||||
|
|
||||||
|
# notification settings create
|
||||||
|
UserNotification.objects.create(user=self.user)
|
||||||
|
|
||||||
|
# notification create
|
||||||
|
self.notification = Notification.objects.create(notification_to=self.user, notification_from=self.user_3)
|
||||||
|
|
||||||
|
# to get guardian/user auth token
|
||||||
|
self.guardian_data = GuardianSerializer(
|
||||||
|
self.guardian, context={'user_type': 2}
|
||||||
|
).data
|
||||||
|
self.auth_token = self.guardian_data['auth_token']
|
||||||
|
|
||||||
|
# api header
|
||||||
|
self.header = {
|
||||||
|
'HTTP_AUTHORIZATION': f'Bearer {self.auth_token}',
|
||||||
|
'Content-Type': "Application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
def test_notification_list(self):
|
||||||
|
"""
|
||||||
|
test notification list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
url = reverse('notifications:notifications-list')
|
||||||
|
response = self.client.get(url, **self.header)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one notification exists in the database
|
||||||
|
self.assertEqual(Notification.objects.filter(notification_to=self.user).count(), 1)
|
||||||
|
|
||||||
|
def test_fcm_register(self):
|
||||||
|
"""
|
||||||
|
test fcm register
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('notifications:notifications-device')
|
||||||
|
data = {
|
||||||
|
'registration_id': 'registration_id',
|
||||||
|
'device_id': 'device_id',
|
||||||
|
'type': 'ios'
|
||||||
|
}
|
||||||
|
response = self.client.post(url, data, **self.header)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# device created for user
|
||||||
|
self.assertEqual(FCMDevice.objects.count(), 1)
|
||||||
|
|
||||||
|
def test_send_test_notification(self):
|
||||||
|
"""
|
||||||
|
test send test notification
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('notifications:notifications-test')
|
||||||
|
response = self.client.get(url, **self.header)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming one notification exists in the database and two created after api run
|
||||||
|
self.assertEqual(Notification.objects.filter(notification_to=self.user).count(), 3)
|
||||||
|
|
||||||
|
def test_mark_as_read(self):
|
||||||
|
"""
|
||||||
|
test mark as read
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('notifications:notifications-mark-as-read')
|
||||||
|
data = {
|
||||||
|
'id': [self.notification.id]
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, **self.header)
|
||||||
|
self.notification.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.notification.is_read, True)
|
||||||
@@ -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)),
|
||||||
|
]
|
||||||
@@ -0,0 +1,165 @@
|
|||||||
|
"""
|
||||||
|
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 django.db.models import Q
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from account.models import UserNotification
|
||||||
|
from account.utils import get_user_full_name
|
||||||
|
from base.constants import GUARDIAN, JUNIOR
|
||||||
|
from guardian.models import Guardian, JuniorTask
|
||||||
|
from junior.models import Junior
|
||||||
|
from notifications.constants import NOTIFICATION_DICT
|
||||||
|
from notifications.models import Notification
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
def register_fcm_token(user_id, registration_id, device_id, device_type):
|
||||||
|
""" used to register the fcm device token"""
|
||||||
|
FCMDevice.objects.filter(registration_id=registration_id).delete()
|
||||||
|
device, _ = FCMDevice.objects.update_or_create(user_id=user_id,
|
||||||
|
defaults={'device_id': device_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_from_user_details(from_user_id, from_user_type):
|
||||||
|
"""
|
||||||
|
used to get from user details
|
||||||
|
"""
|
||||||
|
from_user = None
|
||||||
|
from_user_name = None
|
||||||
|
from_user_image = None
|
||||||
|
if from_user_id:
|
||||||
|
if from_user_type == GUARDIAN:
|
||||||
|
guardian = Guardian.objects.filter(user_id=from_user_id).select_related('user').first()
|
||||||
|
from_user = guardian.user
|
||||||
|
from_user_name = get_user_full_name(from_user)
|
||||||
|
from_user_image = guardian.image
|
||||||
|
elif from_user_type == JUNIOR:
|
||||||
|
junior = Junior.objects.filter(auth_id=from_user_id).select_related('auth').first()
|
||||||
|
from_user = junior.auth
|
||||||
|
from_user_name = get_user_full_name(from_user)
|
||||||
|
from_user_image = junior.image
|
||||||
|
return from_user_name, from_user_image, from_user
|
||||||
|
|
||||||
|
|
||||||
|
def get_notification_data(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
|
||||||
|
"""
|
||||||
|
get notification and push data
|
||||||
|
:param from_user_type: GUARDIAN or JUNIOR
|
||||||
|
:param notification_type: notification_type
|
||||||
|
:param from_user_id: from_user obj
|
||||||
|
:param to_user_id: to_user obj
|
||||||
|
:param extra_data: any extra data provided
|
||||||
|
:return: notification and push data
|
||||||
|
"""
|
||||||
|
push_data = NOTIFICATION_DICT[notification_type].copy()
|
||||||
|
notification_data = push_data.copy()
|
||||||
|
task_name = None
|
||||||
|
points = extra_data.get('points', None)
|
||||||
|
if 'task_id' in extra_data:
|
||||||
|
task = JuniorTask.objects.filter(id=extra_data.get('task_id')).first()
|
||||||
|
task_name = task.task_name
|
||||||
|
extra_data['task_name'] = task_name
|
||||||
|
extra_data['task_image'] = task.image if task.image else task.default_image
|
||||||
|
|
||||||
|
from_user_name, from_user_image, from_user = get_from_user_details(from_user_id, from_user_type)
|
||||||
|
|
||||||
|
push_data['body'] = push_data['body'].format(from_user=from_user_name, task_name=task_name, points=points)
|
||||||
|
notification_data['body'] = notification_data['body'].format(from_user=from_user_name,
|
||||||
|
task_name=task_name, points=points)
|
||||||
|
|
||||||
|
push_data['to_user_type'] = GUARDIAN if from_user_type == JUNIOR else JUNIOR
|
||||||
|
notification_data['to_user_type'] = GUARDIAN if from_user_type == JUNIOR else JUNIOR
|
||||||
|
|
||||||
|
notification_data['from_user'] = from_user_name
|
||||||
|
notification_data['from_user_image'] = from_user_image
|
||||||
|
|
||||||
|
|
||||||
|
notification_data.update(extra_data)
|
||||||
|
to_user = User.objects.filter(id=to_user_id).first()
|
||||||
|
return notification_data, push_data, from_user, to_user
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task()
|
||||||
|
def send_notification(notification_type, from_user_id, from_user_type, to_user_id, extra_data):
|
||||||
|
"""
|
||||||
|
used to send the push for the given notification type
|
||||||
|
"""
|
||||||
|
notification_data, push_data, from_user, to_user = get_notification_data(notification_type, from_user_id,
|
||||||
|
from_user_type, to_user_id, extra_data)
|
||||||
|
user_notification_type = UserNotification.objects.filter(user=to_user).first()
|
||||||
|
|
||||||
|
# notification create method changed on 28sep as per changes required
|
||||||
|
task_id = extra_data['task_id'] if 'task_id' in extra_data else None
|
||||||
|
Notification.objects.update_or_create(data__has_key='task_id', data__task_id=task_id,
|
||||||
|
notification_from=from_user, notification_to=to_user,
|
||||||
|
defaults={
|
||||||
|
'notification_type': notification_type,
|
||||||
|
'notification_from': from_user,
|
||||||
|
'notification_to': to_user,
|
||||||
|
'data': notification_data
|
||||||
|
})
|
||||||
|
|
||||||
|
if user_notification_type and user_notification_type.push_notification:
|
||||||
|
send_push(to_user, push_data)
|
||||||
|
|
||||||
|
|
||||||
|
def send_push(user, data):
|
||||||
|
""" used to send push notification to specific user """
|
||||||
|
data['notification_type'] = str(data['notification_type'])
|
||||||
|
user.fcmdevice_set.filter(active=True).send_message(
|
||||||
|
Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def send_multiple_push(queryset, data):
|
||||||
|
""" used to send same notification to multiple users """
|
||||||
|
data['notification_type'] = str(data['notification_type'])
|
||||||
|
FCMDevice.objects.filter(user__in=queryset, active=True).send_message(
|
||||||
|
Message(notification=FirebaseNotification(data['title'], data['body']), data=data)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@shared_task()
|
||||||
|
def send_notification_multiple_user(notification_type, from_user_id, from_user_type,
|
||||||
|
extra_data: dict = {}):
|
||||||
|
"""
|
||||||
|
used to send notification to multiple user for the given notification type
|
||||||
|
"""
|
||||||
|
to_user_list = User.objects.filter(junior_profile__is_verified=True, is_superuser=False
|
||||||
|
).exclude(junior_profile__isnull=True, guardian_profile__isnull=True)
|
||||||
|
|
||||||
|
notification_data, push_data, from_user, _ = get_notification_data(notification_type, from_user_id,
|
||||||
|
from_user_type, None, extra_data)
|
||||||
|
|
||||||
|
notification_list = []
|
||||||
|
for user in to_user_list:
|
||||||
|
notification_list.append(Notification(notification_type=notification_type,
|
||||||
|
notification_to=user,
|
||||||
|
notification_from=from_user,
|
||||||
|
data=notification_data))
|
||||||
|
Notification.objects.bulk_create(notification_list)
|
||||||
|
to_user_list = to_user_list.filter(user_notification__push_notification=True)
|
||||||
|
send_multiple_push(to_user_list, push_data)
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
"""
|
||||||
|
notifications views file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from django.db.models import Q
|
||||||
|
from rest_framework.decorators import action
|
||||||
|
from rest_framework.permissions import IsAuthenticated, AllowAny
|
||||||
|
from rest_framework.response import Response
|
||||||
|
from rest_framework import viewsets, status, views
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from account.utils import custom_response, custom_error_response
|
||||||
|
from base.messages import SUCCESS_CODE, ERROR_CODE
|
||||||
|
from base.pagination import CustomPageNumberPagination
|
||||||
|
from base.tasks import notify_task_expiry, notify_top_junior
|
||||||
|
from notifications.constants import TEST_NOTIFICATION
|
||||||
|
from notifications.serializers import RegisterDevice, NotificationListSerializer, ReadNotificationSerializer
|
||||||
|
from notifications.utils import send_notification
|
||||||
|
from notifications.models import Notification
|
||||||
|
|
||||||
|
|
||||||
|
class NotificationViewSet(viewsets.GenericViewSet):
|
||||||
|
"""
|
||||||
|
used to do the notification actions
|
||||||
|
"""
|
||||||
|
serializer_class = NotificationListSerializer
|
||||||
|
permission_classes = [IsAuthenticated, ]
|
||||||
|
|
||||||
|
def list(self, request, *args, **kwargs) -> Response:
|
||||||
|
"""
|
||||||
|
to list user's notifications
|
||||||
|
:param request:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
queryset = Notification.objects.filter(notification_to_id=request.auth.payload['user_id']
|
||||||
|
).select_related('notification_to').order_by('-updated_at', '-id')
|
||||||
|
paginator = CustomPageNumberPagination()
|
||||||
|
paginated_queryset = paginator.paginate_queryset(queryset, request)
|
||||||
|
serializer = self.serializer_class(paginated_queryset, many=True)
|
||||||
|
return paginator.get_paginated_response(serializer.data)
|
||||||
|
|
||||||
|
@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')
|
||||||
|
def send_test_notification(self, request):
|
||||||
|
"""
|
||||||
|
to test send notification, task expiry, top junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
notify_task_expiry()
|
||||||
|
notify_top_junior()
|
||||||
|
notification_type = request.query_params.get('type', TEST_NOTIFICATION)
|
||||||
|
from_user_type = request.query_params.get('from_user_type')
|
||||||
|
send_notification(int(notification_type), None, from_user_type, request.auth.payload['user_id'],
|
||||||
|
{})
|
||||||
|
if notification_type and request.query_params.get('clear_all'):
|
||||||
|
Notification.objects.filter(notification_type=notification_type).delete()
|
||||||
|
return custom_response(SUCCESS_CODE["3000"])
|
||||||
|
|
||||||
|
@action(methods=['patch'], url_path='mark-as-read', url_name='mark-as-read', detail=False,
|
||||||
|
serializer_class=ReadNotificationSerializer)
|
||||||
|
def mark_as_read(self, request, *args, **kwargs):
|
||||||
|
"""
|
||||||
|
notification list
|
||||||
|
"""
|
||||||
|
|
||||||
|
if request.data.get('id'):
|
||||||
|
Notification.objects.filter(id__in=request.data.get('id')).update(is_read=True)
|
||||||
|
|
||||||
|
elif request.query_params.get('mark_all'):
|
||||||
|
Notification.objects.filter(notification_to_id=request.auth.payload['user_id']).update(is_read=True)
|
||||||
|
|
||||||
|
elif request.query_params.get('clear_all'):
|
||||||
|
Notification.objects.filter(notification_to_id=request.auth.payload['user_id']).delete()
|
||||||
|
|
||||||
|
return custom_response(SUCCESS_CODE['3039'], response_status=status.HTTP_200_OK)
|
||||||
@@ -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
|
||||||
@@ -41,8 +42,22 @@ 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
|
itypes==1.2.0
|
||||||
@@ -51,17 +66,22 @@ jmespath==0.10.0
|
|||||||
kombu==5.3.1
|
kombu==5.3.1
|
||||||
MarkupSafe==2.1.3
|
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
|
||||||
@@ -79,3 +99,7 @@ uritemplate==4.1.1
|
|||||||
urllib3==1.26.16
|
urllib3==1.26.16
|
||||||
vine==5.0.0
|
vine==5.0.0
|
||||||
wcwidth==0.2.6
|
wcwidth==0.2.6
|
||||||
|
|
||||||
|
pandas==2.0.3
|
||||||
|
XlsxWriter==3.1.2
|
||||||
|
coverage==7.3.1
|
||||||
|
|||||||
@@ -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']
|
||||||
@@ -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'
|
||||||
@@ -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',
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
# Generated by Django 4.2.2 on 2023-08-01 07:35
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
import django.db.models.deletion
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('web_admin', '0003_defaultarticlecardimage_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='surveyoption',
|
||||||
|
name='survey',
|
||||||
|
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='options', to='web_admin.articlesurvey'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@@ -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.question}'
|
||||||
|
|
||||||
|
|
||||||
|
class SurveyOption(models.Model):
|
||||||
|
"""
|
||||||
|
Survey Options model
|
||||||
|
"""
|
||||||
|
survey = models.ForeignKey(ArticleSurvey, on_delete=models.CASCADE, related_name='options')
|
||||||
|
option = models.CharField(max_length=255)
|
||||||
|
is_answer = models.BooleanField(default=False)
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""Return title"""
|
||||||
|
return f'{self.id} | {self.option}'
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultArticleCardImage(models.Model):
|
||||||
|
"""
|
||||||
|
Default images upload in oss bucket
|
||||||
|
"""
|
||||||
|
image_name = models.CharField(max_length=20)
|
||||||
|
image_url = models.URLField()
|
||||||
|
created_at = models.DateTimeField(auto_now_add=True)
|
||||||
|
updated_at = models.DateTimeField(auto_now=True)
|
||||||
|
|
||||||
|
def __str__(self):
|
||||||
|
"""return image_name as an object"""
|
||||||
|
return self.image_name
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
"""
|
||||||
|
web_admin analytics serializer file
|
||||||
|
"""
|
||||||
|
# third party imports
|
||||||
|
from rest_framework import serializers
|
||||||
|
|
||||||
|
# django imports
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from account.utils import get_user_full_name
|
||||||
|
# local imports
|
||||||
|
from base.constants import USER_TYPE, JUNIOR
|
||||||
|
|
||||||
|
from junior.models import JuniorPoints, Junior
|
||||||
|
|
||||||
|
USER = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class JuniorLeaderboardSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
junior leaderboard serializer
|
||||||
|
"""
|
||||||
|
name = serializers.SerializerMethodField()
|
||||||
|
first_name = serializers.SerializerMethodField()
|
||||||
|
last_name = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = Junior
|
||||||
|
fields = ('id', 'name', 'first_name', 'last_name', 'is_active', 'image', 'is_deleted')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: junior object
|
||||||
|
:return: full name
|
||||||
|
"""
|
||||||
|
return get_user_full_name(obj.auth)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_first_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: junior object
|
||||||
|
:return: first name
|
||||||
|
"""
|
||||||
|
return obj.auth.first_name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_last_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: junior object
|
||||||
|
:return: last name
|
||||||
|
"""
|
||||||
|
return obj.auth.last_name
|
||||||
|
|
||||||
|
|
||||||
|
class LeaderboardSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
leaderboard serializer
|
||||||
|
"""
|
||||||
|
user_id = serializers.SerializerMethodField()
|
||||||
|
user_type = serializers.SerializerMethodField()
|
||||||
|
junior = JuniorLeaderboardSerializer()
|
||||||
|
rank = serializers.IntegerField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = JuniorPoints
|
||||||
|
fields = ('user_id', 'user_type', 'total_points', 'rank', 'junior')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_user_id(obj):
|
||||||
|
return obj.junior.auth.id
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_user_type(obj):
|
||||||
|
return JUNIOR
|
||||||
|
|
||||||
|
|
||||||
|
class UserCSVReportSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
user csv/xls report serializer
|
||||||
|
"""
|
||||||
|
name = serializers.SerializerMethodField()
|
||||||
|
phone_number = serializers.SerializerMethodField()
|
||||||
|
user_type = serializers.SerializerMethodField()
|
||||||
|
is_active = serializers.SerializerMethodField()
|
||||||
|
date_joined = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = USER
|
||||||
|
fields = ('name', 'email', 'phone_number', 'user_type', 'is_active', 'date_joined')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: full name
|
||||||
|
"""
|
||||||
|
return get_user_full_name(obj)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_phone_number(obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: user phone number
|
||||||
|
"""
|
||||||
|
if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()):
|
||||||
|
return f"+{profile.country_code}{profile.phone}" \
|
||||||
|
if profile.country_code and profile.phone else profile.phone
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_user_type(obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: user type
|
||||||
|
"""
|
||||||
|
if obj.guardian_profile.all().first():
|
||||||
|
return dict(USER_TYPE).get('2').capitalize()
|
||||||
|
elif obj.junior_profile.all().first():
|
||||||
|
return dict(USER_TYPE).get('1').capitalize()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_is_active(obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: user type
|
||||||
|
"""
|
||||||
|
if profile := (obj.guardian_profile.all().first() or obj.junior_profile.all().first()):
|
||||||
|
return "Active" if profile.is_active else "Inactive"
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_date_joined(obj):
|
||||||
|
"""
|
||||||
|
:param obj: user obj
|
||||||
|
:return: formatted date
|
||||||
|
"""
|
||||||
|
date = obj.date_joined.strftime("%d %b %Y")
|
||||||
|
return date
|
||||||
@@ -0,0 +1,370 @@
|
|||||||
|
"""
|
||||||
|
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 notifications.constants import NEW_ARTICLE_PUBLISHED
|
||||||
|
from notifications.utils import send_notification_multiple_user
|
||||||
|
from web_admin.models import Article, ArticleCard, SurveyOption, ArticleSurvey, DefaultArticleCardImage
|
||||||
|
from web_admin.utils import pop_id, get_image_url
|
||||||
|
from junior.models import JuniorArticlePoints, JuniorArticle
|
||||||
|
|
||||||
|
USER = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleCardSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Article Card serializer
|
||||||
|
"""
|
||||||
|
id = serializers.IntegerField(required=False)
|
||||||
|
image_name = serializers.CharField(required=False)
|
||||||
|
image_url = serializers.CharField(required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = ArticleCard
|
||||||
|
fields = ('id', 'title', 'description', 'image_name', 'image_url')
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
validated_data['image_url'] = get_image_url(validated_data)
|
||||||
|
article = Article.objects.all().first()
|
||||||
|
article_card = ArticleCard.objects.create(article=article, **validated_data)
|
||||||
|
return article_card
|
||||||
|
|
||||||
|
|
||||||
|
class SurveyOptionSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
survey option serializer
|
||||||
|
"""
|
||||||
|
id = serializers.IntegerField(required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = SurveyOption
|
||||||
|
fields = ('id', 'option', 'is_answer')
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleSurveySerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
article survey serializer
|
||||||
|
"""
|
||||||
|
id = serializers.IntegerField(required=False)
|
||||||
|
options = SurveyOptionSerializer(many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = ArticleSurvey
|
||||||
|
fields = ('id', 'question', 'options')
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
serializer for article API
|
||||||
|
"""
|
||||||
|
article_cards = ArticleCardSerializer(many=True)
|
||||||
|
article_survey = ArticleSurveySerializer(many=True)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = Article
|
||||||
|
fields = ('id', 'title', 'description', 'is_published', '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 not 0 < len(article_cards) <= int(MAX_ARTICLE_CARD):
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2039']})
|
||||||
|
if not int(MIN_ARTICLE_SURVEY) <= len(article_survey) <= int(MAX_ARTICLE_SURVEY):
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2040']})
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
"""
|
||||||
|
to create article.
|
||||||
|
ID in post data dict is for update api.
|
||||||
|
:return: article object
|
||||||
|
"""
|
||||||
|
article_cards = validated_data.pop('article_cards')
|
||||||
|
article_survey = validated_data.pop('article_survey')
|
||||||
|
|
||||||
|
article = Article.objects.create(**validated_data)
|
||||||
|
|
||||||
|
for card in article_cards:
|
||||||
|
card = pop_id(card)
|
||||||
|
card['image_url'] = get_image_url(card)
|
||||||
|
ArticleCard.objects.create(article=article, **card)
|
||||||
|
|
||||||
|
for survey in article_survey:
|
||||||
|
survey = pop_id(survey)
|
||||||
|
options = survey.pop('options')
|
||||||
|
survey_obj = ArticleSurvey.objects.create(article=article, points=ARTICLE_SURVEY_POINTS, **survey)
|
||||||
|
for option in options:
|
||||||
|
option = pop_id(option)
|
||||||
|
SurveyOption.objects.create(survey=survey_obj, **option)
|
||||||
|
|
||||||
|
# All juniors will receive notification when admin add any new financial learnings/article
|
||||||
|
send_notification_multiple_user.delay(NEW_ARTICLE_PUBLISHED, None, None, {})
|
||||||
|
|
||||||
|
return article
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
"""
|
||||||
|
to update article and related table
|
||||||
|
:param validated_data:
|
||||||
|
:param instance: article object,
|
||||||
|
:return: article object
|
||||||
|
"""
|
||||||
|
article_cards = validated_data.pop('article_cards')
|
||||||
|
article_survey = validated_data.pop('article_survey')
|
||||||
|
instance.title = validated_data.get('title', instance.title)
|
||||||
|
instance.description = validated_data.get('description', instance.description)
|
||||||
|
instance.save()
|
||||||
|
prev_card = list(ArticleCard.objects.filter(article=instance).values_list('id', flat=True))
|
||||||
|
# Update or create cards
|
||||||
|
for card_data in article_cards:
|
||||||
|
card_id = card_data.get('id', None)
|
||||||
|
if card_id:
|
||||||
|
prev_card.remove(card_id)
|
||||||
|
card = ArticleCard.objects.get(id=card_id, article=instance)
|
||||||
|
card.title = card_data.get('title', card.title)
|
||||||
|
card.description = card_data.get('description', card.description)
|
||||||
|
card.image_url = get_image_url(card_data)
|
||||||
|
card.save()
|
||||||
|
else:
|
||||||
|
card_data = pop_id(card_data)
|
||||||
|
card_data['image_url'] = get_image_url(card_data)
|
||||||
|
ArticleCard.objects.create(article=instance, **card_data)
|
||||||
|
ArticleCard.objects.filter(id__in=prev_card, article=instance).delete()
|
||||||
|
|
||||||
|
prev_survey = list(ArticleSurvey.objects.filter(article=instance).values_list('id', flat=True))
|
||||||
|
# Update or create survey sections
|
||||||
|
for survey_data in article_survey:
|
||||||
|
survey_id = survey_data.get('id', None)
|
||||||
|
options_data = survey_data.pop('options')
|
||||||
|
if survey_id:
|
||||||
|
prev_survey.remove(survey_id)
|
||||||
|
survey = ArticleSurvey.objects.get(id=survey_id, article=instance)
|
||||||
|
survey.question = survey_data.get('question', survey.question)
|
||||||
|
survey.save()
|
||||||
|
else:
|
||||||
|
survey_data = pop_id(survey_data)
|
||||||
|
survey = ArticleSurvey.objects.create(article=instance, points=ARTICLE_SURVEY_POINTS, **survey_data)
|
||||||
|
|
||||||
|
# Update or create survey options
|
||||||
|
for option_data in options_data:
|
||||||
|
option_id = option_data.get('id', None)
|
||||||
|
if option_id:
|
||||||
|
option = SurveyOption.objects.get(id=option_id, survey=survey)
|
||||||
|
option.option = option_data.get('option', option.option)
|
||||||
|
option.is_answer = option_data.get('is_answer', option.is_answer)
|
||||||
|
option.save()
|
||||||
|
else:
|
||||||
|
option_data = pop_id(option_data)
|
||||||
|
SurveyOption.objects.create(survey=survey, **option_data)
|
||||||
|
ArticleSurvey.objects.filter(id__in=prev_survey, article=instance).delete()
|
||||||
|
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleStatusChangeSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Article status change serializer
|
||||||
|
"""
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = Article
|
||||||
|
fields = ('is_published', )
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
"""
|
||||||
|
:param instance: article object
|
||||||
|
:param validated_data:
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
instance.is_published = validated_data['is_published']
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultArticleCardImageSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Article Card serializer
|
||||||
|
"""
|
||||||
|
image = serializers.FileField(required=False)
|
||||||
|
image_url = serializers.URLField(required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = DefaultArticleCardImage
|
||||||
|
fields = ('image_name', 'image', 'image_url')
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
"""
|
||||||
|
to validate data
|
||||||
|
:return: validated data
|
||||||
|
"""
|
||||||
|
if 'image' not in attrs and attrs.get('image') is None:
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2061']})
|
||||||
|
image = attrs.get('image')
|
||||||
|
if image and image.size == NUMBER['zero']:
|
||||||
|
raise serializers.ValidationError(ERROR_CODE['2035'])
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
"""
|
||||||
|
to create and upload image
|
||||||
|
:return: card_image object
|
||||||
|
"""
|
||||||
|
validated_data['image_url'] = get_image_url(validated_data)
|
||||||
|
|
||||||
|
card_image = DefaultArticleCardImage.objects.create(**validated_data)
|
||||||
|
return card_image
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleListSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
serializer for article API
|
||||||
|
"""
|
||||||
|
image = serializers.SerializerMethodField('get_image')
|
||||||
|
total_points = serializers.SerializerMethodField('get_total_points')
|
||||||
|
is_completed = serializers.SerializerMethodField('get_is_completed')
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = Article
|
||||||
|
fields = ('id', 'title', 'description', 'image', 'total_points', 'is_completed')
|
||||||
|
|
||||||
|
def get_image(self, obj):
|
||||||
|
"""article image"""
|
||||||
|
if obj.article_cards.first():
|
||||||
|
return obj.article_cards.first().image_url
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_total_points(self, obj):
|
||||||
|
"""total points of article"""
|
||||||
|
return obj.article_survey.all().count() * NUMBER['five']
|
||||||
|
|
||||||
|
def get_is_completed(self, obj):
|
||||||
|
"""complete all question"""
|
||||||
|
context_data = self.context.get('user')
|
||||||
|
junior_article = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
|
||||||
|
if junior_article:
|
||||||
|
return junior_article.is_completed
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleQuestionSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
article survey serializer
|
||||||
|
"""
|
||||||
|
id = serializers.IntegerField(required=False)
|
||||||
|
options = SurveyOptionSerializer(many=True)
|
||||||
|
is_attempt = serializers.SerializerMethodField('get_is_attempt')
|
||||||
|
correct_answer = serializers.SerializerMethodField('get_correct_answer')
|
||||||
|
attempted_answer = serializers.SerializerMethodField('get_attempted_answer')
|
||||||
|
|
||||||
|
def get_is_attempt(self, obj):
|
||||||
|
"""attempt question or not"""
|
||||||
|
context_data = self.context.get('user')
|
||||||
|
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data, question=obj).last()
|
||||||
|
if junior_article_obj:
|
||||||
|
return junior_article_obj.is_attempt
|
||||||
|
return False
|
||||||
|
|
||||||
|
def get_correct_answer(self, obj):
|
||||||
|
"""attempt question or not"""
|
||||||
|
ans_obj = SurveyOption.objects.filter(survey=obj, is_answer=True).last()
|
||||||
|
if ans_obj:
|
||||||
|
return ans_obj.id
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_attempted_answer(self, obj):
|
||||||
|
"""attempt question or not"""
|
||||||
|
context_data = self.context.get('user')
|
||||||
|
junior_article_obj = JuniorArticlePoints.objects.filter(junior__auth=context_data,
|
||||||
|
question=obj).last()
|
||||||
|
if junior_article_obj and junior_article_obj.submitted_answer:
|
||||||
|
return junior_article_obj.submitted_answer.id
|
||||||
|
return None
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = ArticleSurvey
|
||||||
|
fields = ('id', 'question', 'options', 'points', 'is_attempt', 'correct_answer', 'attempted_answer')
|
||||||
|
|
||||||
|
|
||||||
|
class StartAssessmentSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
serializer for article API
|
||||||
|
"""
|
||||||
|
article_survey = ArticleQuestionSerializer(many=True)
|
||||||
|
current_page = serializers.SerializerMethodField('get_current_page')
|
||||||
|
|
||||||
|
def get_current_page(self, obj):
|
||||||
|
"""current page"""
|
||||||
|
context_data = self.context.get('user')
|
||||||
|
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj).last()
|
||||||
|
total_count = obj.article_survey.all().count()
|
||||||
|
if data:
|
||||||
|
return data.current_que_page if data.current_que_page < total_count else data.current_que_page - 1
|
||||||
|
return NUMBER['zero']
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = Article
|
||||||
|
fields = ('article_survey', 'current_page')
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleCardlistSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Article Card serializer
|
||||||
|
"""
|
||||||
|
id = serializers.IntegerField(required=False)
|
||||||
|
image_name = serializers.CharField(required=False)
|
||||||
|
image_url = serializers.CharField(required=False)
|
||||||
|
current_page = serializers.SerializerMethodField('get_current_page')
|
||||||
|
|
||||||
|
def get_current_page(self, obj):
|
||||||
|
"""current page"""
|
||||||
|
context_data = self.context.get('user')
|
||||||
|
data = JuniorArticle.objects.filter(junior__auth=context_data, article=obj.article).last()
|
||||||
|
total_count = self.context.get('card_count')
|
||||||
|
if data:
|
||||||
|
return data.current_card_page if data.current_card_page < total_count else data.current_card_page - 1
|
||||||
|
return NUMBER['zero']
|
||||||
|
|
||||||
|
class Meta(object):
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = ArticleCard
|
||||||
|
fields = ('id', 'title', 'description', 'image_name', 'image_url', 'current_page')
|
||||||
@@ -0,0 +1,136 @@
|
|||||||
|
"""
|
||||||
|
web_admin auth serializers file
|
||||||
|
"""
|
||||||
|
# python imports
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# django imports
|
||||||
|
from rest_framework import serializers
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from account.models import UserEmailOtp
|
||||||
|
from base.constants import USER_TYPE
|
||||||
|
from base.messages import ERROR_CODE
|
||||||
|
from guardian.tasks import generate_otp
|
||||||
|
from base.tasks import send_email
|
||||||
|
|
||||||
|
USER = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class AdminOTPSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
admin forgot password serializer
|
||||||
|
"""
|
||||||
|
email = serializers.EmailField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = USER
|
||||||
|
fields = ('email',)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
""" used to validate the incoming data """
|
||||||
|
user = USER.objects.filter(email=attrs.get('email')).first()
|
||||||
|
if not user:
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2004']})
|
||||||
|
elif not user.is_superuser:
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2063']})
|
||||||
|
attrs.update({'user': user})
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def create(self, validated_data):
|
||||||
|
"""
|
||||||
|
to send otp
|
||||||
|
:return: user_data
|
||||||
|
"""
|
||||||
|
email = validated_data['email']
|
||||||
|
verification_code = generate_otp()
|
||||||
|
template = 'email_reset_verification.email'
|
||||||
|
# Send the verification code to the user's email
|
||||||
|
data = {
|
||||||
|
"verification_code": verification_code
|
||||||
|
}
|
||||||
|
send_email.delay([email], template, data)
|
||||||
|
|
||||||
|
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||||
|
user_data, created = UserEmailOtp.objects.update_or_create(email=email,
|
||||||
|
defaults={
|
||||||
|
"otp": verification_code,
|
||||||
|
"expired_at": expiry,
|
||||||
|
"user_type": dict(USER_TYPE).get('3'),
|
||||||
|
})
|
||||||
|
|
||||||
|
return user_data
|
||||||
|
|
||||||
|
|
||||||
|
class AdminVerifyOTPSerializer(serializers.Serializer):
|
||||||
|
"""
|
||||||
|
admin verify otp serializer
|
||||||
|
"""
|
||||||
|
email = serializers.EmailField()
|
||||||
|
otp = serializers.CharField(max_length=6, min_length=6)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
""" meta class """
|
||||||
|
fields = ('email', 'otp',)
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
"""
|
||||||
|
to validate data
|
||||||
|
:return: validated data
|
||||||
|
"""
|
||||||
|
email = attrs.get('email')
|
||||||
|
otp = attrs.get('otp')
|
||||||
|
|
||||||
|
# fetch email otp object of the user
|
||||||
|
user_otp_details = UserEmailOtp.objects.filter(email=email, otp=otp).last()
|
||||||
|
if not user_otp_details:
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2008']})
|
||||||
|
if user_otp_details.user_type != dict(USER_TYPE).get('3'):
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2008']})
|
||||||
|
if user_otp_details.expired_at.replace(tzinfo=None) < datetime.utcnow():
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2029']})
|
||||||
|
user_otp_details.is_verified = True
|
||||||
|
user_otp_details.save()
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
|
||||||
|
class AdminCreatePasswordSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
admin create new password serializer
|
||||||
|
"""
|
||||||
|
email = serializers.EmailField()
|
||||||
|
new_password = serializers.CharField()
|
||||||
|
confirm_password = serializers.CharField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = USER
|
||||||
|
fields = ('email', 'new_password', 'confirm_password')
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
"""
|
||||||
|
to validate data
|
||||||
|
:return: validated data
|
||||||
|
"""
|
||||||
|
email = attrs.get('email')
|
||||||
|
new_password = attrs.get('new_password')
|
||||||
|
confirm_password = attrs.get('confirm_password')
|
||||||
|
|
||||||
|
# matching password
|
||||||
|
if new_password != confirm_password:
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2065']})
|
||||||
|
|
||||||
|
user_otp_details = UserEmailOtp.objects.filter(email=email, is_verified=True).last()
|
||||||
|
if not user_otp_details:
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2064']})
|
||||||
|
if user_otp_details.user_type != dict(USER_TYPE).get('3'):
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2063']})
|
||||||
|
user_otp_details.delete()
|
||||||
|
return attrs
|
||||||
@@ -0,0 +1,286 @@
|
|||||||
|
"""
|
||||||
|
web_admin user_management serializers file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from rest_framework import serializers
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from account.utils import get_user_full_name
|
||||||
|
from base.constants import USER_TYPE, GUARDIAN, JUNIOR
|
||||||
|
# local imports
|
||||||
|
from base.messages import ERROR_CODE, SUCCESS_CODE
|
||||||
|
from guardian.models import Guardian
|
||||||
|
from junior.models import Junior
|
||||||
|
|
||||||
|
USER = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class UserManagementListSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
user management serializer
|
||||||
|
"""
|
||||||
|
name = serializers.SerializerMethodField()
|
||||||
|
country_code = serializers.SerializerMethodField()
|
||||||
|
phone = serializers.SerializerMethodField()
|
||||||
|
user_type = serializers.SerializerMethodField()
|
||||||
|
is_active = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = USER
|
||||||
|
fields = ('id', 'name', 'email', 'country_code', 'phone', 'user_type', 'is_active')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: full name
|
||||||
|
"""
|
||||||
|
return get_user_full_name(obj)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_country_code(obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: user phone number
|
||||||
|
"""
|
||||||
|
if profile := obj.guardian_profile.all().first():
|
||||||
|
return profile.country_code if profile.country_code else None
|
||||||
|
elif profile := obj.junior_profile.all().first():
|
||||||
|
return profile.country_code if profile.country_code else None
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_phone(obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: user phone number
|
||||||
|
"""
|
||||||
|
if profile := obj.guardian_profile.all().first():
|
||||||
|
return profile.phone if profile.phone else None
|
||||||
|
elif profile := obj.junior_profile.all().first():
|
||||||
|
return profile.phone if profile.phone else None
|
||||||
|
|
||||||
|
@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')
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_is_active(obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: user type
|
||||||
|
"""
|
||||||
|
if profile := obj.guardian_profile.all().first():
|
||||||
|
return profile.is_active
|
||||||
|
elif profile := obj.junior_profile.all().first():
|
||||||
|
return profile.is_active
|
||||||
|
|
||||||
|
|
||||||
|
class GuardianSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
guardian serializer
|
||||||
|
"""
|
||||||
|
name = serializers.SerializerMethodField()
|
||||||
|
first_name = serializers.SerializerMethodField()
|
||||||
|
last_name = serializers.SerializerMethodField()
|
||||||
|
username = serializers.SerializerMethodField()
|
||||||
|
email = serializers.EmailField(required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = Guardian
|
||||||
|
fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone',
|
||||||
|
'is_active', 'country_name', 'image', 'email', 'is_deleted')
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
"""
|
||||||
|
to validate request data
|
||||||
|
:return: validated attrs
|
||||||
|
"""
|
||||||
|
email = attrs.get('email')
|
||||||
|
phone = attrs.get('phone')
|
||||||
|
if USER.objects.filter(email=email).exclude(id=self.context.get('user_id')).exists():
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2003']})
|
||||||
|
if Guardian.objects.filter(phone=phone).exclude(user__id=self.context.get('user_id')).exists():
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2012']})
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
"""
|
||||||
|
to update user and its related profile
|
||||||
|
:param instance: user's guardian object
|
||||||
|
:param validated_data:
|
||||||
|
:return: guardian object
|
||||||
|
"""
|
||||||
|
instance.user.email = self.validated_data.get('email', instance.user.email)
|
||||||
|
instance.user.username = self.validated_data.get('email', instance.user.username)
|
||||||
|
instance.user.save()
|
||||||
|
instance.country_code = validated_data.get('country_code', instance.country_code)
|
||||||
|
instance.phone = validated_data.get('phone', instance.phone)
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: guardian object
|
||||||
|
:return: full name
|
||||||
|
"""
|
||||||
|
return get_user_full_name(obj.user)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_first_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: guardian object
|
||||||
|
:return: first name
|
||||||
|
"""
|
||||||
|
return obj.user.first_name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_last_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: guardian object
|
||||||
|
:return: last name
|
||||||
|
"""
|
||||||
|
return obj.user.last_name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_username(obj):
|
||||||
|
"""
|
||||||
|
:param obj: guardian object
|
||||||
|
:return: email
|
||||||
|
"""
|
||||||
|
return obj.user.username
|
||||||
|
|
||||||
|
|
||||||
|
class JuniorSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
junior serializer
|
||||||
|
"""
|
||||||
|
name = serializers.SerializerMethodField()
|
||||||
|
first_name = serializers.SerializerMethodField()
|
||||||
|
last_name = serializers.SerializerMethodField()
|
||||||
|
username = serializers.SerializerMethodField()
|
||||||
|
email = serializers.EmailField(required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = Junior
|
||||||
|
fields = ('id', 'name', 'first_name', 'last_name', 'username', 'dob', 'gender', 'country_code', 'phone',
|
||||||
|
'is_active', 'country_name', 'image', 'email', 'is_deleted')
|
||||||
|
|
||||||
|
def validate(self, attrs):
|
||||||
|
"""
|
||||||
|
to validate request data
|
||||||
|
:return: validated attrs
|
||||||
|
"""
|
||||||
|
email = attrs.get('email')
|
||||||
|
phone = attrs.get('phone')
|
||||||
|
if email and USER.objects.filter(email=email).exclude(id=self.context.get('user_id')).exists():
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2003']})
|
||||||
|
if phone and Junior.objects.filter(phone=phone).exclude(auth__id=self.context.get('user_id')).exists():
|
||||||
|
raise serializers.ValidationError({'details': ERROR_CODE['2012']})
|
||||||
|
return attrs
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
"""
|
||||||
|
:param instance: user's junior object
|
||||||
|
:param validated_data: validated data
|
||||||
|
:return: instance
|
||||||
|
"""
|
||||||
|
instance.auth.email = self.validated_data.get('email', instance.auth.email)
|
||||||
|
instance.auth.username = self.validated_data.get('email', instance.auth.username)
|
||||||
|
instance.auth.save(update_fields=['email', 'username'])
|
||||||
|
instance.country_code = validated_data.get('country_code', instance.country_code)
|
||||||
|
instance.phone = validated_data.get('phone', instance.phone)
|
||||||
|
instance.save(update_fields=['country_code', 'phone'])
|
||||||
|
return instance
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: junior object
|
||||||
|
:return: full name
|
||||||
|
"""
|
||||||
|
return get_user_full_name(obj.auth)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_first_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: junior object
|
||||||
|
:return: first name
|
||||||
|
"""
|
||||||
|
return obj.auth.first_name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_last_name(obj):
|
||||||
|
"""
|
||||||
|
:param obj: junior object
|
||||||
|
:return: last name
|
||||||
|
"""
|
||||||
|
return obj.auth.last_name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_username(obj):
|
||||||
|
"""
|
||||||
|
:param obj: junior object
|
||||||
|
:return: email
|
||||||
|
"""
|
||||||
|
return obj.auth.username
|
||||||
|
|
||||||
|
|
||||||
|
class UserManagementDetailSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
user management details serializer
|
||||||
|
"""
|
||||||
|
user_type = serializers.SerializerMethodField()
|
||||||
|
guardian_profile = GuardianSerializer(many=True)
|
||||||
|
junior_profile = JuniorSerializer(many=True)
|
||||||
|
associated_users = serializers.SerializerMethodField()
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
"""
|
||||||
|
meta class
|
||||||
|
"""
|
||||||
|
model = USER
|
||||||
|
fields = ('id', 'user_type', 'email', 'guardian_profile', 'junior_profile', 'associated_users')
|
||||||
|
|
||||||
|
def get_user_type(self, obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: user type
|
||||||
|
"""
|
||||||
|
return GUARDIAN if self.context['user_type'] == GUARDIAN else JUNIOR
|
||||||
|
|
||||||
|
def get_associated_users(self, obj):
|
||||||
|
"""
|
||||||
|
:param obj: user object
|
||||||
|
:return: associated user
|
||||||
|
"""
|
||||||
|
if self.context['user_type'] == GUARDIAN:
|
||||||
|
profile = obj.guardian_profile.all().only('user_id', 'guardian_code').first()
|
||||||
|
if profile.guardian_code:
|
||||||
|
junior = Junior.objects.filter(guardian_code__contains=[profile.guardian_code],
|
||||||
|
is_verified=True).select_related('auth')
|
||||||
|
serializer = JuniorSerializer(junior, many=True)
|
||||||
|
return serializer.data
|
||||||
|
elif self.context['user_type'] == JUNIOR:
|
||||||
|
profile = obj.junior_profile.all().only('auth_id', 'guardian_code').first()
|
||||||
|
if profile.guardian_code:
|
||||||
|
guardian = Guardian.objects.filter(guardian_code__in=profile.guardian_code,
|
||||||
|
is_verified=True).select_related('user')
|
||||||
|
serializer = GuardianSerializer(guardian, many=True)
|
||||||
|
return serializer.data
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
"""
|
||||||
|
web admin test analytics file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from django.urls import reverse
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from web_admin.tests.test_set_up import AnalyticsSetUp
|
||||||
|
|
||||||
|
|
||||||
|
class AnalyticsViewSetTestCase(AnalyticsSetUp):
|
||||||
|
"""
|
||||||
|
test cases for analytics, users count, new sign-ups,
|
||||||
|
assign tasks report, junior leaderboard, export excel
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
test data set up
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AnalyticsViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
def test_total_sign_up_count(self):
|
||||||
|
"""
|
||||||
|
test total sign up count
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-users-count')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming four users exists in the database
|
||||||
|
self.assertEqual(response.data['data']['total_users'], 4)
|
||||||
|
# Assuming two guardians exists in the database
|
||||||
|
self.assertEqual(response.data['data']['total_guardians'], 2)
|
||||||
|
# Assuming two juniors exists in the database
|
||||||
|
self.assertEqual(response.data['data']['total_juniors'], 2)
|
||||||
|
|
||||||
|
def test_new_user_sign_ups(self):
|
||||||
|
"""
|
||||||
|
test new user sign-ups
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-new-signups')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming four users exists in the database
|
||||||
|
self.assertEqual(response.data['data'][0]['signups'], 4)
|
||||||
|
|
||||||
|
def test_new_user_sign_ups_between_given_dates(self):
|
||||||
|
"""
|
||||||
|
test new user sign-ups
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-new-signups')
|
||||||
|
query_params = {
|
||||||
|
'start_date': '2023-09-12',
|
||||||
|
'end_date': '2023-09-13'
|
||||||
|
}
|
||||||
|
response = self.client.get(url, query_params)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming four users exists in the database
|
||||||
|
self.assertEqual(response.data['data'][0]['signups'], 4)
|
||||||
|
|
||||||
|
def test_assign_tasks_report(self):
|
||||||
|
"""
|
||||||
|
test assign tasks report
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-assign-tasks')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming two completed tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_completed'], 2)
|
||||||
|
# Assuming two pending tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_pending'], 2)
|
||||||
|
# Assuming two in progress tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_in_progress'], 2)
|
||||||
|
# Assuming two requested tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_requested'], 2)
|
||||||
|
# Assuming two rejected tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_rejected'], 2)
|
||||||
|
# Assuming two expired tasks exists in the database
|
||||||
|
self.assertEqual(response.data['data']['task_expired'], 2)
|
||||||
|
|
||||||
|
def test_junior_leaderboard(self):
|
||||||
|
"""
|
||||||
|
test junior leaderboard
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-junior-leaderboard')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_export_excel(self):
|
||||||
|
"""
|
||||||
|
test export excel
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(self.admin_user)
|
||||||
|
url = reverse('web_admin:analytics-export-excel')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertURLEqual(response.data['data'], self.export_excel_url)
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
"""
|
||||||
|
web_admin test article file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from web_admin.models import Article, ArticleCard, ArticleSurvey, DefaultArticleCardImage
|
||||||
|
from web_admin.tests.test_set_up import ArticleTestSetUp
|
||||||
|
|
||||||
|
# user model
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleViewSetTestCase(ArticleTestSetUp):
|
||||||
|
"""
|
||||||
|
test cases for article create, update, list, retrieve, delete
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
inherit data here
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(ArticleViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
|
||||||
|
def test_article_create_with_default_card_image(self):
|
||||||
|
"""
|
||||||
|
test article create with default card_image
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_list_url)
|
||||||
|
response = self.client.post(url, self.article_data_with_default_card_image, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Check that a new article was created
|
||||||
|
self.assertEqual(Article.objects.count(), 2)
|
||||||
|
|
||||||
|
def test_article_create_with_base64_card_image(self):
|
||||||
|
"""
|
||||||
|
test article create with base64 card image
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = reverse(self.article_list_url)
|
||||||
|
response = self.client.post(url, self.article_data_with_base64_card_image, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Check that a new article was created
|
||||||
|
self.assertEqual(Article.objects.count(), 2)
|
||||||
|
|
||||||
|
def test_article_update(self):
|
||||||
|
"""
|
||||||
|
test article update
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = reverse(self.article_detail_url, kwargs={'pk': self.article.id})
|
||||||
|
response = self.client.put(url, self.article_update_data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.article.refresh_from_db()
|
||||||
|
self.assertEqual(self.article.title, self.article_update_data['title'])
|
||||||
|
self.assertEqual(self.article.article_cards.count(), 1)
|
||||||
|
self.assertEqual(self.article.article_survey.count(), 6)
|
||||||
|
self.assertEqual(self.article.article_survey.first().options.count(), 3)
|
||||||
|
|
||||||
|
def test_articles_list(self):
|
||||||
|
"""
|
||||||
|
test articles list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_list_url)
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one article exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
def test_article_retrieve(self):
|
||||||
|
"""
|
||||||
|
test article retrieve
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_detail_url, kwargs={'pk': self.article.id})
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
|
||||||
|
def test_article_delete(self):
|
||||||
|
"""
|
||||||
|
test article delete
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_detail_url, kwargs={'pk': self.article.id})
|
||||||
|
response = self.client.delete(url)
|
||||||
|
self.article.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.article.is_deleted, True)
|
||||||
|
|
||||||
|
def test_article_create_with_invalid_data(self):
|
||||||
|
"""
|
||||||
|
test article create with invalid data
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse(self.article_list_url)
|
||||||
|
# Missing article_cards
|
||||||
|
invalid_data = {
|
||||||
|
"title": "Invalid Article",
|
||||||
|
"article_survey": [{"question": "Invalid Survey Question"}]
|
||||||
|
}
|
||||||
|
response = self.client.post(url, invalid_data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_article_status_change(self):
|
||||||
|
"""
|
||||||
|
test article status change (publish/un-publish)
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-status-change', kwargs={'pk': self.article.id})
|
||||||
|
data = {
|
||||||
|
"is_published": False
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json')
|
||||||
|
self.article.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.article.is_published, False)
|
||||||
|
|
||||||
|
def test_article_card_remove(self):
|
||||||
|
"""
|
||||||
|
test article card remove
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-remove-card', kwargs={'pk': self.article_card.id})
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(ArticleCard.objects.count(), 0)
|
||||||
|
|
||||||
|
def test_article_survey_remove(self):
|
||||||
|
"""
|
||||||
|
test article survey remove
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-remove-survey', kwargs={'pk': self.article_survey.id})
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(ArticleSurvey.objects.count(), 0)
|
||||||
|
|
||||||
|
def test_article_card_create_with_default_card_image(self):
|
||||||
|
"""
|
||||||
|
test article card create with default card_image
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-test-add-card')
|
||||||
|
response = self.client.post(url, self.article_card_data_with_default_card_image, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Check that a new article card was created
|
||||||
|
self.assertEqual(ArticleCard.objects.count(), 2)
|
||||||
|
|
||||||
|
def test_article_cards_list(self):
|
||||||
|
"""
|
||||||
|
test article cards list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-test-list-card')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one article exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class DefaultArticleCardImagesViewSetTestCase(APITestCase):
|
||||||
|
"""
|
||||||
|
test case for default article card image
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
data setup
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client = APIClient()
|
||||||
|
self.admin_user = User.objects.create_user(username='[email protected]', email='[email protected]',
|
||||||
|
password='admin@1234', is_staff=True, is_superuser=True)
|
||||||
|
self.default_image = DefaultArticleCardImage.objects.create(
|
||||||
|
image_name="card1.jpg",
|
||||||
|
image_url="https://example.com/updated_card1.jpg")
|
||||||
|
|
||||||
|
def test_default_article_card_image_list(self):
|
||||||
|
"""
|
||||||
|
test default article card image list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = reverse('web_admin:default-card-images-list')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one default article card image exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleListViewSetTestCase(ArticleTestSetUp):
|
||||||
|
"""
|
||||||
|
test cases for article list for junior
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
data setup
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(ArticleListViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.client.force_authenticate(user=self.user)
|
||||||
|
|
||||||
|
def test_article_list(self):
|
||||||
|
"""
|
||||||
|
test article list
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-list-list')
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one article exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleCardListViewSetTestCase(ArticleTestSetUp):
|
||||||
|
"""
|
||||||
|
test cases for article card list for junior
|
||||||
|
"""
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
data setup
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(ArticleCardListViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.client.force_authenticate(user=self.user)
|
||||||
|
|
||||||
|
def test_article_cards_list(self):
|
||||||
|
"""
|
||||||
|
test article cards list for junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
url = reverse('web_admin:article-card-list-list')
|
||||||
|
query_params = {
|
||||||
|
'article_id': self.article.id,
|
||||||
|
}
|
||||||
|
response = self.client.get(url, query_params)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming only one article exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
# Add more test cases for edge cases, permissions, etc.
|
||||||
@@ -0,0 +1,160 @@
|
|||||||
|
"""
|
||||||
|
web admin test auth file
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
from django.utils import timezone
|
||||||
|
from django.urls import reverse
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
|
||||||
|
from rest_framework.test import APITestCase, APIClient
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
from account.models import UserEmailOtp
|
||||||
|
from base.constants import USER_TYPE
|
||||||
|
from guardian.tasks import generate_otp
|
||||||
|
from web_admin.tests.test_set_up import BaseSetUp
|
||||||
|
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class AdminOTPTestCase(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test case to send otp to admin email
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
inherit data here
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AdminOTPTestCase, self).setUp()
|
||||||
|
self.url = reverse('web_admin:admin-otp')
|
||||||
|
|
||||||
|
def test_admin_otp_for_valid_email(self):
|
||||||
|
"""
|
||||||
|
test admin otp for valid email
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email
|
||||||
|
}
|
||||||
|
response = self.client.post(self.url, data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(UserEmailOtp.objects.count(), 1)
|
||||||
|
|
||||||
|
def test_admin_otp_for_invalid_email(self):
|
||||||
|
"""
|
||||||
|
test admin otp for invalid email
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'email': '[email protected]'
|
||||||
|
}
|
||||||
|
response = self.client.post(self.url, data, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminVerifyOTPTestCase(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test case to verify otp for admin email
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
inherit data here
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AdminVerifyOTPTestCase, self).setUp()
|
||||||
|
self.verification_code = generate_otp()
|
||||||
|
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||||
|
self.user_email_otp = UserEmailOtp.objects.create(email=self.admin_email,
|
||||||
|
otp=self.verification_code,
|
||||||
|
expired_at=expiry,
|
||||||
|
user_type=dict(USER_TYPE).get('3'),
|
||||||
|
)
|
||||||
|
self.url = reverse('web_admin:admin-verify-otp')
|
||||||
|
|
||||||
|
def test_admin_verify_otp_with_valid_otp(self):
|
||||||
|
"""
|
||||||
|
test admin verify otp with valid otp
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email,
|
||||||
|
"otp": self.verification_code
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(self.url, data)
|
||||||
|
self.user_email_otp.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.user_email_otp.is_verified, True)
|
||||||
|
|
||||||
|
def test_admin_verify_otp_with_invalid_otp(self):
|
||||||
|
"""
|
||||||
|
test admin verify otp with invalid otp
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email,
|
||||||
|
"otp": generate_otp()
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(self.url, data)
|
||||||
|
self.user_email_otp.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(self.user_email_otp.is_verified, False)
|
||||||
|
|
||||||
|
|
||||||
|
class AdminCreateNewPassword(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test case to create new password for admin email
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
inherit data here
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AdminCreateNewPassword, self).setUp()
|
||||||
|
self.verification_code = generate_otp()
|
||||||
|
expiry = timezone.now() + timezone.timedelta(days=1)
|
||||||
|
self.user_email_otp = UserEmailOtp.objects.create(email=self.admin_email,
|
||||||
|
otp=self.verification_code,
|
||||||
|
expired_at=expiry,
|
||||||
|
user_type=dict(USER_TYPE).get('3'),
|
||||||
|
)
|
||||||
|
self.url = reverse('web_admin:admin-create-password')
|
||||||
|
|
||||||
|
def test_admin_create_new_password_after_verification(self):
|
||||||
|
"""
|
||||||
|
test admin create new password
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
self.user_email_otp.is_verified = True
|
||||||
|
self.user_email_otp.save()
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email,
|
||||||
|
"new_password": "New@1234",
|
||||||
|
"confirm_password": "New@1234"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(self.url, data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(UserEmailOtp.objects.count(), 0)
|
||||||
|
|
||||||
|
def test_admin_create_new_password_without_verification(self):
|
||||||
|
"""
|
||||||
|
test admin create new password
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
data = {
|
||||||
|
'email': self.admin_email,
|
||||||
|
"new_password": "Some@1234",
|
||||||
|
"confirm_password": "Some@1234"
|
||||||
|
}
|
||||||
|
|
||||||
|
response = self.client.post(self.url, data)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
self.assertEqual(UserEmailOtp.objects.count(), 1)
|
||||||
@@ -0,0 +1,407 @@
|
|||||||
|
"""
|
||||||
|
web_admin test set up file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from django.test import TestCase
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from django.conf import settings
|
||||||
|
from rest_framework.test import APITestCase
|
||||||
|
from rest_framework.test import APIClient
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from guardian.models import Guardian, JuniorTask
|
||||||
|
from junior.models import Junior, JuniorPoints
|
||||||
|
from web_admin.models import Article, ArticleCard, ArticleSurvey, SurveyOption
|
||||||
|
|
||||||
|
# user model
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
# image data in base 64 string
|
||||||
|
base64_image = ("data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD/2wCEAAoHCBISEhIREhISEhgSERIREREYEhg"
|
||||||
|
"SGRERGRgZGhgYGBgcIS4lHB4rHxgYJjgmLC8xNTU1GiQ7QDszPy40NTEBDAwMEA8QGhISHjEhISE0NDQ0NDQ0N"
|
||||||
|
"DQ0NDQ0NDQxNDQ1NDQxNDQ0NDQ0NDQ0NDExNDE0MTQ0NDQ0NDQ0NDQ0P//AABEIALcBEwMBIgACEQEDEQH/xAAb"
|
||||||
|
"AAACAgMBAAAAAAAAAAAAAAAAAQIEAwUGB//EAEkQAAIBAgMEBgYGBgcIAwAAAAECAAMRBBIhBTFRYQYTIkFxkTJC"
|
||||||
|
"UoGhsRRDYnKCkhYjU8HR4QcVVGOD0vEkM5OissLT8ERVlP/EABgBAQEBAQEAAAAAAAAAAAAAAAABAgME/8QAIBEBA"
|
||||||
|
"QEBAAIDAQEBAQAAAAAAAAERAhIhMUFRAyJhE//aAAwDAQACEQMRAD8AuXiiJivAd4rxQgMGO8hHeQSvCRhKiUcheO"
|
||||||
|
"8CUJG8AZBK8d5G8cBxyMcCV4XkY7wHCKOA4RXheA4RQgOEUJQxHFCFOEUcBiSEhJQJRiREYgStCEIFGEIoBEYXheQ"
|
||||||
|
"AjkYAwJQihAccjHAcIoQHHIxwiQhFAGFSjkbwhEoCKOA4RQgOEUcAjvFKNPaSNdgjlL2VwV7YHeqneOBuL791pRfv"
|
||||||
|
"CVU2jRPrOn3qZP8A0ZpmTE0zuqU/A1FQ/lax+EDJGI8jWvlNuNtPORBhUowZGOBISUgJIQJQihAowvCK8AhFeEgcI"
|
||||||
|
"oQHCKOA7wvFCA4QhAcIrwgShIkxwHHIg90jSqBlzDcb/A2/dAyiCtfUd+4zFiKopo7nciM58ACZDZwtRpA7xSp38c"
|
||||||
|
"ohFmEIQHCKJmABJNgASSdwA3mBT2i5IWkpINS+YjetIemeRNwo5tfukGo303ACwG6wkab2DVn0NSxVTvSmPQXkbEs"
|
||||||
|
"RxYjulGviqjE5dBN5kWLRwR7jInDuOcqpiqi75Zw+0GYgWPlI2gKGU3ChTxXsHzGsyriqg3Van4m6z/rzTZCiGGsq"
|
||||||
|
"vhVJ3wmMa7Sqjvpt96nY/wDIyj4TMu1j61IeK1f+1k/7pWfBHumF8LUFgouWIVBxYmwHn38LwZG+w1ZaiB1DAEstm"
|
||||||
|
"ABBG/UEgzKJFECJTpqbimmUG1s7Elnc82YsffJCRlKEUIFGKEjeAQheK8gcIoxAJKRhAlHIR3gShISUBxyMcDG1TK"
|
||||||
|
"DmUkAbwM1x4DWURj1QqVcVKbNkJvc0mtcA99vHdNlNPtnZocGrTFnGrgfWKONt5kos47FCjUSo3oOOrc+yRcofi05"
|
||||||
|
"zAbYc5KTEj9eq3/uzmBB95kNt4rrKVDtf7tyHXnaynnpce+aYVwTfKFN9XF9bA917A+FpPJHc9KsRkwrjvqMtMeBN"
|
||||||
|
"2/5QZtaAsiDgqj4CcZtrH/SPoaA71zuPtk5B8m852GJxC01zMbDMqDmzEKAPeZZdGe8r4XFCo1TLqqMEze049Kx7w"
|
||||||
|
"NB5zT9INsFf9noHNUqHJcepfTfx+UnRqdXSTCYbtuq5Ge3YVvXYnS537o0b681m1cQMyUd+bt1B/dg6L+JhbwDS67"
|
||||||
|
"inTLOdES7NbuA1NpocPUNSobLnqVGD1ADpRX1VY8QthYbzc981BaqMahu3uEyJhXbcLTZ4fBqgzVDraZmxtNRYWm2"
|
||||||
|
"o1qbKJ3y2tGnTGttJixG0idFEqZKlQ3N98mLrLisffspvMwUsHUPaLHwmxw2BVe7WXGCqLnQCMNalcJV9qWcDS1NQ"
|
||||||
|
"nNkzU6Z4vudx4DsDmXlDE7UarUXD0Dq7ZS+8Io1Zj4AE+6blVVQqILKihUG85Rx4k7zzJk3TqYYkhIiSEjCUIQga6"
|
||||||
|
"8V4ooU4RQkDhFCBKO8jeECV4XkYQJXheRjgOSvIAxwJQvIwgcL0godXUqINASHX7p1+BvNUmthzM67pZhcypU4XRv"
|
||||||
|
"A7vj85ydGmSRyNjOPXrRbwDKlSm7g2QqzAbyVJNvMTNjsbUr1OsqNYJqiA2CcLc+cxV6eU3vpYHz3yuzm2gv47v5z"
|
||||||
|
"E6tRdwjqTmYM3BQcoP3mm42Vj6SEhKb1KjdyWyoL7hY7uJmowOw8RWsxXKvcXOUEclGpnR9VUwtJ3apSRKaliEo2"
|
||||||
|
"JPcAS2pJsNZ15lMU+kONqVGp4Yfq8xV6rXuyi/ZWw0uSL2ufRF9JtdkNTwyZFXmp3kt3knvM4vZuJZ6r16tRFLG7"
|
||||||
|
"FiMxOg7I3AWsL23Cb+njLgEeIuLXnWLG+frKlybgQXA8ZfwTh0Vh3j4ywEm1UqWDAlpKdtwmdUlbHY6nRQsxGm4c"
|
||||||
|
"4VOvXSmpZyBYTjdq7aeqSFuq/OV9qbVesddFvumDZeC+kVUp6hdXqMPVpr6R8ToBzYTn11vqOnPPjNro+i+C6uma"
|
||||||
|
"7DtVhlp/Zog7/AMTDyUcZuryJYdwCgABVG5VAsAOQFoXlkxyt26mDGDIXkgZUTvCLNFCNfETFeRkVK8LyMtYbChwT"
|
||||||
|
"1iLb1b3Y/hkFe8Ly+mzr31bQE+ja9pE4Rcpa7aWvuG+XE2KccylEHteY/hMbFR3N+Yfwg0oXgHS9u18DMtOhnNkLE"
|
||||||
|
"86Zt5i8isUInUqSDvBsfGK8Cd4XkLx3gTvC8xfSKanLULj7qhtPeRJtXo+q5P3iU+SkRpjBtKl1lKonFSR4jUTjMJ"
|
||||||
|
"SHWWPebTuhWTvQtySrTb4Egzkm2biDUY08PXK5mKE0z6N+zecf6y34WD6EHYM25QFVOLXOpm52fsdFIeoAzb1U7l5"
|
||||||
|
"24xbLwddNamFrOfV3Jl56jWdBRwVR99OrT+8EI8w9/hJ/P+d+eisF5xnTPaJd1wqXISz1Ld7n0VPgDf3jhOx26hwlC"
|
||||||
|
"pXcoQgAVbkFnOirqOP755W2Ie7OWOZmLseLHUmd2WajhqisGybt1wrfAmdZsSilU3rVFp2toWy33776i9vA9xM4s1G"
|
||||||
|
"be3mYUUJN8nWAd1jY+UsHrmGx+FT9XTqK9jlOTthT9phoPObLOoF7zy3AdY4ChkRF1NNcxPgb7p0CYmoqdltw3Humt"
|
||||||
|
"Tnqblb/AGnthKYOus4rH7Qaq12OncJDEVyxuxuZXYicuutevnnEGedh0fwfU0QzCz18rvxWn6i/HMfvDhNBsTALWqg"
|
||||||
|
"MLpTHWVeag9lPxNYeGbhOueoWJY7ybmXmfbn/AE6+k80eaYg0YaacmUNJBpiDRhoGa8JjvCBTvIVXyqW4SRMxV0zKR"
|
||||||
|
"5SB4Z6mW5eopOtkcoF8t58Zq9pF+su7FyR6R3kbhfnNwoBRXXdorD2WHcfGa3aydkNwNj4GZ6+G+flSSow3MR7zLaY"
|
||||||
|
"yoPrH/MZrc0yK8SpY2i42p+0b8xjOLf2jKC1JLrZpleXFVCfTbztMqVmZtWJ14zWo8vYLffhrBi4TC8jeF4ErwvI3h"
|
||||||
|
"eBRxnp+4TDeZsV6XuErmYt9tyegZ3ez79VT19RflOCvO+wY/Vp9wfKa5Ss+bnC8LTWdI9pjC4d6gIzt2KQPfUINjbv"
|
||||||
|
"AALHks0w4vpxtIV6woA3p4cnNro1e1m/KDl8S05g0U9kTLpvuTckknUkneSe8kxGFZcMqbsqg/dEvIk1qb78JfpV7/"
|
||||||
|
"wAJZXLufa1RpgHNuPfz8ZYaoRulQVIGrNMe2HFJrfjKz6C8z1nvLmwcKHqGq4ulCzkHc9U+gnPUFjyXnOPU9+nt46s"
|
||||||
|
"52t3s/CfR6S0zo72qVuTkdlPwrp4lpYzSu1Qkkk3JJJPEx5ppzt32sZow0r5pINKjOGkw0wBpNWgZ80Ux5oQJnCniI"
|
||||||
|
"voh9oeU2hpiRKiTK3/lrUwzISyMpuLMhHZccDMWJw9N1ZM4psynsOQtj3Wc6MLzcBRMeJwqOuV1BHxB4g90mU9OAOk"
|
||||||
|
"SvNptvZTUQHBzKTa/eOF5pC8zPXpeloPJq8pB5lR5WV6nqZvsDhxluTa81OysOXbkN/hN/cAWA3Rf+LJPtH6OvtHyh"
|
||||||
|
"1Ce0YZ+UYY8JP8AX61nJjDJ7Rk/oqcWgpPCZkvwjL+p6aTHoFqEC+4b/CUnmw2oP1jeC/ISg4mftpjG+eg4Ydhfuj5"
|
||||||
|
"Tz4DUeInodD0V8B8pvlz6ZQJ5Z0z2t9JxJRDdKGamnBnv228wFH3ec7fpdtY4bDNkNqlW9OlxW47T/hHxKzy6nTCjw"
|
||||||
|
"0m2TQaCNorxGQMGNHsbxWvIQq+tS8iX1tK+Gexliqljy3zU9xicyXRr3AkkgADUknQAc7zucFstaVGnSIBIu9Q39Kq"
|
||||||
|
"3peIAAUchNJ0QwBqVDWYdmibJ9qqRp+UG/iVnZmlymMei9StZ9DT2R5mH0RPZHmZsTT5RdXyjGdigMIvsiAwi+yJf6"
|
||||||
|
"rkfKMUjwPlGGxSGFX2RJfRl4CXOr5GHV8jGGqn0deAhLnVHgfKKMNXDhxxMRoLzjNE85gqU27rzbDMmGB3Bj4awNBR"
|
||||||
|
"vVvKaPGbSqYOotRaqWIAeg5sHW51HA67/AJ7p1Gy9o0cWmemdRbPTPpIeY7xz3RC+mj2slNqNRCrklGygKWOa3Zt77"
|
||||||
|
"TgDsyqfq6g8UInsL4YcpgbDLxXzEl52nk8i/qyt+zf8ss4fZdS+qMPwz1L6MnL5wGHTh8I8WfJzOx8OiIQ6sCSPVO6"
|
||||||
|
"bMYVDqB8xNqKKD/SZkVB/pHgvm0ybPU+qPOZBsq/qjzkdo9H2Japh6jAsSzUmqNkYnUlD6hPDd4b5z1Q1Q3Vv1qNTN"
|
||||||
|
"8pbK6Xtfc1iDbmpjxh5V1C7K5LJjZ9u5ZyVSs4Fn6zLcHODoCO863Q79b219LW0RrVFBDFqikEHXtW7wVuQ48LHkd8"
|
||||||
|
"vjDyq/tjYNarUzJURLCygG1xz01mpbo/i1+soN4syn4CTTDP6dMPUAJtqWsQdbPqQQQdD394tKdTDqxIAWmw9JHBU9"
|
||||||
|
"28W5bxp4zP/AJxrzqwmxMSWAJoXvuFRifLJOtFZlQEqNBr2rDzM4KojKynLlYEZXV7knuytv3A6aHwnQbP6QOoC4he"
|
||||||
|
"sXvqA5WUfaJ0O7vPf6RicyM3q1zvSgYjEYhn6tslNFSnZlcZd7EZTvJ4dwE583Ghnqf8AVNGqDUwrimd7Kq3Qk69un"
|
||||||
|
"3anepF+Jmo2pspDpiafVncKy6o3Dt27Pg4HK8uJrghrHpNxtDo7Up6p2xv0328P9ZpmUqbMCJMaPNEkRMZEBbtZdw9"
|
||||||
|
"6mVFF2YhUHFibASi06/8Ao/2TnqNinHZp3SlzcjtN7gbe88JdxHZbGwlPDUadEAkqLs3tOdWPnLpqLwMnlEXViTI2x"
|
||||||
|
"9YvsmLrF9k+cmaYiyCMgh1q8D5x9aOB848ghkEZE1HrR7J84Gry+MnkEMgjIqHXcvj/AChMmQQjIGzGYmvM5EiymbY"
|
||||||
|
"aTbWyVxFMq2+2jW1BnAK+K2ZWFy+QGyuu9VO+3EcjPV2QzX4/ZtOspSooYH4eElmrKsdGeklPFAU2qIXIuh3dYO8W3"
|
||||||
|
"Zhbd8Jvmp8J4rtnY1bZ9TrKRZ6ZOa2oynxG4852vRHputVVp4huCiqdCp4VP83nxidfVS8/jtDS5SJonhLQPf5GBmm"
|
||||||
|
"FPqTwh1J4S1FArimw3eXGYcbs6nWUCoCCPRcGzIfst+7ce+XYXHEQOLx+yatElizOg+sXeuvrp3feGm+4WUGVaah1q"
|
||||||
|
"hVNiAai9W191jc5O70dOVzeehF14jzE0+L2PSZ+spVFoPmzNaxVj7RUEENzBHO8DlKaI5DremxF75SM6g6HMpKuvO5"
|
||||||
|
"te1wbwq1FtlqqdPRqLVCqDpuLAMh+Hdczpf6opub18QatiCqhxTUaWvckvm78wYGQbZgFwMVTI1AzKrNY9zFXUH8sN"
|
||||||
|
"OQxVGrYgXqLuKsi1CRzUGzjwHumKmhAzKM3fY5l3cGBJB0Oh7+8Tqf0fojdXpr9kKoUeC5tPO3KA2DRvm+k2PENluN"
|
||||||
|
"wBs2tu6+6Qc9QJRr026t11y5hoN18oKmx4g28Zv8ABbfOiYhAL6ZwDY3G7dlbv0uDodDHV2PhdBUxIPeMzrpzUk6Hm"
|
||||||
|
"NZA7OwY/wDmDv8AWo6g9x7OsQWn2PTdc+FqKg9j06RPDJvQ/dIGtyDOe2ps1b5cTT6snRal7o7buxUta+ugYBuU3iU"
|
||||||
|
"sPRXrFx1RFGmc1EZbk20LKQBcgW3cpdp7ewdQij9Io1C4IyZg3WADW62sdATuhl5tj+jVRLtTOcezuPumjdGU2YEHm"
|
||||||
|
"J6i7bNps3+09WBvpB7oh+zmUlfAG3KafpZsFBQfG08W7IqhjTdFdHU2yhSigg6ixN9+8SWNSuIwuHarUSlT1aowVeX"
|
||||||
|
"EnkBc+6e0bLwKUKNOkm5FA8T3k8ydZxf9HOy87VMUV9ECmneATqSPh4C3Gegii3CSNI2ECBJ9SeERonhL6GMgSJAmU"
|
||||||
|
"0WkeobhAx2E5na3S+nRqNSp0alZkYo5DKiq47u8/CdV1DTgekn9H9avXqYinVS9RsxRhaxsBoR4SU9sn6Z4o6jZx/O"
|
||||||
|
"5+VOQPTTGDU7Nb3M/+SaM9DNr0/QZvwYgr8LiMbH24m41z/jB/mTJsXK3P6eYj/62p/xH/wDHCaf6Dtz++80jk0yvV"
|
||||||
|
"TIFhMxSRKTpiMVxItaZssWWBTxNBKilHUMDoRPOukPRl8O5r4fNbeRv04Ed4nqBSQeiGBBAIMzZqy48ewfSerS0/XL"
|
||||||
|
"9lajADwE3CdPrKAaddjxNSdXieimGdixpjXlMQ6JYQfViZ9xc5rmm6ff3NU/4n8pibp4f7O/vqfynVHozhR9WvlD+o"
|
||||||
|
"MMPq0/KI2njy5JunL92G86n8pH9Nqn9nH5iZ2I2Phx9Wn5ZlXZVAfVp5RtM5cOemlfuw6ebfxkW6Y4o7qFPyc/vnoK"
|
||||||
|
"bIo/s18hLC7Io/s18hH+jOXmn6W4zuo0/yt/GH6V439lT/I38Z6imyKPsL5CZP6rpD1F8hHszl5Q3SbHndTQf4ZMP0"
|
||||||
|
"j2kfUT/AIRnqpwNMeovkIhhE9lfyiPaenkWJ2ljapBqUaVQqCFL4ZWsO+2YTD1uL/s9D/8AHT/yT2Q4VPZHkJjxgSl"
|
||||||
|
"TqVOrz9WjPlA1awvYR7PTyX6VtDJ1YpqE3ZBhkC777gvHWYsMMcj56dMI1iMwoICAd+uWdRiemBqXFE06Q3dvD9Yb/"
|
||||||
|
"eFTn7M0+Ix+MqHTH0wDpYBqHxyD5xl/TZ+NZWw+Mdi1SmCSbsxoJr78s6LZeKxlWm+HxFOpiKDBUYIQjUgN1sgt3bmE"
|
||||||
|
"0VXZOIqEE1UxGouoxGcn3m9p1WB6GBaYr0atSjW9JUUE07g6KyvdmG/Um3KWc02fjpMDtzCYemKFOnVQU0zCmaToSt7M"
|
||||||
|
"13tnNyLnXfIVemPsUiebOB8AD84ld8Th8tWmKdanchCCoYi47Bb1XW45ZuU5tcG7OVSx4EkLcd2/v5TWYzrdP0txJOi0"
|
||||||
|
"1HDKT8SZJOl9YelTpt4XX95lGnsGqd5A/C3zIA+Mm+y6VP8A3ldF43dE+AzQjaJ0xHrUj+FwfgQJap9KqB39YnigPyJnN"
|
||||||
|
"ddgU+sap9xGf43t8Jmp45NOqwdd/tELTPwAMnlJ841JXXYba9Kp6FS/4WHzEuAk6g3B1B33E45Fx1Q6YWlTXddv1j28WG"
|
||||||
|
"hnZ4NMtNFIIIUAgm5v36gSSy/C5Z8l2odqZ4SjD2v/AERzLCRGIyBhCbChaEICKwKwhMjGyGYnQwhAr1FMwtTPKEJloivG"
|
||||||
|
"wgjjj8I4TNtVZpgnvlpFMITUZZ1TnApzihKqLU+cQpc4QhEhS5xVsMrqytqGBVhxB3whCOZxHQHAvuDJf2WImtxX9HFK3Y"
|
||||||
|
"rOORsR8oQkxdazE9A8QostdSBuBFvlKI6PY6ibg0z4VCscJi9WK3WzRiwmRqGHY3vndi5Hnebangse2hrpSHBEAjhE6rXj"
|
||||||
|
"GYdEC/arYms/HtkfAS7huhuDXXJmPEkn5whN4xrZ0NkYdPRpIPdLS0kG5QPdCEskKlYcIWHCEJUO3KFuUIQD3QhCB//Z")
|
||||||
|
|
||||||
|
# export excel path and
|
||||||
|
# export excel url
|
||||||
|
export_excel_path = 'analytics/ZOD_Bank_Analytics.xlsx'
|
||||||
|
export_excel_url = f"https://{settings.ALIYUN_OSS_BUCKET_NAME}.{settings.ALIYUN_OSS_ENDPOINT}/{export_excel_path}"
|
||||||
|
|
||||||
|
|
||||||
|
class BaseSetUp(APITestCase):
|
||||||
|
"""
|
||||||
|
basic setup
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
user data
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# user and admin email
|
||||||
|
self.user_email = '[email protected]'
|
||||||
|
self.admin_email = '[email protected]'
|
||||||
|
self.client = APIClient()
|
||||||
|
|
||||||
|
# create user
|
||||||
|
self.user = User.objects.create_user(username=self.user_email, email=self.user_email)
|
||||||
|
self.user.set_password('user@1234')
|
||||||
|
self.user.save()
|
||||||
|
|
||||||
|
# create admin
|
||||||
|
self.admin_user = User.objects.create_user(username=self.admin_email, email=self.admin_email,
|
||||||
|
is_staff=True, is_superuser=True)
|
||||||
|
self.admin_user.set_password('admin@1234')
|
||||||
|
self.admin_user.save()
|
||||||
|
|
||||||
|
|
||||||
|
class ArticleTestSetUp(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test cases data set up
|
||||||
|
for article create, update, list, retrieve and
|
||||||
|
remove card, survey and add test card, list test card and
|
||||||
|
default image upload and list
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
"""
|
||||||
|
set up data for test
|
||||||
|
create user and admin
|
||||||
|
create article, article card and article survey and survey options
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(ArticleTestSetUp, self).setUp()
|
||||||
|
|
||||||
|
# create article
|
||||||
|
self.article = Article.objects.create(title="Existing Article", description="Existing Description",
|
||||||
|
is_published=True)
|
||||||
|
# create article card
|
||||||
|
self.article_card = ArticleCard.objects.create(article=self.article, title="Existing Card 1",
|
||||||
|
description="Existing Card 1 Description")
|
||||||
|
# create article survey
|
||||||
|
self.article_survey = ArticleSurvey.objects.create(article=self.article, points=5,
|
||||||
|
question="Existing Survey Question 1")
|
||||||
|
# create article survey options
|
||||||
|
SurveyOption.objects.create(survey=self.article_survey, option="Existing Option 1", is_answer=True)
|
||||||
|
SurveyOption.objects.create(survey=self.article_survey, option="Existing Option 2", is_answer=False)
|
||||||
|
|
||||||
|
# article api url used for get api
|
||||||
|
self.article_list_url = 'web_admin:article-list'
|
||||||
|
|
||||||
|
# article api url used for post api
|
||||||
|
self.article_detail_url = 'web_admin:article-detail'
|
||||||
|
|
||||||
|
# article card data with default card image
|
||||||
|
self.article_card_data_with_default_card_image = {
|
||||||
|
"title": "Card 1",
|
||||||
|
"description": "Card 1 Description",
|
||||||
|
"image_name": "card1.jpg",
|
||||||
|
"image_url": "https://example.com/card1.jpg"
|
||||||
|
}
|
||||||
|
|
||||||
|
# article card data with base64 image
|
||||||
|
self.article_card_data_with_base64_image = {
|
||||||
|
"title": "Card base64",
|
||||||
|
"description": "Card base64 Description",
|
||||||
|
"image_name": "base64_image.jpg",
|
||||||
|
"image_url": base64_image
|
||||||
|
}
|
||||||
|
|
||||||
|
# article survey option data
|
||||||
|
self.article_survey_option_data = [
|
||||||
|
{"option": "Option 1", "is_answer": True},
|
||||||
|
{"option": "Option 2", "is_answer": False}
|
||||||
|
]
|
||||||
|
|
||||||
|
# article survey data
|
||||||
|
self.article_survey_data = [
|
||||||
|
{
|
||||||
|
"question": "Survey Question 1",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Survey Question 2",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Survey Question 3",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Survey Question 4",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"question": "Survey Question 5",
|
||||||
|
"options": self.article_survey_option_data
|
||||||
|
},
|
||||||
|
]
|
||||||
|
|
||||||
|
# article data with default card image
|
||||||
|
self.article_data_with_default_card_image = {
|
||||||
|
"title": "Test Article",
|
||||||
|
"description": "Test Description",
|
||||||
|
"article_cards": [
|
||||||
|
self.article_card_data_with_default_card_image
|
||||||
|
],
|
||||||
|
# minimum 5 article survey needed
|
||||||
|
"article_survey": self.article_survey_data
|
||||||
|
}
|
||||||
|
|
||||||
|
# article data with base64 card image
|
||||||
|
self.article_data_with_base64_card_image = {
|
||||||
|
"title": "Test Article",
|
||||||
|
"description": "Test Description",
|
||||||
|
"article_cards": [
|
||||||
|
self.article_card_data_with_base64_image
|
||||||
|
],
|
||||||
|
# minimum 5 article survey needed
|
||||||
|
"article_survey": self.article_survey_data
|
||||||
|
}
|
||||||
|
|
||||||
|
# article update data
|
||||||
|
self.article_update_data = {
|
||||||
|
"title": "Updated Article",
|
||||||
|
"description": "Updated Description",
|
||||||
|
|
||||||
|
# updated article card
|
||||||
|
"article_cards": [
|
||||||
|
{
|
||||||
|
"id": self.article_card.id,
|
||||||
|
"title": "Updated Card 1",
|
||||||
|
"description": "Updated Card 1 Description",
|
||||||
|
"image_name": "updated_card1.jpg",
|
||||||
|
"image_url": "https://example.com/updated_card1.jpg"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
# updated article survey
|
||||||
|
"article_survey": [
|
||||||
|
# updated article survey
|
||||||
|
{
|
||||||
|
"id": self.article_survey.id,
|
||||||
|
"question": "Updated Survey Question 1",
|
||||||
|
"options": [
|
||||||
|
{"id": self.article_survey.options.first().id,
|
||||||
|
"option": "Updated Option 1", "is_answer": False},
|
||||||
|
# New option
|
||||||
|
{"option": "New Option 3", "is_answer": True}
|
||||||
|
]
|
||||||
|
# added new articles
|
||||||
|
}] + self.article_survey_data
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class UserManagementSetUp(BaseSetUp):
|
||||||
|
"""
|
||||||
|
test cases for user management
|
||||||
|
users count, new sign-ups,
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
data setup
|
||||||
|
create new guardian and junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(UserManagementSetUp, self).setUp()
|
||||||
|
# guardian codes
|
||||||
|
self.guardian_code_1 = 'GRD123'
|
||||||
|
self.guardian_code_2 = 'GRD456'
|
||||||
|
|
||||||
|
# guardian 1
|
||||||
|
self.guardian = Guardian.objects.create(user=self.user, country_code=91, phone='8765876565',
|
||||||
|
country_name='India', gender=2, is_verified=True,
|
||||||
|
guardian_code=self.guardian_code_1)
|
||||||
|
|
||||||
|
# user 2 email
|
||||||
|
self.user_email_2 = '[email protected]'
|
||||||
|
# create user 2
|
||||||
|
self.user_2 = User.objects.create_user(username=self.user_email_2, email=self.user_email_2)
|
||||||
|
self.user_2.set_password('user2@1234')
|
||||||
|
self.user_2.save()
|
||||||
|
|
||||||
|
# guardian 2
|
||||||
|
self.guardian_2 = Guardian.objects.create(user=self.user_2, country_code=92, phone='8765876575',
|
||||||
|
country_name='India', gender=1, is_verified=True,
|
||||||
|
guardian_code=self.guardian_code_2)
|
||||||
|
|
||||||
|
# user 3 email
|
||||||
|
self.user_email_3 = '[email protected]'
|
||||||
|
# create user 3
|
||||||
|
self.user_3 = User.objects.create_user(username=self.user_email_3, email=self.user_email_3)
|
||||||
|
self.user_3.set_password('user3@1234')
|
||||||
|
self.user_3.save()
|
||||||
|
|
||||||
|
# junior 1
|
||||||
|
self.junior = Junior.objects.create(auth=self.user_3, country_name='India', gender=2,
|
||||||
|
is_verified=True, guardian_code=[self.guardian_code_1])
|
||||||
|
|
||||||
|
# user 4 email
|
||||||
|
self.user_email_4 = '[email protected]'
|
||||||
|
# create user 4
|
||||||
|
self.user_4 = User.objects.create_user(username=self.user_email_4, email=self.user_email_4)
|
||||||
|
self.user_4.set_password('user4@1234')
|
||||||
|
self.user_4.save()
|
||||||
|
|
||||||
|
# junior 2
|
||||||
|
self.junior_2 = Junior.objects.create(auth=self.user_4, country_code=92, phone='8768763443',
|
||||||
|
country_name='India', gender=1, is_verified=True,
|
||||||
|
guardian_code=[self.guardian_code_2])
|
||||||
|
|
||||||
|
|
||||||
|
class AnalyticsSetUp(UserManagementSetUp):
|
||||||
|
"""
|
||||||
|
test analytics
|
||||||
|
task assign report, junior leaderboard
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
"""
|
||||||
|
test data set up
|
||||||
|
create task and assigned to junior
|
||||||
|
create junior points data
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
super(AnalyticsSetUp, self).setUp()
|
||||||
|
|
||||||
|
# pending tasks 1
|
||||||
|
self.pending_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Pending Task 1', task_status=1,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# pending tasks 2
|
||||||
|
self.pending_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Pending Task 2', task_status=1,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# in progress tasks 1
|
||||||
|
self.in_progress_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='In progress Task 1', task_status=2,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# in progress tasks 2
|
||||||
|
self.in_progress_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='In progress Task 2', task_status=2,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# rejected tasks 1
|
||||||
|
self.rejected_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Rejected Task 1', task_status=3,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# rejected tasks 2
|
||||||
|
self.rejected_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Rejected Task 2', task_status=3,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# requested task 1
|
||||||
|
self.requested_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Requested Task 1', task_status=4,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# requested task 2
|
||||||
|
self.requested_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Requested Task 2', task_status=4,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# completed task 1
|
||||||
|
self.completed_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Completed Task 1', task_status=5,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
# completed task 2
|
||||||
|
self.completed_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Completed Task 2', task_status=5,
|
||||||
|
due_date='2023-09-12')
|
||||||
|
|
||||||
|
# expired task 1
|
||||||
|
self.expired_task_1 = JuniorTask.objects.create(guardian=self.guardian, junior=self.junior,
|
||||||
|
task_name='Expired Task 1', task_status=6,
|
||||||
|
due_date='2023-09-11')
|
||||||
|
# expired task 2
|
||||||
|
self.expired_task_2 = JuniorTask.objects.create(guardian=self.guardian_2, junior=self.junior_2,
|
||||||
|
task_name='Expired Task 2', task_status=6,
|
||||||
|
due_date='2023-09-11')
|
||||||
|
|
||||||
|
# junior point table data
|
||||||
|
JuniorPoints.objects.create(junior=self.junior_2, total_points=50)
|
||||||
|
JuniorPoints.objects.create(junior=self.junior, total_points=40)
|
||||||
|
|
||||||
|
# export excel url
|
||||||
|
self.export_excel_url = export_excel_url
|
||||||
@@ -0,0 +1,255 @@
|
|||||||
|
"""
|
||||||
|
web admin test user management file
|
||||||
|
"""
|
||||||
|
# django imports
|
||||||
|
from django.contrib.auth import get_user_model
|
||||||
|
from rest_framework import status
|
||||||
|
|
||||||
|
# local imports
|
||||||
|
from base.constants import GUARDIAN, JUNIOR
|
||||||
|
from web_admin.tests.test_set_up import UserManagementSetUp
|
||||||
|
|
||||||
|
# user model
|
||||||
|
User = get_user_model()
|
||||||
|
|
||||||
|
|
||||||
|
class UserManagementViewSetTestCase(UserManagementSetUp):
|
||||||
|
"""
|
||||||
|
test cases for user management
|
||||||
|
"""
|
||||||
|
def setUp(self) -> None:
|
||||||
|
super(UserManagementViewSetTestCase, self).setUp()
|
||||||
|
|
||||||
|
self.update_data = {
|
||||||
|
'email': '[email protected]',
|
||||||
|
'country_code': 93,
|
||||||
|
'phone': '8765454235'
|
||||||
|
}
|
||||||
|
self.user_management_endpoint = "/api/v1/user-management"
|
||||||
|
|
||||||
|
def test_user_management_list_all_users(self):
|
||||||
|
"""
|
||||||
|
test user management list all users
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/"
|
||||||
|
response = self.client.get(url, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming four user exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 4)
|
||||||
|
|
||||||
|
def test_user_management_list_guardians(self):
|
||||||
|
"""
|
||||||
|
test user management list guardians
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/?user_type={GUARDIAN}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming two guardians exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 2)
|
||||||
|
|
||||||
|
def test_user_management_list_juniors(self):
|
||||||
|
"""
|
||||||
|
test user management list juniors
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/?user_type={JUNIOR}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
# Assuming two juniors exists in the database
|
||||||
|
self.assertEqual(len(response.data['data']), 2)
|
||||||
|
|
||||||
|
def test_user_management_list_with_unauthorised_user(self):
|
||||||
|
"""
|
||||||
|
test user management list with unauthorised user
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# user unauthorised access
|
||||||
|
self.client.force_authenticate(user=self.user)
|
||||||
|
url = f"{self.user_management_endpoint}/"
|
||||||
|
response = self.client.get(url, format='json')
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
|
||||||
|
|
||||||
|
def test_user_management_retrieve_guardian(self):
|
||||||
|
"""
|
||||||
|
test user management retrieve guardian
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
def test_user_management_retrieve_junior(self):
|
||||||
|
"""
|
||||||
|
test user management retrieve junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(len(response.data['data']), 1)
|
||||||
|
|
||||||
|
def test_user_management_retrieve_without_user_type(self):
|
||||||
|
"""
|
||||||
|
test user management retrieve without user type
|
||||||
|
user status is mandatory
|
||||||
|
API will throw error
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_guardian(self):
|
||||||
|
"""
|
||||||
|
test user management update guardian
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}"
|
||||||
|
response = self.client.patch(url, self.update_data, format='json',)
|
||||||
|
self.user.refresh_from_db()
|
||||||
|
self.guardian.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.user.email, self.update_data['email'])
|
||||||
|
self.assertEqual(self.guardian.country_code, self.update_data['country_code'])
|
||||||
|
self.assertEqual(self.guardian.phone, self.update_data['phone'])
|
||||||
|
|
||||||
|
def test_user_management_update_guardian_with_existing_email(self):
|
||||||
|
"""
|
||||||
|
test user management update guardian with existing email
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}"
|
||||||
|
data = {
|
||||||
|
'email': self.user_email_2
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_guardian_with_existing_phone(self):
|
||||||
|
"""
|
||||||
|
test user management update guardian with existing phone
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/?user_type={GUARDIAN}"
|
||||||
|
data = {
|
||||||
|
'phone': self.guardian_2.phone
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_junior(self):
|
||||||
|
"""
|
||||||
|
test user management update junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}"
|
||||||
|
response = self.client.patch(url, self.update_data, format='json',)
|
||||||
|
self.user_3.refresh_from_db()
|
||||||
|
self.junior.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.user_3.email, self.update_data['email'])
|
||||||
|
self.assertEqual(self.junior.country_code, self.update_data['country_code'])
|
||||||
|
self.assertEqual(self.junior.phone, self.update_data['phone'])
|
||||||
|
|
||||||
|
def test_user_management_update_junior_with_existing_email(self):
|
||||||
|
"""
|
||||||
|
test user management update guardian with existing phone
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}"
|
||||||
|
data = {
|
||||||
|
'email': self.user_email_4
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_junior_with_existing_phone(self):
|
||||||
|
"""
|
||||||
|
test user management update junior with existing phone
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/?user_type={JUNIOR}"
|
||||||
|
data = {
|
||||||
|
'phone': self.junior_2.phone
|
||||||
|
}
|
||||||
|
response = self.client.patch(url, data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_update_without_user_type(self):
|
||||||
|
"""
|
||||||
|
test user management update without user type
|
||||||
|
user status is mandatory
|
||||||
|
API will throw error
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/"
|
||||||
|
response = self.client.patch(url, self.update_data, format='json',)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
def test_user_management_change_status_guardian(self):
|
||||||
|
"""
|
||||||
|
test user management change status guardian
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/change-status/?user_type={GUARDIAN}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.guardian.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.guardian.is_active, False)
|
||||||
|
|
||||||
|
def test_user_management_change_status_junior(self):
|
||||||
|
"""
|
||||||
|
test user management change status junior
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user_3.id}/change-status/?user_type={JUNIOR}"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.junior.refresh_from_db()
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_200_OK)
|
||||||
|
self.assertEqual(self.junior.is_active, False)
|
||||||
|
|
||||||
|
def test_user_management_change_status_without_user_type(self):
|
||||||
|
"""
|
||||||
|
test user management change status without user type
|
||||||
|
user status is mandatory
|
||||||
|
API will throw error
|
||||||
|
:return:
|
||||||
|
"""
|
||||||
|
# admin user authentication
|
||||||
|
self.client.force_authenticate(user=self.admin_user)
|
||||||
|
url = f"{self.user_management_endpoint}/{self.user.id}/"
|
||||||
|
response = self.client.get(url)
|
||||||
|
self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user