282 Commits

Author SHA1 Message Date
komoliddin
3a08c81ff3 Add is_archive field to EvaluationRequest model. Write apis for update is_archive and list archived requests 2026-04-23 12:23:55 +05:00
github-actions[bot]
320f490d23 🔄 Update image to 107 [CI SKIP] 2026-04-22 10:03:44 +00:00
d4e6d80c86 Merge pull request 'deploy.yaml fixess' (#88) from fix/cicd-env into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #88
2026-04-22 10:02:12 +00:00
1f0e942be8 deploy.yaml fixess 2026-04-22 15:01:52 +05:00
github-actions[bot]
2af67333e2 🔄 Update image to 106 [CI SKIP] 2026-04-22 09:21:04 +00:00
81c689e7e9 Merge pull request 'feat: add 404 response for missing company or person in Didox API' (#86) from didox-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m56s
Reviewed-on: #86
2026-04-22 09:19:30 +00:00
github-actions[bot]
09d2e0954c 🔄 Update image to 105 [CI SKIP] 2026-04-22 09:17:53 +00:00
84a5afb2ee Merge pull request 'add request status change api' (#87) from shaxob into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m57s
Reviewed-on: #87
Reviewed-by: xoliqberdiyev <behruz@felixits.uz>
2026-04-22 09:16:19 +00:00
github-actions[bot]
cc8fc345d9 🔄 Update image to 104 [CI SKIP] 2026-04-22 09:11:31 +00:00
03e4387e4d Merge pull request 'fix: handle response errors and improve data return in TechPassportService' (#85) from tech-passport into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m16s
Reviewed-on: #85
Reviewed-by: xoliqberdiyev <behruz@felixits.uz>
2026-04-22 09:09:38 +00:00
Shaxobff
64993805af add request status change api 2026-04-22 13:52:39 +05:00
komoliddin
01711b5927 feat: add 404 response for missing company or person in Didox API 2026-04-22 11:28:35 +05:00
komoliddin
33aa06f80b fix: handle response errors and improve data return in TechPassportService 2026-04-22 11:17:09 +05:00
github-actions[bot]
cb2d83b00d 🔄 Update image to 103 [CI SKIP] 2026-04-21 12:36:45 +00:00
46c9621457 Merge pull request 'sort yangilandi' (#84) from fix/evaluation-request-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #84
2026-04-21 12:35:14 +00:00
github-actions[bot]
8d73fa09a7 🔄 Update image to 102 [CI SKIP] 2026-04-21 11:43:22 +00:00
3367063707 Merge pull request 'write swagger docs' (#82) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m56s
Reviewed-on: #82
2026-04-21 11:41:48 +00:00
xoliqberdiyev
ae926914a5 write swagger docs 2026-04-21 16:41:37 +05:00
github-actions[bot]
229676be5c 🔄 Update image to 101 [CI SKIP] 2026-04-21 11:27:41 +00:00
d2f517baf4 Merge pull request 'feat: uncomment significant code for ws' (#81) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #81
2026-04-21 11:26:08 +00:00
xoliqberdiyev
6df20f35c2 feat: uncomment significant code for ws 2026-04-21 16:25:51 +05:00
github-actions[bot]
fa676bfa96 🔄 Update image to 100 [CI SKIP] 2026-04-21 11:13:53 +00:00
ca9f12ae32 Merge pull request 'fix' (#80) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m57s
Reviewed-on: #80
2026-04-21 11:12:18 +00:00
xoliqberdiyev
56f693abfd fix 2026-04-21 16:12:06 +05:00
github-actions[bot]
5049919865 🔄 Update image to 99 [CI SKIP] 2026-04-21 10:29:59 +00:00
ca3c9bfe4a Merge pull request 'feat: removing location fields from auto-evalutaion model, adding new tex_passport_file field for auto-evalutaion model' (#79) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m0s
Reviewed-on: #79
2026-04-21 10:28:22 +00:00
xoliqberdiyev
8d8744731a feat: removing location fields from auto-evalutaion model, adding new tex_passport_file field for auto-evalutaion model 2026-04-21 15:28:03 +05:00
github-actions[bot]
7ab1e0b1e0 🔄 Update image to 98 [CI SKIP] 2026-04-21 10:23:04 +00:00
f66a35ec80 Merge pull request 'remote: tex_passport_file field removed from quick evaluation serializers' (#78) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m56s
Reviewed-on: #78
2026-04-21 10:21:29 +00:00
xoliqberdiyev
f71dfa50c1 remote: tex_passport_file field removed from quick evaluation serializers 2026-04-21 15:21:14 +05:00
github-actions[bot]
f68f34178e 🔄 Update image to 97 [CI SKIP] 2026-04-21 10:20:39 +00:00
ab68a6a463 Merge pull request 'feat: add Tech Passport API integration for vehicle information retrieval' (#76) from tech-passport into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m0s
Reviewed-on: #76
Reviewed-by: xoliqberdiyev <behruz@felixits.uz>
2026-04-21 10:19:01 +00:00
github-actions[bot]
d246406384 🔄 Update image to 96 [CI SKIP] 2026-04-21 10:15:49 +00:00
0a74f71efa Merge pull request 'fix: change the evaluation-request when creating auto evaluation to this evaluation-request' (#77) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m2s
Reviewed-on: #77
2026-04-21 10:14:13 +00:00
xoliqberdiyev
575364e0ef fix: change the evaluation-request when creating auto evaluation to this evaluation-request 2026-04-21 15:13:41 +05:00
komoliddin
406477fc33 feat: add Tech Passport API integration for vehicle information retrieval 2026-04-21 15:11:46 +05:00
github-actions[bot]
2aa73e15a0 🔄 Update image to 95 [CI SKIP] 2026-04-21 09:52:02 +00:00
372a289447 Merge pull request 'change region models name field from unique to typical' (#75) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m58s
Reviewed-on: #75
2026-04-21 09:50:28 +00:00
xoliqberdiyev
7343f6191d change region models name field from unique to typical 2026-04-21 14:48:41 +05:00
github-actions[bot]
5c4d5fbb71 🔄 Update image to 94 [CI SKIP] 2026-04-21 09:47:17 +00:00
20043db5b3 Merge pull request 'change ws response' (#74) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m1s
Reviewed-on: #74
2026-04-21 09:45:41 +00:00
xoliqberdiyev
7d6618155f change ws response 2026-04-21 14:45:01 +05:00
github-actions[bot]
11605e6e6c 🔄 Update image to 93 [CI SKIP] 2026-04-21 08:07:30 +00:00
komoliddin
8a1a66a05d feat: add Didox service integration for company info retrieval
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m9s
2026-04-21 13:05:45 +05:00
github-actions[bot]
70555fa93a 🔄 Update image to 92 [CI SKIP] 2026-04-20 13:12:55 +00:00
674eafe65b Merge pull request 'fix' (#73) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m59s
Reviewed-on: #73
2026-04-20 13:11:20 +00:00
xoliqberdiyev
47833176e2 fix 2026-04-20 18:10:51 +05:00
b03d2e1c5e Merge pull request 'add new fields' (#72) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 31s
Reviewed-on: #72
2026-04-20 13:06:21 +00:00
xoliqberdiyev
7be8999a39 add new fields 2026-04-20 18:06:06 +05:00
github-actions[bot]
c5624e361d 🔄 Update image to 90 [CI SKIP] 2026-04-20 13:02:55 +00:00
c44b08bb28 Merge pull request 'fix' (#71) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m59s
Reviewed-on: #71
2026-04-20 13:01:19 +00:00
xoliqberdiyev
d448324f89 fix 2026-04-20 18:01:00 +05:00
github-actions[bot]
0508a49c1d 🔄 Update image to 89 [CI SKIP] 2026-04-20 10:38:57 +00:00
8b97ca53bb Merge pull request 'fix' (#70) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m56s
Reviewed-on: #70
2026-04-20 10:37:24 +00:00
xoliqberdiyev
2d64777041 fix 2026-04-20 15:37:06 +05:00
github-actions[bot]
9f7b29fa13 🔄 Update image to 88 [CI SKIP] 2026-04-20 10:35:28 +00:00
b4a3243e9c Merge pull request 'gi' (#69) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #69
2026-04-20 10:33:56 +00:00
xoliqberdiyev
63359e69f7 gi 2026-04-20 15:33:04 +05:00
github-actions[bot]
3c9438aff6 🔄 Update image to 87 [CI SKIP] 2026-04-20 10:31:42 +00:00
a3c67c5bfb Merge pull request 'remove name_ru and name_en fields' (#68) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #68
2026-04-20 10:30:11 +00:00
xoliqberdiyev
e768c73d54 remove name_ru and name_en fields 2026-04-20 15:29:59 +05:00
github-actions[bot]
a2684ff749 🔄 Update image to 86 [CI SKIP] 2026-04-20 10:27:16 +00:00
91bbb6b6e5 Merge pull request 'change file name' (#67) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m56s
Reviewed-on: #67
2026-04-20 10:25:44 +00:00
xoliqberdiyev
01a70debaf change file name 2026-04-20 15:25:28 +05:00
github-actions[bot]
ae9f10aa4d 🔄 Update image to 85 [CI SKIP] 2026-04-20 10:16:13 +00:00
f2dc2e1d52 Merge pull request 'add migrations files in shared app' (#66) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m56s
Reviewed-on: #66
2026-04-20 10:14:39 +00:00
xoliqberdiyev
7081a03a98 add migrations files in shared app 2026-04-20 15:14:26 +05:00
github-actions[bot]
10646e2ebf 🔄 Update image to 84 [CI SKIP] 2026-04-20 10:09:49 +00:00
93f6e79f58 Merge pull request 'fix' (#65) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m57s
Reviewed-on: #65
2026-04-20 10:08:16 +00:00
xoliqberdiyev
3b95734ea4 fix 2026-04-20 15:08:04 +05:00
d1aad04ab8 Merge pull request 'registring region,district and village models' (#64) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 51s
Reviewed-on: #64
2026-04-20 10:06:37 +00:00
xoliqberdiyev
adf3c9fff9 registring region,district and village models 2026-04-20 15:06:06 +05:00
github-actions[bot]
7e4a90fe2a 🔄 Update image to 82 [CI SKIP] 2026-04-20 05:46:19 +00:00
503c16bdb8 Merge pull request 'swagger to'g'irlandi' (#63) from fix/swagger into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m57s
Reviewed-on: #63
2026-04-20 05:44:45 +00:00
701a375e3c swagger to'g'irlandi 2026-04-20 10:44:23 +05:00
github-actions[bot]
e64d7d037f 🔄 Update image to 81 [CI SKIP] 2026-04-18 11:56:52 +00:00
b7ecf0e47c Merge pull request 'fix' (#62) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m54s
Reviewed-on: #62
2026-04-18 11:55:21 +00:00
xoliqberdiyev
c7c76e9c79 fix 2026-04-18 16:55:02 +05:00
github-actions[bot]
afdc6aa0ac 🔄 Update image to 80 [CI SKIP] 2026-04-18 11:52:43 +00:00
f991e3e4ec Merge pull request 'behruz' (#61) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m57s
Reviewed-on: #61
2026-04-18 11:51:08 +00:00
xoliqberdiyev
0f142be77b fix 2026-04-18 16:50:48 +05:00
xoliqberdiyev
182b2483bf fix 2026-04-18 16:48:32 +05:00
469659baa8 Merge pull request 'fix' (#60) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m17s
Reviewed-on: #60
2026-04-18 11:32:05 +00:00
xoliqberdiyev
965328edd4 fix 2026-04-18 16:31:53 +05:00
github-actions[bot]
11aa88a2b0 🔄 Update image to 78 [CI SKIP] 2026-04-18 11:25:47 +00:00
225565bc41 Merge pull request 'add extra user fields for chat' (#59) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #59
2026-04-18 11:24:13 +00:00
xoliqberdiyev
75365f8e7e add extra user fields for chat 2026-04-18 16:24:02 +05:00
github-actions[bot]
3e9675a4be 🔄 Update image to 77 [CI SKIP] 2026-04-18 11:22:09 +00:00
c3cf7ac9d5 Merge pull request 'add all fields to ordering_fields filter' (#58) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #58
2026-04-18 11:20:36 +00:00
xoliqberdiyev
5dcafb9cc1 add all fields to ordering_fields filter 2026-04-18 16:20:19 +05:00
github-actions[bot]
62bc6c17b8 🔄 Update image to 76 [CI SKIP] 2026-04-17 13:46:12 +00:00
22284f228f Merge pull request 'fi' (#57) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m56s
Reviewed-on: #57
2026-04-17 13:44:40 +00:00
xoliqberdiyev
aa487c1557 fi 2026-04-17 18:44:18 +05:00
github-actions[bot]
73d5222a5b 🔄 Update image to 75 [CI SKIP] 2026-04-17 13:35:44 +00:00
707ea1e1fd Merge pull request 'add celery to docker-compose file' (#56) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m52s
Reviewed-on: #56
2026-04-17 13:34:12 +00:00
xoliqberdiyev
bfbc14db82 add celery to docker-compose file 2026-04-17 18:33:58 +05:00
github-actions[bot]
4c1f1d7104 🔄 Update image to 74 [CI SKIP] 2026-04-17 13:26:45 +00:00
38e7aac505 Merge pull request 'fix' (#55) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m50s
Reviewed-on: #55
2026-04-17 13:25:14 +00:00
xoliqberdiyev
a973da91f5 fix 2026-04-17 18:24:59 +05:00
github-actions[bot]
ec94e4cdd0 🔄 Update image to 73 [CI SKIP] 2026-04-17 13:21:05 +00:00
87701c31f1 Merge pull request 'fix' (#54) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m51s
Reviewed-on: #54
2026-04-17 13:19:34 +00:00
xoliqberdiyev
35739ddc27 fix 2026-04-17 18:19:12 +05:00
e8feafca0b Merge pull request 'add new task for sending new messages to the chat' (#53) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 48s
Reviewed-on: #53
2026-04-17 13:15:06 +00:00
xoliqberdiyev
505c33a554 add new task for sending new messages to the chat 2026-04-17 18:14:24 +05:00
github-actions[bot]
f24afaf55c 🔄 Update image to 71 [CI SKIP] 2026-04-17 10:15:24 +00:00
665db58bbc Merge pull request 'fix' (#52) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m50s
Reviewed-on: #52
2026-04-17 10:13:52 +00:00
xoliqberdiyev
57e22e04db fix 2026-04-17 15:13:36 +05:00
github-actions[bot]
528dc57ea2 🔄 Update image to 70 [CI SKIP] 2026-04-17 10:13:21 +00:00
774be87f45 Merge pull request 'feat: add new extra fields for auto-evalution list serializer' (#51) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m50s
Reviewed-on: #51
2026-04-17 10:11:48 +00:00
xoliqberdiyev
3d0141197f feat: add new extra fields for auto-evalution list serializer 2026-04-17 15:11:26 +05:00
9dc093e244 Merge pull request 'feat: auto-evaluation update api serializer class changed' (#50) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m19s
Reviewed-on: #50
2026-04-17 10:01:57 +00:00
xoliqberdiyev
aa1e4ca6fc feat: auto-evaluation update api serializer class changed 2026-04-17 15:01:25 +05:00
github-actions[bot]
04ef567d5c 🔄 Update image to 68 [CI SKIP] 2026-04-16 10:33:44 +00:00
b604fff1c2 Merge pull request 'fi' (#49) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m51s
Reviewed-on: #49
2026-04-16 10:32:13 +00:00
xoliqberdiyev
277bc2874e fi 2026-04-16 15:31:49 +05:00
github-actions[bot]
37459e8485 🔄 Update image to 67 [CI SKIP] 2026-04-16 10:28:48 +00:00
f231d889a5 Merge pull request 'add base url' (#48) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m49s
Reviewed-on: #48
2026-04-16 10:27:19 +00:00
xoliqberdiyev
3fa70d9436 add base url 2026-04-16 15:26:47 +05:00
github-actions[bot]
6b1a9439ce 🔄 Update image to 66 [CI SKIP] 2026-04-16 10:15:07 +00:00
d26e2b0147 Merge pull request 'fix' (#47) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m49s
Reviewed-on: #47
2026-04-16 10:13:37 +00:00
xoliqberdiyev
da71ce6d28 fix 2026-04-16 15:13:19 +05:00
github-actions[bot]
bc157da00d 🔄 Update image to 65 [CI SKIP] 2026-04-16 10:09:53 +00:00
9c40908cc7 Merge pull request 'some changes' (#46) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m54s
Reviewed-on: #46
2026-04-16 10:08:16 +00:00
xoliqberdiyev
7ee2eee437 some changes 2026-04-16 15:07:52 +05:00
github-actions[bot]
31a08c810d 🔄 Update image to 64 [CI SKIP] 2026-04-03 12:34:09 +00:00
264548c3b1 Merge pull request 'feat: add appraisers for auto-evaluation and add curd for this' (#45) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m59s
Reviewed-on: #45
2026-04-03 12:32:29 +00:00
xoliqberdiyev
9a18dda657 feat: add appraisers for auto-evaluation and add curd for this 2026-04-03 17:32:05 +05:00
github-actions[bot]
c7612221b8 🔄 Update image to 63 [CI SKIP] 2026-04-03 11:45:10 +00:00
a89dbafb62 Merge pull request 'feat: add filter and pagination to document api' (#44) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #44
2026-04-03 11:42:02 +00:00
xoliqberdiyev
be0c96b28a feat: add filter and pagination to document api 2026-04-03 16:41:39 +05:00
github-actions[bot]
9d39f0b339 🔄 Update image to 62 [CI SKIP] 2026-04-03 11:32:26 +00:00
797286bcdb Merge pull request 'behruz' (#43) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m54s
Reviewed-on: #43
2026-04-03 11:30:50 +00:00
xoliqberdiyev
091ddb39ed feat: add new api 2026-04-03 16:28:29 +05:00
xoliqberdiyev
12b19290d6 feat: add new document and documentcategory model for auto evaluation detail 2026-04-03 16:19:53 +05:00
github-actions[bot]
36ac4af4cd 🔄 Update image to 61 [CI SKIP] 2026-04-03 10:30:10 +00:00
4784b58abc Merge pull request 'feat: add new evaluation-request crud apis for admin' (#42) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #42
2026-04-03 10:28:32 +00:00
xoliqberdiyev
3664f8f66d feat: add new evaluation-request crud apis for admin 2026-04-03 15:27:43 +05:00
github-actions[bot]
17e63ee2e4 🔄 Update image to 60 [CI SKIP] 2026-04-02 16:16:23 +00:00
40a68ff47d Merge pull request 'feat/avto-chat' (#41) from feat/avto-chat into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m56s
Reviewed-on: #41
2026-04-02 16:14:46 +00:00
cc6ef4cd63 make test qilindi 2026-04-02 21:14:17 +05:00
8f54bdedc2 chat qo'shildi 2026-04-02 20:50:00 +05:00
github-actions[bot]
878301605c 🔄 Update image to 59 [CI SKIP] 2026-04-02 13:30:22 +00:00
ea3f14058e Merge pull request 'feat: change AutoEvaluation model field and serializer fields' (#40) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m50s
Reviewed-on: #40
2026-04-02 13:28:51 +00:00
xoliqberdiyev
ee4754b23d feat: change AutoEvaluation model field and serializer fields 2026-04-02 18:28:21 +05:00
github-actions[bot]
5dea19c104 🔄 Update image to 58 [CI SKIP] 2026-04-02 13:05:23 +00:00
eb2156f076 Merge pull request 'fix: error fixed about .authenticate() method and Evaluationrequest tests updated' (#39) from behruz into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m51s
Reviewed-on: #39
2026-04-02 13:03:51 +00:00
xoliqberdiyev
de03c97dac fix: error fixed about .authenticate() method and Evaluationrequest tests updated 2026-04-02 18:03:20 +05:00
01617c7e14 Merge pull request 'feat: change EvaluationrequestModel fields from CharField to ForeignKey, such as value_determined, rate_goal, property_rights, form_ownership, and change the BaseEvaluationrequestSerializer fields' (#38) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m25s
Reviewed-on: #38
2026-04-02 12:49:33 +00:00
xoliqberdiyev
d5a132c75b feat: change EvaluationrequestModel fields from CharField to ForeignKey, such as value_determined, rate_goal, property_rights, form_ownership, and change the BaseEvaluationrequestSerializer fields 2026-04-02 17:49:03 +05:00
09862985e1 Merge pull request 'worked_hours to'g'irlandi' (#37) from fix/evaluation-request into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m27s
Reviewed-on: #37
2026-04-02 12:18:24 +00:00
e9d779d489 worked_hours to'g'irlandi 2026-04-02 17:17:49 +05:00
b8dcbe811d Merge pull request 'feat: change user_update api response, and add the basic and session auth to rest framework config file' (#36) from behruz into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m26s
Reviewed-on: #36
2026-04-02 12:08:35 +00:00
xoliqberdiyev
0dbd246120 feat: change user_update api response, and add the basic and session auth to rest framework config file 2026-04-02 17:07:56 +05:00
github-actions[bot]
9d8fd8e6de 🔄 Update image to 54 [CI SKIP] 2026-04-02 11:20:16 +00:00
41b63b1645 Merge pull request 'fix test' (#35) from fix/make-test into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m50s
Reviewed-on: #35
2026-04-02 11:18:45 +00:00
22740e59a7 fix test 2026-04-02 16:17:36 +05:00
43e6d8190e Merge pull request 'api response struktra ozgardi qb query kamytraildi' (#34) from fix/evaluation-history into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m14s
Reviewed-on: #34
2026-04-02 11:13:00 +00:00
ea020ae500 api response struktra ozgardi qb query kamytraildi 2026-04-02 16:12:28 +05:00
ad1e61498c Merge pull request 'feat: avto va tezkor baholash uchun history bo'limi qo'shildi' (#33) from feat/evaluation-history into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m14s
Reviewed-on: #33
2026-04-02 10:59:01 +00:00
2492583f43 feat: avto va tezkor baholash uchun history bo'limi qo'shildi 2026-04-02 15:57:41 +05:00
github-actions[bot]
c4330fd6f7 🔄 Update image to 51 [CI SKIP] 2026-03-18 09:13:47 +00:00
11f967da42 Merge pull request 'Mulk shakli uchun api kerak va Mulk egalik huquqi uchun api kerak olish uchun api chiqarildi' (#32) from feat/property-references-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m55s
Reviewed-on: #32
2026-03-18 09:12:13 +00:00
Husanjonazamov
f13f1eb16f Mulk shakli uchun api kerak va Mulk egalik huquqi uchun api kerak olish uchun api chiqarildi 2026-03-18 14:10:49 +05:00
github-actions[bot]
2ed50e10d9 🔄 Update image to 50 [CI SKIP] 2026-03-18 08:00:35 +00:00
b3f60b257a Merge pull request 'Aniqlanayotgan qiymat turi uchun api kerak' (#31) from feat/determined-value-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m16s
Reviewed-on: #31
2026-03-18 07:58:50 +00:00
Husanjonazamov
7f8858ac7e Aniqlanayotgan qiymat turi uchun api kerak 2026-03-18 12:41:51 +05:00
github-actions[bot]
28fdf82c08 🔄 Update image to 49 [CI SKIP] 2026-03-17 14:38:21 +00:00
a06cf173b6 Merge pull request 'Baholash maqsadi uchun api chiqarildi' (#30) from feat/valuation-purpose-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m59s
Reviewed-on: #30
2026-03-17 14:36:47 +00:00
Husanjonazamov
9593b02c2d Baholash maqsadi uchun api chiqarildi 2026-03-17 19:36:21 +05:00
386e9ead89 Merge pull request 'user request yuborishda api fix qilindi' (#29) from fix/user-request into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m34s
Reviewed-on: #29
2026-03-17 14:07:08 +00:00
Husanjonazamov
9a13a2b4f0 user request yuborishda api fix qilindi 2026-03-17 19:06:38 +05:00
github-actions[bot]
9b57611b62 🔄 Update image to 47 [CI SKIP] 2026-03-17 13:27:52 +00:00
4479a0c1a5 Merge pull request 'use modeliga avatar fieldni qoshildi' (#28) from feat/user into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m34s
Reviewed-on: #28
2026-03-17 13:25:41 +00:00
Husanjonazamov
0d574a92a9 use modeliga avatar fieldni qoshildi 2026-03-17 18:25:20 +05:00
Husanjonazamov
8207b750b8 sort yangilandi 2026-03-12 17:29:10 +05:00
github-actions[bot]
397f239b1d 🔄 Update image to 46 [CI SKIP] 2026-03-10 09:06:20 +00:00
7bcf7bdc7f Merge pull request 'user ariza uchun requesty yaratish uchun api chiqarildi' (#27) from feat/evaluation-request-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m6s
Reviewed-on: #27
2026-03-10 09:04:34 +00:00
Husanjonazamov
93757b0342 user ariza uchun requesty yaratish uchun api chiqarildi 2026-03-10 14:02:48 +05:00
github-actions[bot]
6525a14ca6 🔄 Update image to 45 [CI SKIP] 2026-03-10 07:13:04 +00:00
5bb3dcd432 Merge pull request 'feat(auto-evaluation): to'liq CRUD API va 4 bosqichli forma qo'shildi' (#25) from feat/auto-evaluation-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m8s
Reviewed-on: #25
2026-03-10 07:11:18 +00:00
Husanjonazamov
5c2fe032d0 feat(auto-evaluation): to'liq CRUD API va 4 bosqichli forma qo'shildi
- 10 ta yangi choice klass qo'shildi: ObjectOwnerType, PropertyRights,
  FormOwnership, LocationHighways, LocationConvenience, AutoCarType, AutoCarWheel
- AutoEvaluationModel ga ~30 ta yangi field qo'shildi (4 bosqich):
  1-bosqich: ro'yxatga olish raqami, sanalar, ob'ekt turi
  2-bosqich: egasi ma'lumotlari (jismoniy/yuridik), mulk huquqi, egalik shakli
  3-bosqich: manzil (viloyat, tuman, shahar, mahalla, ko'cha, uy)
  4-bosqich: avtomobil (tex passport, marka, model, raqam, rang, dvigatel)
- CreateSerializer ga validatsiya qo'shildi:
  passport formati (AA 1234567), tex passport formati (AAA 1234567),
  egasi turiga qarab majburiy fieldlar (jismoniy yoki yuridik)
- View ReadOnlyModelViewSet dan ModelViewSet ga o'zgartirildi
- Admin 4 bosqichli fieldset bilan yangilandi
- Yangi filterlar: object_owner_type, property_rights, form_ownership
- VehicleModel fieldlari FK → ReferenceitemModel ga o'tkazildi
- Migratsiyalar: 0015, 0016, 0017
2026-03-10 12:10:28 +05:00
github-actions[bot]
b3581233c1 🔄 Update image to 44 [CI SKIP] 2026-03-09 11:22:03 +00:00
8d30162e87 Merge pull request 'fix test error' (#24) from fix/reference into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m7s
Reviewed-on: #24
2026-03-09 11:20:19 +00:00
Husanjonazamov
44f53649cd fix test error 2026-03-09 16:19:53 +05:00
460e3158b8 Merge pull request 'feat: add ReferenceItem model and update QuickEvaluation FKs' (#23) from feat/quick-evaluation-create-api into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m24s
Reviewed-on: #23
2026-03-09 11:04:38 +00:00
Husanjonazamov
3798037240 feat: add ReferenceItem model and update QuickEvaluation FKs
- Create ReferenceitemModel with type, name, parent (self FK), order, is_active fields
- Add ReferenceType choices: brand, marka, color, fuel_type, body_type, car_position, state_car
- Implement ReferenceItem API (list, retrieve) with filter by type/parent/is_active, search, ordering
- Add ReferenceItem admin with list_filter, search, inline editing
- Change QuickEvaluation FK fields from shared.OptionsModel to evaluation.ReferenceitemModel
- Update serializers and views to use .name instead of .key
- Add ReferenceItem to unfold admin navigation
2026-03-09 16:04:15 +05:00
github-actions[bot]
fd2ecd953a 🔄 Update image to 42 [CI SKIP] 2026-03-09 08:32:20 +00:00
9d405330c6 Merge pull request 'feat: add search, filter, sort and pagination to QuickEvaluation list API' (#22) from feat/quick-evaluation-list-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m50s
Reviewed-on: #22
2026-03-09 08:30:49 +00:00
Husanjonazamov
8feee78ce4 feat: add search, filter, sort and pagination to QuickEvaluation list API 2026-03-09 13:21:55 +05:00
github-actions[bot]
1e67a9f86e 🔄 Update image to 41 [CI SKIP] 2026-03-09 07:30:29 +00:00
9c176674f9 Merge pull request 'tezkor avto baholanganlarni id bo'yicha olish uchun api qoshilfi' (#21) from feat/quick-evaluation-detail-api into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 1m53s
Reviewed-on: #21
2026-03-09 07:28:57 +00:00
Husanjonazamov
04d5b92ac5 tezkor avto baholanganlarni id bo'yicha olish uchun api qoshilfi 2026-03-09 12:26:29 +05:00
github-actions[bot]
50e085fcfd 🔄 Update image to 40 [CI SKIP] 2026-03-05 09:58:13 +00:00
13ba9dcae4 Merge pull request 'log file va test ad file togirilafi' (#20) from fix/cicd-stockv2 into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m8s
Reviewed-on: #20
2026-03-05 09:56:27 +00:00
Husanjonazamov
3707e10ccf log file va test ad file togirilafi 2026-03-05 14:56:06 +05:00
github-actions[bot]
1d275b9907 🔄 Update image to 39 [CI SKIP] 2026-03-02 07:36:37 +00:00
1ccee92417 Merge pull request 'hamma modellarda get serizlizers uchun fiedl qoshilfi' (#19) from feat/get-serializers into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 5m0s
Reviewed-on: #19
2026-03-02 07:31:58 +00:00
Husanjonazamov
1a90803527 hamma modellarda get serizlizers uchun fiedl qoshilfi 2026-03-02 12:31:16 +05:00
github-actions[bot]
8d4eea1dfa 🔄 Update image to 38 [CI SKIP] 2026-02-23 12:43:23 +00:00
cabd159774 Merge pull request 'serach tigurkladfi' (#18) from feat/ad-forms into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m22s
Reviewed-on: #18
2026-02-23 12:41:22 +00:00
Husanjonazamov
e51dd1f952 serach tigurkladfi 2026-02-23 17:40:50 +05:00
github-actions[bot]
a92a2599c7 🔄 Update image to 37 [CI SKIP] 2026-02-18 13:19:30 +00:00
cdf2f40a9c Merge pull request 'admin fayllari to'g'irlandi' (#17) from feat/all-admins into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 3m7s
Reviewed-on: #17
2026-02-18 13:16:45 +00:00
Husanjonazamov
cba724e2d0 admin fayllari to'g'irlandi 2026-02-18 18:16:20 +05:00
github-actions[bot]
cd3037e55f 🔄 Update image to 36 [CI SKIP] 2026-02-18 13:10:40 +00:00
7b05f1f99e Merge pull request 'payment modeli yaratildi' (#16) from feat/payment-model into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 3m52s
Reviewed-on: #16
2026-02-18 13:07:08 +00:00
Husanjonazamov
a6d7fa8c9a payment modeli yaratildi 2026-02-18 18:06:42 +05:00
github-actions[bot]
fbe798043c 🔄 Update image to 35 [CI SKIP] 2026-02-18 13:00:29 +00:00
d4d8c18a2a Merge pull request 'ValuationDocumentModel' (#15) from feat/evaluation-document-model into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 3m18s
Reviewed-on: #15
2026-02-18 12:57:32 +00:00
Husanjonazamov
4680581e8d ValuationDocumentModel
— bu arizaga biriktiriladigan hujjatlar va rasmlar uchun kerak
2026-02-18 17:56:51 +05:00
github-actions[bot]
b3d864d8c1 🔄 Update image to 34 [CI SKIP] 2026-02-18 12:49:24 +00:00
88bcbc07bd Merge pull request 'feat(evaluation): baholash modellari yaratildi (auto, ko'chmas mulk, ko'char mulk, tezkor, hisobot)' (#14) from feat/evaluation-report-model into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m24s
Reviewed-on: #14
2026-02-18 12:47:20 +00:00
Husanjonazamov
4ddd4f138f feat(evaluation): baholash modellari yaratildi (auto, ko'chmas mulk, ko'char mulk, tezkor, hisobot) 2026-02-18 17:44:15 +05:00
github-actions[bot]
3ef3f8fd3f 🔄 Update image to 33 [CI SKIP] 2026-02-13 13:38:28 +00:00
627f7eb9c4 Merge pull request 'QuickEvaluationModel modeli qoshildi shu tezkor baholash uchun ishatiladi' (#13) from feat/evaluation-quick-model into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 3m45s
Reviewed-on: #13
2026-02-13 13:35:04 +00:00
Husanjonazamov
53b0737ee5 QuickEvaluationModel modeli qoshildi shu tezkor baholash uchun ishatiladi 2026-02-13 18:33:56 +05:00
github-actions[bot]
8c7861ca9f 🔄 Update image to 32 [CI SKIP] 2026-02-13 13:01:56 +00:00
97e5850bf7 Merge pull request 'movable-model qoshildi bu (Ko'char mulk) modeli to'liq va optimallashgan holda tayyor!' (#12) from feat/evaluation-movable-model into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 3m51s
Reviewed-on: #12
2026-02-13 12:58:25 +00:00
Husanjonazamov
2cb094cbd3 movable-model qoshildi bu (Ko'char mulk) modeli to'liq va optimallashgan holda tayyor! 2026-02-13 17:57:56 +05:00
github-actions[bot]
9282197c84 🔄 Update image to 31 [CI SKIP] 2026-02-13 12:47:03 +00:00
7d975a0acc Merge pull request 'RealEstateEvaluationModel modeli qoshildi (Ko'chmas mulk (Real Estate) baholash modeli uchun quyidagicha nomlarni tavsiya qilaman:' (#11) from feat/evaluation-real-estate-models into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 3m4s
Reviewed-on: #11
2026-02-13 12:44:18 +00:00
Husanjonazamov
799b5e5cf1 RealEstateEvaluationModel modeli qoshildi (Ko'chmas mulk (Real Estate) baholash modeli uchun quyidagicha nomlarni tavsiya qilaman:
)
2026-02-13 17:43:04 +05:00
github-actions[bot]
e6c64f9621 🔄 Update image to 30 [CI SKIP] 2026-02-13 12:26:10 +00:00
e2e7c23987 Merge pull request 'AutoEvaluationModel qoshildi yani bu valuation bilan vehicle ni asosiy ulab beradigan model' (#10) from feat/evaluation-auto-model into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 3m11s
Reviewed-on: #10
2026-02-13 12:23:18 +00:00
Husanjonazamov
c36a2318a1 AutoEvaluationModel qoshildi yani bu valuation bilan vehicle ni asosiy ulab beradigan model 2026-02-13 17:22:47 +05:00
github-actions[bot]
166bb7240d 🔄 Update image to 29 [CI SKIP] 2026-02-13 11:49:48 +00:00
e074a9abfa Merge pull request '.env.example o'zgartirildi' (#9) from feat/example into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m5s
Reviewed-on: #9
2026-02-13 11:48:03 +00:00
Husanjonazamov
d6299ae378 .env.example o'zgartirildi 2026-02-13 16:47:34 +05:00
github-actions[bot]
1b62e969e2 🔄 Update image to 28 [CI SKIP] 2026-02-13 10:58:33 +00:00
782f14a4a9 Merge pull request 'cicd da deploy yaml togirlandi' (#8) from fix/ci-cd into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 4m55s
Reviewed-on: #8
2026-02-13 10:53:58 +00:00
github-actions[bot]
5158c0ebd0 🔄 Update image to 27 [CI SKIP] 2026-02-13 10:43:17 +00:00
Husanjonazamov
68089a5b6a cicd da deploy yaml togirlandi 2026-02-13 15:42:32 +05:00
88e5ad17bd Merge pull request 'valuation va vihicle modellari qoshildi' (#7) from feat/evaluation-core into main
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 4m56s
Reviewed-on: #7
2026-02-13 10:38:41 +00:00
Husanjonazamov
acf426ee9b valuation va vihicle modellari qoshildi 2026-02-13 15:38:13 +05:00
github-actions[bot]
4bc54f1952 🔄 Update image to 26 [CI SKIP] 2026-02-12 14:36:47 +00:00
Husanjonazamov
93dcaaf511 fix: postgres version 17-alpine
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m53s
2026-02-12 19:34:00 +05:00
github-actions[bot]
2d0c9766a1 🔄 Update image to 25 [CI SKIP] 2026-02-12 14:18:44 +00:00
Husanjonazamov
58399a8f9f fix: deploy yaml GITEATOKEN secret
All checks were successful
Deploy to Production / build-and-deploy (push) Successful in 2m57s
2026-02-12 19:15:56 +05:00
github-actions[bot]
eab5e591ff 🔄 Update image to 24 [CI SKIP] 2026-02-12 14:01:44 +00:00
Husanjonazamov
aca1599d78 cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 4m4s
2026-02-12 18:57:38 +05:00
github-actions[bot]
925e0b0bba 🔄 Update image to 23 [CI SKIP] 2026-02-12 13:46:15 +00:00
Husanjonazamov
ac51403c1d cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m46s
2026-02-12 18:43:29 +05:00
github-actions[bot]
a8503b5e15 🔄 Update image to 22 [CI SKIP] 2026-02-12 13:25:10 +00:00
Husanjonazamov
8600daf8fa cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m48s
2026-02-12 18:22:24 +05:00
github-actions[bot]
09bc83d975 🔄 Update image to 21 [CI SKIP] 2026-02-12 13:16:39 +00:00
Husanjonazamov
7f46c5ac28 cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m52s
2026-02-12 18:13:49 +05:00
github-actions[bot]
4fc5840727 🔄 Update image to 20 [CI SKIP] 2026-02-12 13:08:46 +00:00
Husanjonazamov
33d1a904b9 cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m4s
2026-02-12 18:06:44 +05:00
github-actions[bot]
7e9f61b18f 🔄 Update image to 19 [CI SKIP] 2026-02-12 13:00:39 +00:00
Husanjonazamov
97545c5a46 cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m51s
2026-02-12 17:57:46 +05:00
github-actions[bot]
630c89e790 🔄 Update image to 18 [CI SKIP] 2026-02-12 12:52:18 +00:00
Husanjonazamov
d16b853830 cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 3m39s
2026-02-12 17:48:37 +05:00
github-actions[bot]
8e5ba3786d 🔄 Update image to 17 [CI SKIP] 2026-02-12 12:27:07 +00:00
Husanjonazamov
862eacad0b cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m3s
2026-02-12 17:25:03 +05:00
github-actions[bot]
895aa0d1de 🔄 Update image to 16 [CI SKIP] 2026-02-12 12:20:51 +00:00
Husanjonazamov
5cc54f0530 cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 3m34s
2026-02-12 17:17:20 +05:00
github-actions[bot]
b6b724739a 🔄 Update image to 15 [CI SKIP] 2026-02-12 12:13:36 +00:00
Husanjonazamov
725da2dc36 cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m52s
2026-02-12 17:10:16 +05:00
github-actions[bot]
39946be25e 🔄 Update image to 14 [CI SKIP] 2026-02-12 11:51:21 +00:00
Husanjonazamov
fc60d5ac8f fix: deploy yaml port va evaluation models yangilandi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m1s
2026-02-12 16:48:58 +05:00
github-actions[bot]
31fc8dec9d 🔄 Update image to 13 [CI SKIP] 2026-02-12 11:43:20 +00:00
Husanjonazamov
d9d3f73870 cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m48s
2026-02-12 16:40:27 +05:00
github-actions[bot]
e84250d539 🔄 Update image to 12 [CI SKIP] 2026-02-12 11:36:13 +00:00
Husanjonazamov
aa3137fa4e cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 1m52s
2026-02-12 16:34:18 +05:00
github-actions[bot]
7c0db33485 🔄 Update image to 11 [CI SKIP] 2026-02-12 11:29:47 +00:00
Husanjonazamov
10a8106312 cicd port ozgardi
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m51s
2026-02-12 16:26:53 +05:00
github-actions[bot]
e32f58ca0d 🔄 Update image to 10 [CI SKIP] 2026-02-12 11:17:11 +00:00
b6182b1cf8 Merge pull request 'cicd port ozgardi' (#6) from feat/testv1 into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m50s
Reviewed-on: #6
2026-02-12 11:14:29 +00:00
Husanjonazamov
2d00559656 cicd port ozgardi 2026-02-12 16:13:57 +05:00
github-actions[bot]
4e26fb1d24 🔄 Update image to 9 [CI SKIP] 2026-02-12 10:57:03 +00:00
fc5c8f02b0 Merge pull request 'cicd port ozgardi' (#5) from feat/test into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m7s
Reviewed-on: #5
2026-02-12 10:55:03 +00:00
Husanjonazamov
e304376e99 cicd port ozgardi 2026-02-12 15:54:40 +05:00
github-actions[bot]
6b6b5d6866 🔄 Update image to 8 [CI SKIP] 2026-02-12 10:43:15 +00:00
3f6268c023 Merge pull request 'cicd port ozgardi' (#4) from feat/cicd-deploy into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m4s
Reviewed-on: #4
2026-02-12 10:41:19 +00:00
Husanjonazamov
fe64830a7d cicd port ozgardi 2026-02-12 15:40:52 +05:00
github-actions[bot]
559208b450 🔄 Update image to 7 [CI SKIP] 2026-02-12 10:35:23 +00:00
f885c66cf6 Merge pull request 'evaluation-customer modeli yaratildi va kerakli fiealdlr qoshildi' (#3) from feat/evaluation-customer into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 4m44s
Reviewed-on: #3
2026-02-12 10:30:45 +00:00
Husanjonazamov
b685800087 evaluation-customer modeli yaratildi va kerakli fiealdlr qoshildi 2026-02-12 15:29:58 +05:00
d2c6ecd772 Merge pull request 'userga role qoshildi' (#2) from feat/users into main
Some checks failed
Deploy to Production / build-and-deploy (push) Failing after 2m41s
Reviewed-on: #2
2026-02-12 08:32:44 +00:00
bf335946cd Merge pull request 'evalution app yaratildi' (#1) from feat/evaluation into main
Some checks failed
Deploy to Production / build-and-deploy (push) Has been cancelled
Reviewed-on: #1
2026-02-12 08:32:32 +00:00
Husanjonazamov
6c296857ae evalution app yaratildi 2026-02-12 13:31:49 +05:00
Husanjonazamov
e3a306bdaa userga role qoshildi 2026-02-12 13:23:38 +05:00
355 changed files with 35486 additions and 102 deletions

View File

@@ -50,7 +50,7 @@ SMS_PASSWORD=key
# Addition # Addition
ALLOWED_HOSTS=127.0.0.1,web ALLOWED_HOSTS=127.0.0.1,web
CSRF_TRUSTED_ORIGINS=http://127.0.0.1:8081 CSRF_TRUSTED_ORIGINS=http://127.0.0.1:8081,https://sifatbaho.felixits.uz
OTP_MODULE=core.services.otp OTP_MODULE=core.services.otp
@@ -73,3 +73,9 @@ STORAGE_BUCKET_STATIC=name
STORAGE_PATH=127.0.0.1:8081/bucket/ STORAGE_PATH=127.0.0.1:8081/bucket/
STORAGE_PROTOCOL=http: STORAGE_PROTOCOL=http:
# Didox configs
DIDOX_PARTNER_TOKEN=...
# Celery configs

View File

@@ -92,9 +92,10 @@ jobs:
- name: Update stack.yaml and version - name: Update stack.yaml and version
run: | run: |
sed -i 's|image: ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:.*|image: ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.run_number }}|' stack.yaml sed -i "s|image: .*/${{ env.PROJECT_NAME }}:.*|image: ${{ secrets.DOCKER_USERNAME }}/${{ env.PROJECT_NAME }}:${{ github.run_number }}|g" stack.yaml
sed -i 's/return HttpResponse("OK.*"/return HttpResponse("OK: #${{ github.sha }}"/' config/urls.py sed -i 's/return HttpResponse("OK.*"/return HttpResponse("OK: #${{ github.sha }}"/' config/urls.py
- name: Commit and push updated version - name: Commit and push updated version
run: | run: |
git config user.name "github-actions[bot]" git config user.name "github-actions[bot]"
@@ -112,44 +113,47 @@ jobs:
key: ${{ secrets.KEY }} key: ${{ secrets.KEY }}
port: ${{ secrets.PORT }} port: ${{ secrets.PORT }}
script: | script: |
PROJECTS=/opt/projects/ set -e
DIR=/opt/projects/${{ env.PROJECT_NAME }}/
if [ -d "$PROJECTS" ]; then PROJECTS=/opt/projects
echo "projects papkasi mavjud" DIR=/opt/projects/${{ env.PROJECT_NAME }}
else REPO_URL=https://${{ secrets.GITEATOKEN }}@gitea.felixits.uz/${{ github.repository }}.git
mkdir -p $PROJECTS
echo "projects papkasi yaratildi"
fi
if [ -d "$DIR" ]; then mkdir -p "$PROJECTS"
if [ -d "$DIR/.git" ]; then
echo "loyiha mavjud" echo "loyiha mavjud"
else else
cd $PROJECTS rm -rf "$DIR"
git clone git@gitea.felixits.uz:${{ github.repository }}.git ${{ env.PROJECT_NAME }} cd "$PROJECTS"
echo "Clone qilindi"; git clone "$REPO_URL" "${{ env.PROJECT_NAME }}"
echo "Clone qilindi"
fi fi
cd $DIR cd "$DIR"
git remote set-url origin "$REPO_URL"
git fetch origin main git fetch origin main
git reset --hard origin/main git reset --hard origin/main
cp .env.example .env cp .env.example .env
update_env() { update_env() {
local env_file=".env" local env_file=".env"
cp .env.example "$env_file" for kv in "$@"; do
local key="${kv%%=*}"
for kv in "$@"; do local value="${kv#*=}"
local key="${kv%%=*}" if grep -q "^$key=" "$env_file"; then
local value="${kv#*=}" sed -i "s|^$key=.*|$key=$value|" "$env_file"
sed -i "s|^$key=.*|$key=$value|" "$env_file" else
done echo "$key=$value" >> "$env_file"
fi
done
} }
update_env \ update_env \
"DB_HOST=postgres" \ "DB_HOST=postgres" \
"DB_NAME=sifatbaho" \ "DB_NAME=sifatbahodb" \
"DB_PORT=5432" "DB_PORT=5432" \
"DIDOX_TOKEN=${{ secrets.DIDOX_TOKEN }}"
export PORT=8084 export PORT=8085
docker stack deploy -c stack.yaml ${{ env.PROJECT_NAME }} --with-registry-auth docker stack deploy -c stack.yaml ${{ env.PROJECT_NAME }} --with-registry-auth

2
.gitignore vendored
View File

@@ -2,7 +2,7 @@ node_modules
# OS ignores # OS ignores
*.DS_Store *.DS_Store
#sa
# Byte-compiled / optimized / DLL files # Byte-compiled / optimized / DLL files
__pycache__/ __pycache__/
*.py[cod] *.py[cod]

114
API_ENDPOINTS.md Normal file
View File

@@ -0,0 +1,114 @@
# SifatBaho — GET API Endpoints
> Barcha endpointlar hozirda `AllowAny` permission bilan ishlaydi.
> Token yuborish shart emas (hozircha).
> Base URL: `http://localhost:8000/api/v1/`
---
## 🔐 Auth (Autentifikatsiya)
| # | URL | Method | Tavsif |
|---|-----|--------|--------|
| 1 | `api/v1/auth/token/` | POST | Login — access va refresh token olish |
| 2 | `api/v1/auth/token/refresh/` | POST | Access tokenni yangilash |
| 3 | `api/v1/auth/token/verify/` | POST | Tokenni tekshirish |
---
## 👤 Buyurtmachilar
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 1 | `api/v1/customer/` | GET | ❌ | Barcha buyurtmachilar ro'yxati (ism, INN, JSHSHIR) |
| 2 | `api/v1/customer/{id}/` | GET | ❌ | Bitta buyurtmachining to'liq ma'lumotlari (passport, manzil, bank) |
| 3 | `api/v1/property-owner/` | GET | ❌ | Barcha mulk egalari ro'yxati |
| 4 | `api/v1/property-owner/{id}/` | GET | ❌ | Bitta mulk egasining to'liq ma'lumotlari |
---
## 📋 Arizalar (Valuation)
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 5 | `api/v1/valuation/` | GET | ❌ | Barcha arizalar ro'yxati (raqam, tur, status, narx) |
| 6 | `api/v1/valuation/{id}/` | GET | ❌ | Bitta arizaning to'liq ma'lumotlari (buyurtmachi, baholovchi, narxlar) |
---
## 🚗 Transport vositalari
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 7 | `api/v1/vehicle/` | GET | ❌ | Barcha transportlar ro'yxati (marka, model, raqam, rang) |
| 8 | `api/v1/vehicle/{id}/` | GET | ❌ | Bitta transportning to'liq ma'lumotlari (VIN, tex passport, yurgan km) |
---
## 🔧 Avto baholash
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 9 | `api/v1/auto-evaluation/` | GET | ❌ | Barcha avto baholashlar (ariza + moshina bog'lanishi) |
| 10 | `api/v1/auto-evaluation/{id}/` | GET | ❌ | Bitta avto baholash detali (ariza va moshina to'liq) |
---
## 🏠 Ko'chmas mulk baholash
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 11 | `api/v1/real-estate-evaluation/` | GET | ❌ | Barcha ko'chmas mulk baholashlar (tur, manzil, maydon) |
| 12 | `api/v1/real-estate-evaluation/{id}/` | GET | ❌ | Bitta ko'chmas mulk detali (kadastr, qavat, xonalar) |
---
## 📦 Ko'char mulk baholash
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 13 | `api/v1/movable-property-evaluation/` | GET | ❌ | Barcha ko'char mulk baholashlar (nomi, kategoriya, soni) |
| 14 | `api/v1/movable-property-evaluation/{id}/` | GET | ❌ | Bitta ko'char mulk detali (seriya raqami, holat) |
---
## ⚡ Tezkor baholash
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 15 | `api/v1/quick-evaluation/` | GET | ❌ | Barcha tezkor baholashlar (marka, model, taxminiy narx) |
| 16 | `api/v1/quick-evaluation/{id}/` | GET | ❌ | Bitta tezkor baholash detali (VIN, yoqilg'i turi, holat) |
---
## 📄 Hisobotlar
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 17 | `api/v1/evaluation-report/` | GET | ❌ | Barcha hisobotlar ro'yxati (raqam, baholovchi, yakuniy narx) |
| 18 | `api/v1/evaluation-report/{id}/` | GET | ❌ | Bitta hisobot detali (xulosa matni, PDF fayl) |
---
## 📎 Ariza hujjatlari
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 19 | `api/v1/valuation-document/` | GET | ❌ | Barcha hujjatlar ro'yxati (turi, sarlavha, fayl) |
| 20 | `api/v1/valuation-document/{id}/` | GET | ❌ | Bitta hujjat detali (tavsif, kim yuklagan) |
---
## 💳 To'lovlar
| # | URL | Method | Token | Tavsif |
|---|-----|--------|-------|--------|
| 21 | `api/v1/payment/` | GET | ❌ | Barcha to'lovlar ro'yxati (summa, usul, status) |
| 22 | `api/v1/payment/{id}/` | GET | ❌ | Bitta to'lov detali (tranzaksiya ID, izoh) |
---
**Jami: 22 ta GET endpoint** (11 ta List + 11 ta Detail)
> ⚠️ Hozir barcha endpointlar `AllowAny` — token kerak emas.
> Keyinchalik `IsAuthenticated` ga o'zgartiriladi.

32
MODELS.md Normal file
View File

@@ -0,0 +1,32 @@
# SifatBaho — Modellar Ro'yxati
## Evaluation App — 11 ta model
| # | Model nomi | Fayl | Vazifasi |
|---|-----------|------|----------|
| 1 | `CustomerModel` | `models/customer.py` | Buyurtmachi (jismoniy yoki yuridik shaxs). Ariza beruvchi mijozning shaxsiy ma'lumotlari saqlanadi. |
| 2 | `PropertyOwnerModel` | `models/customer.py` | Mulk egasi. Agar buyurtmachi va mulk egasi boshqa odam bo'lsa, shu model to'ldiriladi. |
| 3 | `ValuationModel` | `models/valuation.py` | Asosiy ariza. Barcha baholash turlari uchun umumiy model: status, narx, buyurtmachi, baholovchi. |
| 4 | `VehicleModel` | `models/vehicle.py` | Transport vositasi. Moshinaning texnik ma'lumotlari: VIN, davlat raqami, marka, model, rang, yurgan masofasi. |
| 5 | `AutoEvaluationModel` | `models/auto.py` | Avto baholash bog'lamasi. Arizani moshinaga bog'laydi (Ariza + Vehicle = AutoEvaluation). |
| 6 | `RealEstateEvaluationModel` | `models/real_estate.py` | Ko'chmas mulk baholash. Uy, xonadon, ofis uchun: maydoni, qavati, kadastr raqami, manzili. |
| 7 | `MovablePropertyEvaluationModel` | `models/movable.py` | Ko'char mulk baholash. Uskunalar, mebellar, stanoklar uchun: nomi, kategoriyasi, seriya raqami, miqdori. |
| 8 | `QuickEvaluationModel` | `models/quick.py` | Tezkor baholash. To'liq ariza yaratmasdan, moshina narxini taxminiy hisoblab beradi (kalkulyator). |
| 9 | `EvaluationReportModel` | `models/report.py` | Yakuniy hisobot. Baholovchi tomonidan tayyorlanadigan rasmiy natija: yakuniy narx, PDF fayl, xulosa matni. |
| 10 | `ValuationDocumentModel` | `models/document.py` | Ariza hujjatlari. Arizaga biriktiriladigan fayllar: passport, tex passport, rasmlar, guvohnomalar. |
## Payment App — 1 ta model
| # | Model nomi | Fayl | Vazifasi |
|---|-----------|------|----------|
| 11 | `PaymentModel` | `models/payment.py` | To'lov. Ariza uchun to'lov ma'lumotlari: summa, to'lov usuli (Click/Payme/naqd), tranzaksiya ID, status. |
## Accounts App — 1 ta model (mavjud edi)
| # | Model nomi | Fayl | Vazifasi |
|---|-----------|------|----------|
| 12 | `User` | `models/user.py` | Foydalanuvchi. Tizimga kirish, rollar (admin, baholovchi, diler, mijoz). |
---
**Jami: 12 ta model** — barchasi TZ asosida yaratildi va ishlamoqda. ✅

View File

@@ -1,12 +1,27 @@
import os import os
from django.core.asgi import get_asgi_application from django.core.asgi import get_asgi_application
asgi_application = get_asgi_application() os.environ.setdefault("DJANGO_SETTINGS_MODULE", "config.settings.local")
from config.env import env # noqa
asgi_application = get_asgi_application()
os.environ.setdefault("DJANGO_SETTINGS_MODULE", env("DJANGO_SETTINGS_MODULE"))
from channels.routing import ProtocolTypeRouter, URLRouter # noqa
from django.urls import re_path # noqa
application = asgi_application
from core.apps.chat.consumers import ChatConsumer # noqa
from core.apps.chat.middlewares.auth import JWTAuthMiddlewareStack # noqa
websocket_urlpatterns = [
re_path(
r"^ws/chat/room/(?P<room_id>\d+)/$",
ChatConsumer.as_asgi(),
),
]
application = ProtocolTypeRouter({
"http": asgi_application,
"websocket": JWTAuthMiddlewareStack(
URLRouter(websocket_urlpatterns)
),
})

View File

@@ -1,3 +1 @@
MODULES = [ MODULES = ["core.apps.shared", "core.apps.evaluation", "core.apps.payment", "core.apps.chat"]
"core.apps.shared",
]

View File

@@ -6,26 +6,185 @@ PAGES = [
"seperator": False, "seperator": False,
"items": [ "items": [
{ {
"title": _("Home page"), "title": _("Bosh sahifa"),
"icon": "home", "icon": "home",
"link": reverse_lazy("admin:index"), "link": reverse_lazy("admin:index"),
} }
], ],
}, },
{ {
"title": _("Auth"), "title": _("Foydalanuvchilar"),
"separator": True, # Top border "separator": True,
"items": [ "items": [
{ {
"title": _("Users"), "title": _("Foydalanuvchilar"),
"icon": "group", "icon": "group",
"link": reverse_lazy("admin:http_user_changelist"), "link": reverse_lazy("admin:accounts_user_changelist"),
}, },
{ {
"title": _("Group"), "title": _("Guruhlar"),
"icon": "group", "icon": "admin_panel_settings",
"link": reverse_lazy("admin:auth_group_changelist"), "link": reverse_lazy("admin:auth_group_changelist"),
}, },
], ],
}, },
{
"title": _("Buyurtmachilar"),
"separator": True,
"items": [
{
"title": _("Buyurtmachilar"),
"icon": "person",
"link": reverse_lazy("admin:evaluation_customermodel_changelist"),
},
{
"title": _("Mulk egalari"),
"icon": "badge",
"link": reverse_lazy("admin:evaluation_propertyownermodel_changelist"),
},
],
},
{
"title": _("Arizalar"),
"separator": True,
"items": [
{
"title": _("Baholash so'rovlari"),
"icon": "rate_review",
"link": reverse_lazy("admin:evaluation_evaluationrequestmodel_changelist"),
},
{
"title": _("Barcha arizalar"),
"icon": "description",
"link": reverse_lazy("admin:evaluation_valuationmodel_changelist"),
},
{
"title": _("Ariza hujjatlari"),
"icon": "attach_file",
"link": reverse_lazy("admin:evaluation_valuationdocumentmodel_changelist"),
},
],
},
{
"title": _("Baholash turlari"),
"separator": True,
"items": [
{
"title": _("Avto baholash"),
"icon": "directions_car",
"link": reverse_lazy("admin:evaluation_autoevaluationmodel_changelist"),
},
{
"title": _("Transport vositalari"),
"icon": "car_repair",
"link": reverse_lazy("admin:evaluation_vehiclemodel_changelist"),
},
{
"title": _("Ko'chmas mulk"),
"icon": "home_work",
"link": reverse_lazy("admin:evaluation_realestateevaluationmodel_changelist"),
},
{
"title": _("Ko'char mulk"),
"icon": "inventory_2",
"link": reverse_lazy("admin:evaluation_movablepropertyevaluationmodel_changelist"),
},
{
"title": _("Tezkor baholash"),
"icon": "bolt",
"link": reverse_lazy("admin:evaluation_quickevaluationmodel_changelist"),
},
],
},
{
"title": _("Natijalar"),
"separator": True,
"items": [
{
"title": _("Hisobotlar"),
"icon": "summarize",
"link": reverse_lazy("admin:evaluation_evaluationreportmodel_changelist"),
},
{
"title": _("To'lovlar"),
"icon": "payments",
"link": reverse_lazy("admin:payment_paymentmodel_changelist"),
},
],
},
{
"title": _("Tarix"),
"separator": True,
"items": [
{
"title": _("Avto baholash tarixi"),
"icon": "history",
"link": reverse_lazy("admin:evaluation_autoevaluationhistorymodel_changelist"),
},
{
"title": _("Tezkor baholash tarixi"),
"icon": "manage_history",
"link": reverse_lazy("admin:evaluation_quickevaluationhistorymodel_changelist"),
},
],
},
{
"title": _("Chat"),
"separator": True,
"items": [
{
"title": _("Chat xabarlari"),
"icon": "chat",
"link": reverse_lazy("admin:chat_chatmessagemodel_changelist"),
},
],
},
{
"title": _("Ma'lumotnomalari"),
"separator": True,
"items": [
{
"title": _("Ma'lumotnomalar"),
"icon": "category",
"link": reverse_lazy("admin:evaluation_referenceitemmodel_changelist"),
},
],
},
{
"title": _("Auto baholash"),
"separator": True,
"items": [
{
"title": _("Hujjatlar"),
"icon": "attach_file",
"link": reverse_lazy("admin:evaluation_documentmodel_changelist"),
},
{
"title": _("Kategoriyalar"),
"icon": "category",
"link": reverse_lazy("admin:evaluation_documentcategorymodel_changelist"),
}
]
},
{
"title": _("Joylashuvlar"),
"separator": True,
"items": [
{
"title": _("Shaharlar"),
"icon": "attach_file",
"link": reverse_lazy("admin:shared_regionmodel_changelist"),
},
{
"title": _("Tumanlar"),
"icon": "attach_file",
"link": reverse_lazy("admin:shared_districtmodel_changelist"),
},
{
"title": _("Mahallalar"),
"icon": "attach_file",
"link": reverse_lazy("admin:shared_villagemodel_changelist"),
},
]
}
] ]

View File

@@ -1,7 +1,11 @@
from typing import Any, Union from typing import Any, Union
REST_FRAMEWORK: Union[Any] = { REST_FRAMEWORK: Union[Any] = {
"DEFAULT_AUTHENTICATION_CLASSES": ("rest_framework_simplejwt.authentication.JWTAuthentication",), "DEFAULT_AUTHENTICATION_CLASSES": (
"rest_framework_simplejwt.authentication.JWTAuthentication",
"rest_framework.authentication.SessionAuthentication",
# "rest_framework.authentication.BaseAuthentication",
),
"DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema", "DEFAULT_SCHEMA_CLASS": "drf_spectacular.openapi.AutoSchema",
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"], "DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
"DEFAULT_PAGINATION_CLASS": "django_core.paginations.CustomPagination", "DEFAULT_PAGINATION_CLASS": "django_core.paginations.CustomPagination",

View File

@@ -90,6 +90,6 @@ UNFOLD = {
"SIDEBAR": { "SIDEBAR": {
"show_search": True, "show_search": True,
"show_all_applications": True, "show_all_applications": True,
# "navigation": navigation.PAGES, "navigation": navigation.PAGES,
}, },
} }

View File

@@ -9,7 +9,7 @@ import environ
environ.Env.read_env(os.path.join(".env")) environ.Env.read_env(os.path.join(".env"))
env = environ.Env( env = environ.Env(
DEBUG=(bool, False), DEBUG=(bool, True),
CACHE_TIME=(int, 180), CACHE_TIME=(int, 180),
OTP_EXPIRE_TIME=(int, 2), OTP_EXPIRE_TIME=(int, 2),
VITE_LIVE=(bool, False), VITE_LIVE=(bool, False),
@@ -26,4 +26,5 @@ env = environ.Env(
OTP_SERVICE="EskizService", OTP_SERVICE="EskizService",
PROJECT_ENV=(str, "prod"), PROJECT_ENV=(str, "prod"),
SILK_ENABLED=(bool, False), SILK_ENABLED=(bool, False),
DIDOX_PARTNER_TOKEN=(str),
) )

View File

@@ -49,6 +49,7 @@ INSTALLED_APPS = [
"django.contrib.sessions", "django.contrib.sessions",
"django.contrib.messages", "django.contrib.messages",
"django.contrib.staticfiles", "django.contrib.staticfiles",
"django.contrib.postgres",
] + APPS ] + APPS
MODULES = [app for app in MODULES if isinstance(app, str)] MODULES = [app for app in MODULES if isinstance(app, str)]
@@ -148,13 +149,25 @@ AUTH_USER_MODEL = "accounts.User"
CELERY_BROKER_URL = env("REDIS_URL") CELERY_BROKER_URL = env("REDIS_URL")
CELERY_RESULT_BACKEND = env("REDIS_URL") CELERY_RESULT_BACKEND = env("REDIS_URL")
CHANNEL_LAYERS = {
"default": {
"BACKEND": "channels_redis.core.RedisChannelLayer",
"CONFIG": {
"hosts": [env("REDIS_URL")],
},
},
}
ALLOWED_HOSTS += env("ALLOWED_HOSTS").split(",") ALLOWED_HOSTS += env("ALLOWED_HOSTS").split(",")
CSRF_TRUSTED_ORIGINS = env("CSRF_TRUSTED_ORIGINS").split(",") CSRF_TRUSTED_ORIGINS = env("CSRF_TRUSTED_ORIGINS").split(",")
SITE_URL = env.str("SITE_URL", default="http://localhost:8055")
MODELTRANSLATION_LANGUAGES = ("uz", "ru", "en") MODELTRANSLATION_LANGUAGES = ("uz", "ru", "en")
MODELTRANSLATION_DEFAULT_LANGUAGE = "uz" MODELTRANSLATION_DEFAULT_LANGUAGE = "uz"
DIDOX_PARTNER_TOKEN = env.str("DIDOX_PARTNER_TOKEN")
JST_LANGUAGES = [ JST_LANGUAGES = [

View File

@@ -2,32 +2,28 @@
All urls configurations tree All urls configurations tree
""" """
from config.env import env
from django.conf import settings from django.conf import settings
from django.contrib import admin from django.contrib import admin
from django.http import HttpResponse from django.http import HttpResponse
from django.urls import include, path, re_path from django.urls import include, path, re_path
from django.views.static import serve from django.views.static import serve
from drf_spectacular.views import (SpectacularAPIView, SpectacularRedocView, from drf_spectacular.views import SpectacularAPIView, SpectacularRedocView, SpectacularSwaggerView
SpectacularSwaggerView)
from config.env import env
def home(request): def home(request):
return HttpResponse("OK") return HttpResponse("OK: #d4e6d80c86fcf4422f71238c6552dfa4b42f9737")
################
# My apps url
################
urlpatterns = [ urlpatterns = [
path("health/", home), path("health/", home),
path("api/v1/", include("core.apps.accounts.urls")), path("api/v1/", include("core.apps.accounts.urls")),
path("api/", include("core.apps.shared.urls")), path("api/", include("core.apps.shared.urls")),
path("api/v1/", include("core.apps.evaluation.urls")),
path("api/v1/", include("core.apps.payment.urls")),
path("api/v1/", include("core.apps.chat.urls")),
] ]
################
# Library urls
################
urlpatterns += [ urlpatterns += [
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
path("accounts/", include("django.contrib.auth.urls")), path("accounts/", include("django.contrib.auth.urls")),
@@ -35,29 +31,15 @@ urlpatterns += [
path("rosetta/", include("rosetta.urls")), path("rosetta/", include("rosetta.urls")),
path("ckeditor5/", include("django_ckeditor_5.urls"), name="ck_editor_5_upload_file"), path("ckeditor5/", include("django_ckeditor_5.urls"), name="ck_editor_5_upload_file"),
] ]
################
# Project env debug mode
################
if env.bool("SILK_ENABLED", False): if env.bool("SILK_ENABLED", False):
urlpatterns += [ urlpatterns += []
]
if env.str("PROJECT_ENV") == "debug": if env.str("PROJECT_ENV") == "debug":
################
# Swagger urls
################
urlpatterns += [ urlpatterns += [
path("schema/", SpectacularAPIView.as_view(), name="schema"), path("schema/", SpectacularAPIView.as_view(), name="schema"),
path("swagger/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"), path("swagger/", SpectacularSwaggerView.as_view(url_name="schema"), name="swagger-ui"),
path("redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"), path("redoc/", SpectacularRedocView.as_view(url_name="schema"), name="redoc"),
] ]
################
# Media urls
################
urlpatterns += [ urlpatterns += [
re_path(r"static/(?P<path>.*)", serve, {"document_root": settings.STATIC_ROOT}), re_path("static/(?P<path>.*)", serve, {"document_root": settings.STATIC_ROOT}),
re_path(r"media/(?P<path>.*)", serve, {"document_root": settings.MEDIA_ROOT}), re_path("media/(?P<path>.*)", serve, {"document_root": settings.MEDIA_ROOT}),
] ]

View File

@@ -14,5 +14,6 @@ admin.site.unregister(db_models.Group)
admin.site.register(db_models.Group, user.GroupAdmin) admin.site.register(db_models.Group, user.GroupAdmin)
admin.site.register(db_models.Permission, user.PermissionAdmin) admin.site.register(db_models.Permission, user.PermissionAdmin)
admin.site.register(get_user_model(), user.CustomUserAdmin) admin.site.register(get_user_model(), user.CustomUserAdmin)
admin.site.register(SmsConfirm, SmsConfirmAdmin) admin.site.register(SmsConfirm, SmsConfirmAdmin)

View File

@@ -1,5 +1,6 @@
from django.contrib.auth import admin from django.contrib.auth import admin
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django.utils.safestring import mark_safe
from unfold.admin import ModelAdmin from unfold.admin import ModelAdmin
from unfold.forms import AdminPasswordChangeForm # UserCreationForm, from unfold.forms import AdminPasswordChangeForm # UserCreationForm,
from unfold.forms import UserChangeForm from unfold.forms import UserChangeForm
@@ -10,13 +11,15 @@ class CustomUserAdmin(admin.UserAdmin, ModelAdmin):
# add_form = UserCreationForm # add_form = UserCreationForm
form = UserChangeForm form = UserChangeForm
list_display = ( list_display = (
"display_avatar",
"first_name", "first_name",
"last_name", "last_name",
"phone", "phone",
"role", "role",
) )
search_fields = ("phone", "first_name", "last_name", "username")
autocomplete_fields = ["groups", "user_permissions"] autocomplete_fields = ["groups", "user_permissions"]
fieldsets = ((None, {"fields": ("phone",)}),) + ( fieldsets = ((None, {"fields": ("phone", "avatar",)}),) + (
(None, {"fields": ("username", "password")}), (None, {"fields": ("username", "password")}),
(_("Personal info"), {"fields": ("first_name", "last_name", "email")}), (_("Personal info"), {"fields": ("first_name", "last_name", "email")}),
( (
@@ -35,9 +38,18 @@ class CustomUserAdmin(admin.UserAdmin, ModelAdmin):
(_("Important dates"), {"fields": ("last_login", "date_joined")}), (_("Important dates"), {"fields": ("last_login", "date_joined")}),
) )
def display_avatar(self, obj):
if obj.avatar:
return mark_safe(
f'<img src="{obj.avatar.url}" width="35" height="35" style="border-radius: 50%; object-fit: cover;" />'
)
return _("No Image")
display_avatar.short_description = _("Avatar")
class PermissionAdmin(ModelAdmin): class PermissionAdmin(ModelAdmin):
list_display = ("name",) list_display = ("name",)
search_fields = ("name",) search_fields = ("name",)

View File

@@ -10,3 +10,6 @@ class RoleChoice(models.TextChoices):
SUPERUSER = "superuser", _("Superuser") SUPERUSER = "superuser", _("Superuser")
ADMIN = "admin", _("Admin") ADMIN = "admin", _("Admin")
USER = "user", _("User") USER = "user", _("User")
EVALUATOR = "evaluator", _("Evaluator")
DEALER = "dealer", _("Dealer")
CLIENT = "client", _("Client")

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2026-02-12 10:18
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='user',
name='role',
field=models.CharField(choices=[('superuser', 'Superuser'), ('admin', 'Admin'), ('user', 'User'), ('evaluator', 'Evaluator'), ('dealer', 'Dealer'), ('client', 'Client')], default='user', max_length=255),
),
]

View File

@@ -0,0 +1,18 @@
# Generated by Django 5.2.7 on 2026-03-17 12:32
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0002_alter_user_role'),
]
operations = [
migrations.AddField(
model_name='user',
name='avatar',
field=models.ImageField(blank=True, null=True, upload_to='avatars/'),
),
]

View File

@@ -16,6 +16,7 @@ class User(auth_models.AbstractUser):
choices=RoleChoice, choices=RoleChoice,
default=RoleChoice.USER, default=RoleChoice.USER,
) )
avatar = models.ImageField(upload_to="avatars/", null=True, blank=True)
USERNAME_FIELD = "phone" USERNAME_FIELD = "phone"
objects = UserManager() objects = UserManager()

View File

@@ -3,21 +3,30 @@ from rest_framework import serializers
class UserSerializer(serializers.ModelSerializer): class UserSerializer(serializers.ModelSerializer):
avatar = serializers.SerializerMethodField(method_name='get_avatar')
class Meta: class Meta:
exclude = [ exclude = [
"created_at", "created_at",
"updated_at", "updated_at",
"password", "password",
"groups", "groups",
"user_permissions" "user_permissions",
] ]
model = get_user_model() model = get_user_model()
def get_avatar(self, obj):
request = self.context.get('request')
if obj.avatar:
return request.build_absolute_uri(obj.avatar.url)
return None
class UserUpdateSerializer(serializers.ModelSerializer): class UserUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = get_user_model() model = get_user_model()
fields = [ fields = [
"first_name", "first_name",
"last_name" "last_name",
"avatar"
] ]

View File

@@ -4,7 +4,7 @@ Accounts app urls
from django.urls import path, include from django.urls import path, include
from rest_framework_simplejwt import views as jwt_views from rest_framework_simplejwt import views as jwt_views
from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView from .views import RegisterView, ResetPasswordView, MeView, ChangePasswordView, UserListApiView, AdminUserListApiView
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
router = DefaultRouter() router = DefaultRouter()
@@ -23,4 +23,6 @@ urlpatterns = [
jwt_views.TokenRefreshView.as_view(), jwt_views.TokenRefreshView.as_view(),
name="token_refresh", name="token_refresh",
), ),
path("user/list/", UserListApiView.as_view(), name="user-list"),
path("admin-user/list/", AdminUserListApiView.as_view(), name="admin-user-list"),
] ]

View File

@@ -1 +1,2 @@
from .auth import * # noqa from .auth import * # noqa
from .user import * # noqa

View File

@@ -6,7 +6,7 @@ from django.contrib.auth import get_user_model
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from django_core import exceptions from django_core import exceptions
from drf_spectacular.utils import extend_schema from drf_spectacular.utils import extend_schema
from rest_framework import status, throttling, request from rest_framework import status, throttling, request, parsers
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.exceptions import PermissionDenied from rest_framework.exceptions import PermissionDenied
from rest_framework.viewsets import GenericViewSet from rest_framework.viewsets import GenericViewSet
@@ -160,6 +160,11 @@ class ResetPasswordView(BaseViewSetMixin, GenericViewSet, UserService):
@extend_schema(tags=["me"]) @extend_schema(tags=["me"])
class MeView(BaseViewSetMixin, GenericViewSet, UserService): class MeView(BaseViewSetMixin, GenericViewSet, UserService):
permission_classes = [IsAuthenticated] permission_classes = [IsAuthenticated]
parser_classes = (
parsers.MultiPartParser,
parsers.FormParser,
parsers.JSONParser,
)
def get_serializer_class(self): def get_serializer_class(self):
match self.action: match self.action:
@@ -172,14 +177,14 @@ class MeView(BaseViewSetMixin, GenericViewSet, UserService):
@action(methods=["GET", "OPTIONS"], detail=False, url_path="me") @action(methods=["GET", "OPTIONS"], detail=False, url_path="me")
def me(self, request): def me(self, request):
return Response(self.get_serializer(request.user).data) return Response(UserSerializer(request.user, context={"request": request}).data)
@action(methods=["PATCH", "PUT"], detail=False, url_path="user-update") @action(methods=["PATCH", "PUT"], detail=False, url_path="user-update")
def user_update(self, request): def user_update(self, request):
ser = self.get_serializer(instance=request.user, data=request.data, partial=True) ser = self.get_serializer(instance=request.user, data=request.data, partial=True, context={"request": request})
ser.is_valid(raise_exception=True) ser.is_valid(raise_exception=True)
ser.save() data = ser.save()
return Response({"detail": _("Malumotlar yangilandi")}) return Response(UserSerializer(data).data)
@extend_schema(tags=["change-password"], description="Parolni o'zgartirish uchun") @extend_schema(tags=["change-password"], description="Parolni o'zgartirish uchun")

View File

@@ -0,0 +1,31 @@
from django.contrib.auth import get_user_model
from rest_framework import generics, filters
from rest_framework.permissions import IsAuthenticated
from drf_spectacular.utils import extend_schema
from core.apps.accounts.serializers.user import UserSerializer
from core.apps.accounts.choices.user import RoleChoice
User = get_user_model()
@extend_schema(tags=['User'])
class UserListApiView(generics.ListAPIView):
queryset = User.objects.filter(role=RoleChoice.USER)
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
filter_backends = [filters.SearchFilter]
search_fields = ['phone', 'first_name', 'last_name']
def serializer_context(self):
return self.serializer_class(context={"request": self.request})
@extend_schema(tags=['User'])
class AdminUserListApiView(generics.ListAPIView):
queryset = User.objects.exclude(role=RoleChoice.USER)
serializer_class = UserSerializer
permission_classes = [IsAuthenticated]
filter_backends = [filters.SearchFilter]
search_fields = ['phone', 'first_name', 'last_name']

View File

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,86 @@
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from unfold.admin import ModelAdmin
from core.apps.chat.models import ChatmessageModel, ChatroomModel
@admin.register(ChatmessageModel)
class ChatmessageAdmin(ModelAdmin):
list_display = (
"id",
"room",
"message_type",
"sender",
"short_text",
"is_read",
"created_at",
)
list_filter = ("message_type", "is_read", "created_at")
search_fields = (
"text",
"sender__phone",
"sender__first_name",
"sender__last_name",
)
readonly_fields = ("created_at",)
autocomplete_fields = ("sender",)
ordering = ("-created_at",)
fieldsets = (
(
_("Xabar"),
{
"fields": ("room", "sender", "message_type", "text", "file", "is_read"),
},
),
(
_("Tizim"),
{
"classes": ("collapse",),
"fields": ("created_at",),
},
),
)
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
@admin.display(description=_("Xabar"))
def short_text(self, obj):
if not obj.text:
return f"[{obj.message_type}]"
return obj.text[:60] + "..." if len(obj.text) > 60 else obj.text
@admin.register(ChatroomModel)
class ChatroomAdmin(ModelAdmin):
list_display = (
"id",
"__str__",
"type",
"auto_evaluation",
"created_at",
)
list_filter = ("type",)
search_fields = ("auto_evaluation__id",)
autocomplete_fields = ("auto_evaluation",)
filter_horizontal = ("members",)
readonly_fields = ("created_at", "updated_at")
fieldsets = (
(
_("Xona"),
{
"fields": ("type", "auto_evaluation", "members"),
},
),
(
_("Tizim"),
{
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
},
),
)

9
core/apps/chat/apps.py Normal file
View File

@@ -0,0 +1,9 @@
from django.apps import AppConfig
class ModuleConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "core.apps.chat"
def ready(self):
import core.apps.chat.signals # noqa

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,15 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class RoomType(models.TextChoices):
AUTO_EVALUATION = "auto_evaluation", _("AutoEvaluation xonasi")
DIRECT = "direct", _("To'g'ridan-to'g'ri")
class MessageType(models.TextChoices):
TEXT = "text", _("Matn")
IMAGE = "image", _("Rasm")
VIDEO = "video", _("Video")
VOICE = "voice", _("Ovoz")
FILE = "file", _("Fayl")

View File

@@ -0,0 +1 @@
from .chat import ChatConsumer # noqa

View File

@@ -0,0 +1,115 @@
import json
from channels.db import database_sync_to_async
from channels.generic.websocket import AsyncWebsocketConsumer
from django.contrib.auth.models import AnonymousUser
def get_base_url(scope):
headers = dict(scope["headers"])
host = headers.get(b"host", b"").decode()
scheme = "https" if scope.get("scheme") == "https" else "http"
return f"{scheme}://{host}"
class ChatConsumer(AsyncWebsocketConsumer):
"""
Xona asosidagi chat consumer.
Ulanish: ws://host/ws/chat/room/{room_id}/?token=<jwt>
Matn xabari yuborish:
{ "message_type": "text", "text": "Salom" }
Fayl xabari (oldin REST orqali yuklab, keyin URL yuborish):
{ "message_type": "image", "file_url": "https://...", "text": "caption (ixtiyoriy)" }
"""
async def connect(self):
user = self.scope.get("user")
if not user or isinstance(user, AnonymousUser):
await self.close(code=4001)
return
self.room_id = self.scope["url_route"]["kwargs"]["room_id"]
self.room_group = f"chat_room_{self.room_id}"
await self.channel_layer.group_add(self.room_group, self.channel_name)
await self.accept()
async def disconnect(self, close_code):
if hasattr(self, "room_group"):
await self.channel_layer.group_discard(self.room_group, self.channel_name)
async def receive(self, text_data):
user = self.scope.get("user")
if not user or isinstance(user, AnonymousUser):
await self.close(code=401)
return
try:
data = json.loads(text_data)
except (json.JSONDecodeError, ValueError):
await self.send(text_data=json.dumps({"error": "Noto'g'ri format."}))
return
message_type = data.get("message_type", "text")
text = (data.get("text") or "").strip()
if message_type == "text" and not text:
await self.send(text_data=json.dumps({"error": "Matn bo'sh bo'lishi mumkin emas."}))
return
if message_type != "text":
await self.send(
text_data=json.dumps(
{"error": "Fayl xabarlarni yuklash uchun REST API dan foydalaning."}
)
)
return
await self._save_message(user, text)
async def chat_message(self, event):
await self.send(
text_data=json.dumps(
{
"id": event["id"],
"message_type": event["message_type"],
"text": event["text"],
"file_url": event["file_url"],
"sender": event["sender"],
"created_at": event["created_at"],
}
)
)
@database_sync_to_async
def _save_message(self, user, text):
from core.apps.chat.models import ChatmessageModel
msg = ChatmessageModel.objects.create(
room_id=self.room_id,
sender=user,
message_type="text",
text=text,
)
full_name = user.get_full_name().strip() or str(user.phone)
base_url = get_base_url(self.scope)
avatar_url = (
base_url + user.avatar.url
if user.avatar else None
)
return {
"id": msg.id,
"message_type": msg.message_type,
"text": msg.text,
"file_url": None,
"sender": {
"id": user.id,
"full_name": full_name,
"role": user.role,
"phone": user.phone,
"avatar": avatar_url,
},
"created_at": msg.created_at.isoformat(),
}

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,33 @@
from django_filters import rest_framework as filters
from core.apps.chat.models import ChatmessageModel, ChatroomModel
class ChatmessageFilter(filters.FilterSet):
room = filters.NumberFilter(field_name="room_id", lookup_expr="exact")
message_type = filters.CharFilter(field_name="message_type", lookup_expr="exact")
is_read = filters.BooleanFilter(field_name="is_read")
created_from = filters.DateTimeFilter(field_name="created_at", lookup_expr="gte")
created_to = filters.DateTimeFilter(field_name="created_at", lookup_expr="lte")
class Meta:
model = ChatmessageModel
fields = [
"room",
"message_type",
"is_read",
"created_from",
"created_to",
]
class ChatroomFilter(filters.FilterSet):
type = filters.CharFilter(field_name="type", lookup_expr="exact")
auto_evaluation = filters.NumberFilter(field_name="auto_evaluation_id", lookup_expr="exact")
class Meta:
model = ChatroomModel
fields = [
"type",
"auto_evaluation",
]

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,17 @@
from django import forms
from core.apps.chat.models import ChatmessageModel, ChatroomModel
class ChatmessageForm(forms.ModelForm):
class Meta:
model = ChatmessageModel
fields = "__all__"
class ChatroomForm(forms.ModelForm):
class Meta:
model = ChatroomModel
fields = "__all__"

View File

@@ -0,0 +1 @@
from .auth import * # noqa

View File

@@ -0,0 +1,70 @@
import traceback
from urllib.parse import parse_qs
from channels.auth import AuthMiddlewareStack
from channels.db import database_sync_to_async
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.auth.models import AnonymousUser
from django.db import close_old_connections
from jwt import DecodeError, ExpiredSignatureError, InvalidSignatureError
from jwt import decode as jwt_decode
User = get_user_model()
class JWTAuthMiddleware:
def __init__(self, app):
self.app = app
async def __call__(self, scope, receive, send):
close_old_connections()
try:
if jwt_token_list := parse_qs(scope["query_string"].decode("utf8")).get(
"token", None
):
jwt_token = jwt_token_list[0]
jwt_payload = self.get_payload(jwt_token)
user_credentials = self.get_user_credentials(jwt_payload)
user = await self.get_logged_in_user(user_credentials)
scope["user"] = user
else:
scope["user"] = AnonymousUser()
except (
InvalidSignatureError,
KeyError,
ExpiredSignatureError,
DecodeError,
):
traceback.print_exc()
except Exception as e:
print(e)
scope["user"] = AnonymousUser()
return await self.app(scope, receive, send)
def get_payload(self, jwt_token):
payload = jwt_decode(jwt_token, settings.SECRET_KEY, algorithms=["HS256"])
return payload
def get_user_credentials(self, payload):
"""
method to get user credentials from jwt token payload.
defaults to user id.
"""
user_id = payload["user_id"]
return user_id
async def get_logged_in_user(self, user_id):
user = await self.get_user(user_id)
return user
@database_sync_to_async
def get_user(self, user_id):
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
return AnonymousUser()
def JWTAuthMiddlewareStack(app):
return JWTAuthMiddleware(AuthMiddlewareStack(app))

View File

@@ -0,0 +1,36 @@
# Generated by Django 5.2.7 on 2026-04-02 14:22
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
('evaluation', '0026_alter_autoevaluationmodel_form_ownership_and_more'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='ChatmessageModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('text', models.TextField(verbose_name='text')),
('is_read', models.BooleanField(db_index=True, default=False, verbose_name='is read')),
('created_at', models.DateTimeField(auto_now_add=True, verbose_name='created at')),
('auto_evaluation', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='chat_messages', to='evaluation.autoevaluationmodel', verbose_name='auto evaluation')),
('sender', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='chat_messages', to=settings.AUTH_USER_MODEL, verbose_name='sender')),
],
options={
'verbose_name': 'Chat Message',
'verbose_name_plural': 'Chat Messages',
'db_table': 'ChatMessage',
'ordering': ['created_at'],
'indexes': [models.Index(fields=['auto_evaluation_id', 'created_at'], name='chat_eval_date_idx'), models.Index(fields=['auto_evaluation_id', 'is_read'], name='chat_eval_read_idx')],
},
),
]

View File

@@ -0,0 +1,152 @@
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
import core.apps.chat.models.chat
def delete_old_messages(apps, schema_editor):
"""Eski xabarlarni o'chirish — room FK bilan mos kelmaydi."""
ChatmessageModel = apps.get_model("chat", "ChatmessageModel")
ChatmessageModel.objects.all().delete()
class Migration(migrations.Migration):
dependencies = [
("chat", "0001_initial_chat_message"),
("evaluation", "0026_alter_autoevaluationmodel_form_ownership_and_more"),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
# 1. ChatroomModel yaratish
migrations.CreateModel(
name="ChatroomModel",
fields=[
("id", models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name="ID")),
("created_at", models.DateTimeField(auto_now_add=True)),
("updated_at", models.DateTimeField(auto_now=True)),
(
"type",
models.CharField(
choices=[
("auto_evaluation", "AutoEvaluation xonasi"),
("direct", "To\u2018g\u2018ridan-to\u2018g\u2018ri"),
],
db_index=True,
default="auto_evaluation",
max_length=20,
verbose_name="type",
),
),
(
"auto_evaluation",
models.OneToOneField(
blank=True,
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="chat_room",
to="evaluation.autoevaluationmodel",
verbose_name="auto evaluation",
),
),
(
"members",
models.ManyToManyField(
blank=True,
related_name="chat_rooms",
to=settings.AUTH_USER_MODEL,
verbose_name="members",
),
),
],
options={
"verbose_name": "Chat Xona",
"verbose_name_plural": "Chat Xonalar",
"db_table": "ChatRoom",
},
),
# 2. Eski xabarlarni o'chirish (room FKsiz migratsiya mumkin emas)
migrations.RunPython(delete_old_messages, migrations.RunPython.noop),
# 3. Eski indexlarni o'chirish
migrations.RemoveIndex(
model_name="chatmessagemodel",
name="chat_eval_date_idx",
),
migrations.RemoveIndex(
model_name="chatmessagemodel",
name="chat_eval_read_idx",
),
# 4. auto_evaluation FK o'chirish
migrations.RemoveField(
model_name="chatmessagemodel",
name="auto_evaluation",
),
# 5. room FK qo'shish
migrations.AddField(
model_name="chatmessagemodel",
name="room",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="messages",
to="chat.chatroommodel",
verbose_name="room",
null=True,
),
),
# 6. room ni non-null qilish
migrations.AlterField(
model_name="chatmessagemodel",
name="room",
field=models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="messages",
to="chat.chatroommodel",
verbose_name="room",
),
),
# 7. message_type qo'shish
migrations.AddField(
model_name="chatmessagemodel",
name="message_type",
field=models.CharField(
choices=[
("text", "Matn"),
("image", "Rasm"),
("video", "Video"),
("voice", "Ovoz"),
("file", "Fayl"),
],
db_index=True,
default="text",
max_length=10,
verbose_name="message type",
),
),
# 8. text ni nullable qilish
migrations.AlterField(
model_name="chatmessagemodel",
name="text",
field=models.TextField(blank=True, null=True, verbose_name="text"),
),
# 9. file field qo'shish
migrations.AddField(
model_name="chatmessagemodel",
name="file",
field=models.FileField(
blank=True,
null=True,
upload_to=core.apps.chat.models.chat.chat_file_upload_path,
verbose_name="file",
),
),
# 10. Yangi indexlar
migrations.AddIndex(
model_name="chatmessagemodel",
index=models.Index(fields=["room_id", "created_at"], name="chat_room_date_idx"),
),
migrations.AddIndex(
model_name="chatmessagemodel",
index=models.Index(fields=["room_id", "is_read"], name="chat_room_read_idx"),
),
]

View File

@@ -0,0 +1,22 @@
# Generated by Django 5.2.7 on 2026-04-03 10:58
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('chat', '0002_chatroom_and_message_media'),
]
operations = [
migrations.AlterModelOptions(
name='chatmessagemodel',
options={'ordering': ['created_at'], 'verbose_name': 'Chat Xabar', 'verbose_name_plural': 'Chat Xabarlar'},
),
migrations.AlterField(
model_name='chatroommodel',
name='type',
field=models.CharField(choices=[('auto_evaluation', 'AutoEvaluation xonasi'), ('direct', "To'g'ridan-to'g'ri")], db_index=True, default='auto_evaluation', max_length=20, verbose_name='type'),
),
]

View File

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,110 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
from django_core.models import AbstractBaseModel
from model_bakery import baker
from core.apps.chat.choices.chat import MessageType, RoomType
def chat_file_upload_path(instance, filename):
return f"chat/{instance.message_type}/{filename}"
class ChatroomModel(AbstractBaseModel):
type = models.CharField(
verbose_name=_("type"),
max_length=20,
choices=RoomType.choices,
default=RoomType.AUTO_EVALUATION,
db_index=True,
)
auto_evaluation = models.OneToOneField(
"evaluation.AutoEvaluationModel",
on_delete=models.CASCADE,
null=True,
blank=True,
related_name="chat_room",
verbose_name=_("auto evaluation"),
)
members = models.ManyToManyField(
"accounts.User",
blank=True,
related_name="chat_rooms",
verbose_name=_("members"),
)
def __str__(self):
if self.type == RoomType.AUTO_EVALUATION and self.auto_evaluation_id:
return f"AutoEval #{self.auto_evaluation_id} xonasi"
return f"Direct xona #{self.pk}"
@classmethod
def _baker(cls):
return baker.make(cls)
class Meta:
db_table = "ChatRoom"
verbose_name = _("Chat Xona")
verbose_name_plural = _("Chat Xonalar")
class ChatmessageModel(models.Model):
room = models.ForeignKey(
ChatroomModel,
on_delete=models.CASCADE,
related_name="messages",
verbose_name=_("room"),
)
sender = models.ForeignKey(
"accounts.User",
on_delete=models.SET_NULL,
null=True,
related_name="chat_messages",
verbose_name=_("sender"),
)
message_type = models.CharField(
verbose_name=_("message type"),
max_length=10,
choices=MessageType.choices,
default=MessageType.TEXT,
db_index=True,
)
text = models.TextField(verbose_name=_("text"), null=True, blank=True)
file = models.FileField(
verbose_name=_("file"),
upload_to=chat_file_upload_path,
null=True,
blank=True,
)
is_read = models.BooleanField(
verbose_name=_("is read"),
default=False,
db_index=True,
)
created_at = models.DateTimeField(
verbose_name=_("created at"),
auto_now_add=True,
)
def __str__(self):
return f"#{self.pk}{self.sender}"
@classmethod
def _baker(cls):
return baker.make(cls)
class Meta:
db_table = "ChatMessage"
verbose_name = _("Chat Xabar")
verbose_name_plural = _("Chat Xabarlar")
ordering = ["created_at"]
indexes = [
models.Index(
fields=["room_id", "created_at"],
name="chat_room_date_idx",
),
models.Index(
fields=["room_id", "is_read"],
name="chat_room_read_idx",
),
]

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,23 @@
from rest_framework import permissions
class ChatmessagePermission(permissions.BasePermission):
def __init__(self) -> None: ...
def __call__(self, *args, **kwargs):
return self
def has_permission(self, request, view):
return True
class ChatroomPermission(permissions.BasePermission):
def __init__(self) -> None: ...
def __call__(self, *args, **kwargs):
return self
def has_permission(self, request, view):
return True

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,83 @@
from rest_framework import serializers
from core.apps.chat.models import ChatmessageModel
from core.apps.chat.tasks.message import send_message_to_chat
class BaseChatmessageSerializer(serializers.ModelSerializer):
sender = serializers.SerializerMethodField()
file_url = serializers.SerializerMethodField()
def get_sender(self, obj):
if obj.sender is None:
return None
full_name = obj.sender.get_full_name().strip()
if not full_name:
full_name = str(obj.sender.phone)
request = self.context.get("request")
return {
"id": obj.sender.id,
"full_name": full_name,
"role": obj.sender.role,
"phone": obj.sender.phone,
"avatar": request.build_absolute_uri(obj.sender.avatar.url) if obj.sender.avatar else None,
}
def get_file_url(self, obj):
if not obj.file:
return None
request = self.context.get("request")
if request:
return request.build_absolute_uri(obj.file.url)
return obj.file.url
class Meta:
model = ChatmessageModel
fields = [
"id",
"room",
"sender",
"message_type",
"text",
"file_url",
"is_read",
"created_at",
]
class ListChatmessageSerializer(BaseChatmessageSerializer):
class Meta(BaseChatmessageSerializer.Meta): ...
class RetrieveChatmessageSerializer(BaseChatmessageSerializer):
class Meta(BaseChatmessageSerializer.Meta): ...
class CreateChatmessageSerializer(serializers.ModelSerializer):
class Meta:
model = ChatmessageModel
fields = [
"room",
"message_type",
"text",
"file",
]
def validate(self, attrs):
message_type = attrs.get("message_type", "text")
text = attrs.get("text", "").strip() if attrs.get("text") else ""
file = attrs.get("file")
if message_type == "text" and not text:
raise serializers.ValidationError({"text": "Matn xabari uchun text majburiy."})
if message_type != "text" and not file:
raise serializers.ValidationError({"file": f"{message_type} xabari uchun fayl majburiy."})
return attrs
def create(self, validated_data):
validated_data["sender"] = self.context["request"].user
request = self.context["request"]
message = super().create(validated_data)
file_url = request.build_absolute_uri(message.file.url) if message.file else None
avatar_url = request.build_absolute_uri(self.context['request'].user.avatar.url) if self.context['request'].user.avatar else None
send_message_to_chat.delay(message.id, file_url, avatar_url)
return message

View File

@@ -0,0 +1,68 @@
from rest_framework import serializers
from core.apps.chat.models import ChatroomModel
class MemberSerializer(serializers.Serializer):
id = serializers.IntegerField()
full_name = serializers.SerializerMethodField()
role = serializers.CharField()
def get_full_name(self, obj):
name = obj.get_full_name().strip()
return name if name else str(obj.phone)
class BaseChatroomSerializer(serializers.ModelSerializer):
members = serializers.SerializerMethodField()
def get_members(self, obj):
return [
{
"id": u.id,
"full_name": (u.get_full_name().strip() or str(u.phone)),
"role": u.role,
}
for u in obj.members.only("id", "first_name", "last_name", "phone", "role")
]
class Meta:
model = ChatroomModel
fields = [
"id",
"type",
"auto_evaluation",
"members",
"created_at",
]
class ListChatroomSerializer(BaseChatroomSerializer):
class Meta(BaseChatroomSerializer.Meta): ...
class RetrieveChatroomSerializer(BaseChatroomSerializer):
class Meta(BaseChatroomSerializer.Meta): ...
class CreateChatroomSerializer(serializers.ModelSerializer):
type = serializers.ChoiceField(
choices=["auto_evaluation", "direct"],
required=True,
)
class Meta:
model = ChatroomModel
fields = [
"type",
"auto_evaluation",
"members",
]
def validate(self, attrs):
room_type = attrs.get("type")
if room_type == "auto_evaluation" and not attrs.get("auto_evaluation"):
raise serializers.ValidationError(
{"auto_evaluation": "auto_evaluation turi uchun AutoEvaluation majburiy."}
)
return attrs

View File

@@ -0,0 +1,2 @@
from .ChatMessage import * # noqa
from .ChatRoom import * # noqa

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,55 @@
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from django.conf import settings
from django.db.models.signals import post_save
from django.dispatch import receiver
from core.apps.chat.models import ChatmessageModel, ChatroomModel
# @receiver(post_save, sender=ChatmessageModel)
# def broadcast_new_message(sender, instance, created, **kwargs):
# """Yangi xabar saqlanganda xonadagi barcha WS ulanishlariga yuboradi."""
# if not created:
# return
# channel_layer = get_channel_layer()
# if channel_layer is None:
# return
# sender_obj = instance.sender
# if sender_obj:
# full_name = sender_obj.get_full_name().strip() or str(sender_obj.phone)
# sender_data = {
# "id": sender_obj.id,
# "full_name": full_name,
# "role": sender_obj.role,
# }
# else:
# sender_data = None
# site_url = getattr(settings, "SITE_URL", "").rstrip("/")
# file_url = (site_url + instance.file.url) if instance.file else None
# async_to_sync(channel_layer.group_send)(
# f"chat_room_{instance.room_id}",
# {
# "type": "chat_message",
# "id": instance.id,
# "message_type": instance.message_type,
# "text": instance.text,
# "file_url": file_url,
# "sender": sender_data,
# "created_at": instance.created_at.isoformat(),
# },
# )
@receiver(post_save, sender="evaluation.AutoEvaluationModel")
def auto_create_chatroom(sender, instance, created, **kwargs):
"""AutoEvaluation yaratilganda avtomatik chat xonasi ochiladi."""
if created:
ChatroomModel.objects.get_or_create(
auto_evaluation=instance,
defaults={"type": "auto_evaluation"},
)

View File

@@ -0,0 +1 @@
from .message import send_message_to_chat

View File

@@ -0,0 +1,44 @@
from asgiref.sync import async_to_sync
from channels.layers import get_channel_layer
from celery import shared_task
from core.apps.chat.models import ChatmessageModel
@shared_task
def send_message_to_chat(message_id, file_url, avatar_url):
try:
message = ChatmessageModel.objects.get(id=message_id)
except ChatmessageModel.DoesNotExist:
return "Not found"
channel_layer = get_channel_layer()
if channel_layer is None:
return
sender_obj = message.sender
if sender_obj:
full_name = sender_obj.get_full_name().strip() or str(sender_obj.phone)
sender_data = {
"id": sender_obj.id,
"full_name": full_name,
"role": sender_obj.role,
"phone": sender_obj.phone,
"avatar": avatar_url,
}
else:
sender_data = None
async_to_sync(channel_layer.group_send)(
f"chat_room_{message.room_id}",
{
"type": "chat_message",
"id": message.id,
"message_type": message.message_type,
"text": message.text,
"file_url": file_url,
"sender": sender_data,
"created_at": message.created_at.isoformat(),
},
)

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,2 @@
from .test_ChatMessage import * # noqa
from .test_ChatRoom import * # noqa

View File

@@ -0,0 +1,112 @@
import pytest
from django.urls import reverse
from rest_framework.test import APIClient
from core.apps.chat.models import ChatmessageModel
@pytest.fixture
def instance(db):
return ChatmessageModel._baker()
@pytest.fixture
def api_client(instance):
from model_bakery import baker
user = instance.sender or baker.make("accounts.User")
if not instance.sender:
instance.sender = user
instance.save()
# ensure the sender is a member of the room (just in case)
if instance.room:
instance.room.members.add(user)
client = APIClient()
client.force_authenticate(user=user)
return client, instance
@pytest.fixture
def data(api_client):
client, instance = api_client
return (
{
"list": reverse("chat-messages-list"),
"retrieve": reverse("chat-messages-detail", kwargs={"pk": instance.pk}),
"retrieve-not-found": reverse("chat-messages-detail", kwargs={"pk": 1000}),
},
client,
instance,
)
@pytest.mark.django_db
def test_list(data):
urls, client, _ = data
response = client.get(urls["list"])
data_resp = response.json()
assert response.status_code == 200
assert data_resp["status"] is True
@pytest.mark.django_db
def test_retrieve(data):
urls, client, _ = data
response = client.get(urls["retrieve"])
data_resp = response.json()
assert response.status_code == 200
assert data_resp["status"] is True
@pytest.mark.django_db
def test_retrieve_not_found(data):
urls, client, _ = data
response = client.get(urls["retrieve-not-found"])
data_resp = response.json()
assert response.status_code == 404
assert data_resp["status"] is False
# @pytest.mark.django_db
# def test_create(data):
# urls, client, _ = data
# response = client.post(urls["list"], data={"name": "test"})
# assert response.json()["status"] is True
# assert response.status_code == 201
# @pytest.mark.django_db
# def test_update(data):
# urls, client, _ = data
# response = client.patch(urls["retrieve"], data={"name": "updated"})
# assert response.json()["status"] is True
# assert response.status_code == 200
#
# # verify updated value
# response = client.get(urls["retrieve"])
# assert response.json()["status"] is True
# assert response.status_code == 200
# assert response.json()["data"]["name"] == "updated"
# @pytest.mark.django_db
# def test_partial_update():
# urls, client, _ = data
# response = client.patch(urls["retrieve"], data={"name": "updated"})
# assert response.json()["status"] is True
# assert response.status_code == 200
#
# # verify updated value
# response = client.get(urls["retrieve"])
# assert response.json()["status"] is True
# assert response.status_code == 200
# assert response.json()["data"]["name"] == "updated"
# @pytest.mark.django_db
# def test_destroy(data):
# urls, client, _ = data
# response = client.delete(urls["retrieve"])
# assert response.status_code == 204

View File

@@ -0,0 +1,105 @@
import pytest
from django.urls import reverse
from rest_framework.test import APIClient
from core.apps.chat.models import ChatroomModel
@pytest.fixture
def instance(db):
return ChatroomModel._baker()
@pytest.fixture
def api_client(instance):
from model_bakery import baker
user = baker.make("accounts.User")
instance.members.add(user)
client = APIClient()
client.force_authenticate(user=user)
return client, instance
@pytest.fixture
def data(api_client):
client, instance = api_client
return (
{
"list": reverse("chat-rooms-list"),
"retrieve": reverse("chat-rooms-detail", kwargs={"pk": instance.pk}),
"retrieve-not-found": reverse("chat-rooms-detail", kwargs={"pk": 1000}),
},
client,
instance,
)
@pytest.mark.django_db
def test_list(data):
urls, client, _ = data
response = client.get(urls["list"])
data_resp = response.json()
assert response.status_code == 200
assert data_resp["status"] is True
@pytest.mark.django_db
def test_retrieve(data):
urls, client, _ = data
response = client.get(urls["retrieve"])
data_resp = response.json()
assert response.status_code == 200
assert data_resp["status"] is True
@pytest.mark.django_db
def test_retrieve_not_found(data):
urls, client, _ = data
response = client.get(urls["retrieve-not-found"])
data_resp = response.json()
assert response.status_code == 404
assert data_resp["status"] is False
# @pytest.mark.django_db
# def test_create(data):
# urls, client, _ = data
# response = client.post(urls["list"], data={"name": "test"})
# assert response.json()["status"] is True
# assert response.status_code == 201
# @pytest.mark.django_db
# def test_update(data):
# urls, client, _ = data
# response = client.patch(urls["retrieve"], data={"name": "updated"})
# assert response.json()["status"] is True
# assert response.status_code == 200
#
# # verify updated value
# response = client.get(urls["retrieve"])
# assert response.json()["status"] is True
# assert response.status_code == 200
# assert response.json()["data"]["name"] == "updated"
# @pytest.mark.django_db
# def test_partial_update():
# urls, client, _ = data
# response = client.patch(urls["retrieve"], data={"name": "updated"})
# assert response.json()["status"] is True
# assert response.status_code == 200
#
# # verify updated value
# response = client.get(urls["retrieve"])
# assert response.json()["status"] is True
# assert response.status_code == 200
# assert response.json()["data"]["name"] == "updated"
# @pytest.mark.django_db
# def test_destroy(data):
# urls, client, _ = data
# response = client.delete(urls["retrieve"])
# assert response.status_code == 204

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,13 @@
from modeltranslation.translator import TranslationOptions, register
from core.apps.chat.models import ChatmessageModel, ChatroomModel
@register(ChatmessageModel)
class ChatmessageTranslation(TranslationOptions):
fields = []
@register(ChatroomModel)
class ChatroomTranslation(TranslationOptions):
fields = []

9
core/apps/chat/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import include, path
from rest_framework.routers import DefaultRouter
from .views import ChatmessageView, ChatRoomView
router = DefaultRouter()
router.register("rooms", ChatRoomView, basename="chat-rooms")
router.register("messages", ChatmessageView, basename="chat-messages")
urlpatterns = [path("", include(router.urls))]

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,15 @@
# from django.core.exceptions import ValidationError
class ChatmessageValidator:
def __init__(self): ...
def __call__(self):
return True
class ChatroomValidator:
def __init__(self): ...
def __call__(self):
return True

View File

@@ -0,0 +1 @@
from .chat import * # noqa

View File

@@ -0,0 +1,172 @@
from django_core.mixins import BaseViewSetMixin
from django_filters.rest_framework import DjangoFilterBackend
from drf_spectacular.utils import OpenApiParameter, extend_schema
from rest_framework.decorators import action
from rest_framework.filters import OrderingFilter
from rest_framework.permissions import IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
from core.apps.chat.filters.chat import ChatmessageFilter, ChatroomFilter
from core.apps.chat.models import ChatmessageModel, ChatroomModel
from core.apps.chat.serializers.chat import (
CreateChatmessageSerializer,
CreateChatroomSerializer,
ListChatmessageSerializer,
ListChatroomSerializer,
RetrieveChatmessageSerializer,
RetrieveChatroomSerializer,
)
@extend_schema(
tags=["Chat"],
parameters=[
OpenApiParameter("room", int, description="Xona ID bo'yicha filter"),
OpenApiParameter("message_type", str, description="Xabar turi: text, image, video, voice, file"),
OpenApiParameter("is_read", bool, description="O'qilgan/o'qilmagan"),
OpenApiParameter("created_from", str, description="Boshlanish sanasi (ISO 8601)"),
OpenApiParameter("created_to", str, description="Tugash sanasi (ISO 8601)"),
],
)
class ChatmessageView(BaseViewSetMixin, ModelViewSet):
permission_classes = [IsAuthenticated]
parser_classes_for_create = None # multipart + json qo'llab-quvvatlanadi
filter_backends = [DjangoFilterBackend, OrderingFilter]
filterset_class = ChatmessageFilter
ordering_fields = ["created_at"]
ordering = ["created_at"]
pagination_class = None
action_permission_classes = {}
action_serializer_class = {
"list": ListChatmessageSerializer,
"retrieve": RetrieveChatmessageSerializer,
"create": CreateChatmessageSerializer,
}
def get_serializer_class(self):
return self.action_serializer_class.get(self.action, ListChatmessageSerializer)
def get_queryset(self):
return (
ChatmessageModel.objects.select_related("sender")
.only(
"id",
"room_id",
"message_type",
"text",
"file",
"is_read",
"created_at",
"sender__id",
"sender__first_name",
"sender__last_name",
"sender__phone",
"sender__role",
)
)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
results = list(queryset)
serializer = self.get_serializer(results, many=True)
return Response(
{
"count": len(results),
"next": None,
"previous": None,
"results": serializer.data,
}
)
@extend_schema(description="Xabarlarni o'qilgan deb belgilash")
@action(detail=False, methods=["post"], url_path="mark-read")
def mark_read(self, request):
"""
Berilgan xona uchun barcha o'qilmagan xabarlarni o'qilgan qiladi.
Body: { "room": <id> }
"""
room_id = request.data.get("room")
if not room_id:
return Response({"detail": "room majburiy."}, status=400)
updated = (
ChatmessageModel.objects.filter(room_id=room_id, is_read=False)
.exclude(sender=request.user)
.update(is_read=True)
)
return Response({"updated": updated})
@extend_schema(
tags=["ChatRoom"],
parameters=[
OpenApiParameter("type", str, description="Xona turi: auto_evaluation, direct"),
OpenApiParameter("auto_evaluation", int, description="AutoEvaluation ID"),
],
)
class ChatRoomView(BaseViewSetMixin, ModelViewSet):
permission_classes = [IsAuthenticated]
filter_backends = [DjangoFilterBackend]
filterset_class = ChatroomFilter
pagination_class = None
action_permission_classes = {}
action_serializer_class = {
"list": ListChatroomSerializer,
"retrieve": RetrieveChatroomSerializer,
"create": CreateChatroomSerializer,
}
def get_serializer_class(self):
return self.action_serializer_class.get(self.action, ListChatroomSerializer)
def get_queryset(self):
return ChatroomModel.objects.prefetch_related("members").only(
"id",
"type",
"auto_evaluation_id",
"created_at",
)
def list(self, request, *args, **kwargs):
queryset = self.filter_queryset(self.get_queryset())
results = list(queryset)
serializer = self.get_serializer(results, many=True)
return Response(
{
"count": len(results),
"next": None,
"previous": None,
"results": serializer.data,
}
)
@extend_schema(
description="Xonaga yangi a'zo qo'shish",
request={"application/json": {"type": "object", "properties": {"user_id": {"type": "integer"}}}},
)
@action(detail=True, methods=["post"], url_path="add-member")
def add_member(self, request, pk=None):
room = self.get_object()
user_id = request.data.get("user_id")
if not user_id:
return Response({"detail": "user_id majburiy."}, status=400)
room.members.add(user_id)
return Response({"detail": "A'zo qo'shildi."})
@extend_schema(
description="Xonadan a'zoni chiqarish",
request={"application/json": {"type": "object", "properties": {"user_id": {"type": "integer"}}}},
)
@action(detail=True, methods=["post"], url_path="remove-member")
def remove_member(self, request, pk=None):
room = self.get_object()
user_id = request.data.get("user_id")
if not user_id:
return Response({"detail": "user_id majburiy."}, status=400)
room.members.remove(user_id)
return Response({"detail": "A'zo chiqarildi."})

View File

View File

@@ -0,0 +1,13 @@
from .auto import * # noqa
from .customer import * # noqa
from .document import * # noqa
from .documentcategory import * # noqa
from .history import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .request import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -0,0 +1,68 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import AutoEvaluationModel
@admin.register(AutoEvaluationModel)
class AutoEvaluationAdmin(ModelAdmin):
list_display = (
"id",
"registration_number",
"object_type",
"car_brand",
"car_model",
"car_number",
"status",
"created_at",
)
list_filter = ("status", "object_type", "rate_type", "value_determined", "object_owner_type")
search_fields = (
"registration_number",
"car_brand",
"car_model",
"car_number",
)
readonly_fields = ("created_at", "updated_at")
autocomplete_fields = ("valuation", "vehicle")
fieldsets = (
("Bog'lanishlar", {
"classes": ("collapse",),
"fields": ("valuation", "vehicle"),
}),
("Step 1 — Umumiy ma'lumotlar", {
"fields": (
"registration_number",
("contract_date", "object_inspection_date"),
("rate_date", "rate_report_date"),
"rate_object_name",
"object_type",
"status",
),
}),
("Step 2 — Shaxs ma'lumotlari", {
"fields": (
"object_owner_type",
("object_owner_individual_person_f_name", "object_owner_individual_person_l_name"),
("object_owner_individual_person_p_name", "object_owner_individual_person_passport_num"),
("object_owner_legal_entity", "object_owner_legal_inn"),
("property_rights", "form_ownership"),
("value_determined", "rate_type"),
),
}),
("Step 3 — Avtomobil ma'lumotlari", {
"fields": (
"tex_passport_serie_num",
("tex_passport_gived_date", "tex_passport_gived_location"),
("car_type", "car_wheel"),
("car_brand", "car_model"),
("car_number", "manufacture_year"),
("car_dvigatel_number", "car_color"),
),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)

View File

@@ -0,0 +1,108 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import CustomerModel, PropertyOwnerModel
@admin.register(CustomerModel)
class CustomerAdmin(ModelAdmin):
list_display = (
"id",
"customer_type",
"first_name",
"last_name",
"jshshir",
"inn",
"org_name",
"created_at",
)
list_filter = ("customer_type",)
search_fields = (
"first_name",
"last_name",
"jshshir",
"passport_number",
"inn",
"org_name",
)
readonly_fields = ("created_at", "updated_at")
fieldsets = (
("Umumiy", {
"fields": ("customer_type",),
}),
("Jismoniy shaxs", {
"fields": (
"jshshir",
("passport_series", "passport_number"),
("first_name", "last_name", "middle_name"),
"address",
("passport_issued_date", "passport_issued_by"),
),
}),
("Yuridik shaxs", {
"fields": (
"inn",
"org_name",
"org_address",
"director_name",
("mfo", "bank_account"),
"certificate_file",
),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)
@admin.register(PropertyOwnerModel)
class PropertyOwnerAdmin(ModelAdmin):
list_display = (
"id",
"owner_type",
"first_name",
"last_name",
"jshshir",
"inn",
"org_name",
"created_at",
)
list_filter = ("owner_type",)
search_fields = (
"first_name",
"last_name",
"jshshir",
"passport_number",
"inn",
"org_name",
)
readonly_fields = ("created_at", "updated_at")
fieldsets = (
("Umumiy", {
"fields": ("owner_type",),
}),
("Jismoniy shaxs", {
"fields": (
"jshshir",
("passport_series", "passport_number"),
("first_name", "last_name", "middle_name"),
"address",
("passport_issued_date", "passport_issued_by"),
),
}),
("Yuridik shaxs", {
"fields": (
"inn",
"org_name",
"org_address",
"director_name",
("mfo", "bank_account"),
"certificate_file",
),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)

View File

@@ -0,0 +1,59 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import DocumentModel, ValuationDocumentModel
@admin.register(ValuationDocumentModel)
class ValuationDocumentAdmin(ModelAdmin):
list_display = (
"id",
"valuation",
"document_type",
"title",
"uploaded_by",
"created_at",
)
list_filter = ("document_type",)
search_fields = (
"title",
"description",
"valuation__conclusion_number",
)
readonly_fields = ("created_at", "updated_at")
autocomplete_fields = ("valuation", "uploaded_by")
fieldsets = (
(
"Hujjat",
{
"fields": (
"valuation",
"document_type",
"title",
"file",
"uploaded_by",
),
},
),
(
"Qo'shimcha",
{
"fields": ("description",),
},
),
(
"Tizim",
{
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
},
),
)
@admin.register(DocumentModel)
class DocumentAdmin(ModelAdmin):
list_display = (
"id",
"__str__",
)

View File

@@ -0,0 +1,57 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import DocumentcategoryModel
@admin.register(DocumentcategoryModel)
class DocumentcategoryAdmin(ModelAdmin):
list_display = (
"id",
"label",
"value",
"created_at",
"updated_at"
)
search_fields = (
"label",
"value",
)
list_filter = (
"created_at",
"updated_at",
)
readonly_fields = (
"created_at",
"updated_at",
)
fieldsets = (
(
("Uzbekcha"),
{
"fields": (
"label_uz",
"value_uz",
)
}
),
(
("Ruscha"),
{
"fields": (
"label_ru",
"value_ru",
)
}
),
(
("Inglizcha"),
{
"fields": (
"label_en",
"value_en",
)
}
),
)
list_display_links =list_display

View File

@@ -0,0 +1,157 @@
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from unfold.admin import ModelAdmin
from core.apps.evaluation.choices.history import EvaluationEventType
from core.apps.evaluation.models import AutoevaluationhistoryModel, QuickevaluationhistoryModel
class BaseHistoryAdmin(ModelAdmin):
"""
History yozuvlari o'zgartirib bo'lmaydi — faqat o'qiladi.
Signallar tomonidan avtomatik yoziladi.
"""
list_filter = (
"event_type",
"actor_role",
"created_at",
)
search_fields = (
"actor_full_name",
"meta",
)
ordering = ("created_at",)
def has_add_permission(self, request):
return False
def has_change_permission(self, request, obj=None):
return False
def has_delete_permission(self, request, obj=None):
return False
@admin.register(AutoevaluationhistoryModel)
class AutoevaluationhistoryAdmin(BaseHistoryAdmin):
list_display = (
"id",
"auto_evaluation",
"event_type_colored",
"actor_full_name",
"actor_role",
"created_at",
)
autocomplete_fields = ("auto_evaluation",)
readonly_fields = (
"auto_evaluation",
"event_type",
"actor_id",
"actor_full_name",
"actor_role",
"meta",
"created_at",
)
fieldsets = (
(_("Baholash"), {
"fields": ("auto_evaluation",),
}),
(_("Hodisa"), {
"fields": (
"event_type",
"meta",
),
}),
(_("Kim bajargan"), {
"fields": (
"actor_id",
"actor_full_name",
"actor_role",
),
}),
(_("Tizim"), {
"classes": ("collapse",),
"fields": ("created_at",),
}),
)
@admin.display(description=_("Hodisa turi"))
def event_type_colored(self, obj):
colors = {
EvaluationEventType.ORDER_CREATED: "#16a34a",
EvaluationEventType.STATUS_CHANGED: "#2563eb",
EvaluationEventType.EVALUATOR_ASSIGNED: "#7c3aed",
EvaluationEventType.DOCUMENT_UPLOADED: "#d97706",
EvaluationEventType.PAYMENT_MADE: "#dc2626",
}
color = colors.get(obj.event_type, "#6b7280")
label = obj.get_event_type_display()
from django.utils.html import format_html
return format_html(
'<span style="color:{}; font-weight:600;">{}</span>',
color,
label,
)
@admin.register(QuickevaluationhistoryModel)
class QuickevaluationhistoryAdmin(BaseHistoryAdmin):
list_display = (
"id",
"quick_evaluation",
"event_type_colored",
"actor_full_name",
"actor_role",
"created_at",
)
autocomplete_fields = ("quick_evaluation",)
readonly_fields = (
"quick_evaluation",
"event_type",
"actor_id",
"actor_full_name",
"actor_role",
"meta",
"created_at",
)
fieldsets = (
(_("Tezkor baholash"), {
"fields": ("quick_evaluation",),
}),
(_("Hodisa"), {
"fields": (
"event_type",
"meta",
),
}),
(_("Kim bajargan"), {
"fields": (
"actor_id",
"actor_full_name",
"actor_role",
),
}),
(_("Tizim"), {
"classes": ("collapse",),
"fields": ("created_at",),
}),
)
@admin.display(description=_("Hodisa turi"))
def event_type_colored(self, obj):
colors = {
EvaluationEventType.ORDER_CREATED: "#16a34a",
EvaluationEventType.STATUS_CHANGED: "#2563eb",
EvaluationEventType.EVALUATOR_ASSIGNED: "#7c3aed",
EvaluationEventType.DOCUMENT_UPLOADED: "#d97706",
EvaluationEventType.PAYMENT_MADE: "#dc2626",
}
color = colors.get(obj.event_type, "#6b7280")
label = obj.get_event_type_display()
from django.utils.html import format_html
return format_html(
'<span style="color:{}; font-weight:600;">{}</span>',
color,
label,
)

View File

@@ -0,0 +1,50 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import MovablePropertyEvaluationModel
@admin.register(MovablePropertyEvaluationModel)
class MovablePropertyEvaluationAdmin(ModelAdmin):
list_display = (
"id",
"valuation",
"property_name",
"property_category",
"serial_number",
"manufacture_year",
"condition",
"quantity",
)
list_filter = (
"property_category",
"condition",
)
search_fields = (
"property_name",
"serial_number",
"valuation__conclusion_number",
)
readonly_fields = ("created_at", "updated_at")
autocomplete_fields = ("valuation",)
fieldsets = (
("Ariza", {
"fields": ("valuation",),
}),
("Mulk ma'lumotlari", {
"fields": (
"property_name",
"property_category",
"serial_number",
"manufacture_year",
"quantity",
),
}),
("Holat", {
"fields": ("condition",),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)

View File

@@ -0,0 +1,67 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import QuickEvaluationModel
@admin.register(QuickEvaluationModel)
class QuickEvaluationAdmin(ModelAdmin):
list_display = (
"id",
"created_by",
"brand",
"marka",
"car_number",
"car_manufactured_date",
"estimated_price",
"status",
"car_type",
"state_car",
"created_at",
)
list_filter = (
"status",
"car_type",
)
search_fields = (
"car_number",
"vin_number",
"engine_number",
"tex_passport_serie_num",
)
readonly_fields = ("created_at", "updated_at")
autocomplete_fields = ("created_by",)
fieldsets = (
("Foydalanuvchi", {
"fields": ("created_by",),
}),
("Tex passport", {
"fields": (
"tex_passport_serie_num",
("tech_passport_issued_date", "tech_passport_issued_place"),
"tex_passport_file",
),
}),
("Transport ma'lumotlari", {
"fields": (
"car_number",
("brand", "marka"),
("car_manufactured_date", "color"),
("vin_number", "engine_number"),
("distance_covered", "car_position"),
),
}),
("Texnik holat", {
"fields": (
("fuel_type", "body_type"),
("car_type", "state_car"),
),
}),
("Natija", {
"fields": ("estimated_price", "status"),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)

View File

@@ -0,0 +1,60 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import RealEstateEvaluationModel
@admin.register(RealEstateEvaluationModel)
class RealEstateEvaluationAdmin(ModelAdmin):
list_display = (
"id",
"valuation",
"property_type",
"address",
"total_area",
"floor",
"build_year",
"condition",
)
list_filter = (
"property_type",
"condition",
"has_renovation",
)
search_fields = (
"address",
"cadastral_number",
"valuation__conclusion_number",
)
readonly_fields = ("created_at", "updated_at")
autocomplete_fields = ("valuation",)
fieldsets = (
("Ariza", {
"fields": ("valuation",),
}),
("Mulk ma'lumotlari", {
"fields": (
"property_type",
"address",
"cadastral_number",
),
}),
("Texnik parametrlar", {
"fields": (
("total_area", "living_area"),
("floor", "total_floors"),
"rooms_count",
"build_year",
),
}),
("Holat", {
"fields": (
"condition",
"has_renovation",
),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)

View File

@@ -0,0 +1,18 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import ReferenceitemModel
@admin.register(ReferenceitemModel)
class ReferenceitemAdmin(ModelAdmin):
list_display = ("id", "type", "name", "parent", "order", "is_active")
list_filter = ("type", "is_active")
search_fields = ("name",)
list_editable = ("order", "is_active")
autocomplete_fields = ("parent",)
fieldsets = (
(None, {
"fields": ("type", "name", "parent", "order", "is_active"),
}),
)

View File

@@ -0,0 +1,50 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import EvaluationReportModel
@admin.register(EvaluationReportModel)
class EvaluationReportAdmin(ModelAdmin):
list_display = (
"id",
"report_number",
"valuation",
"evaluator",
"final_value",
"approved_at",
"created_at",
)
list_filter = ("approved_at",)
search_fields = (
"report_number",
"valuation__conclusion_number",
"evaluator__phone",
"evaluator__first_name",
"conclusion_text",
)
readonly_fields = ("created_at", "updated_at")
autocomplete_fields = ("valuation", "evaluator")
fieldsets = (
("Hisobot", {
"fields": (
"report_number",
"valuation",
"evaluator",
),
}),
("Natija", {
"fields": (
"final_value",
"conclusion_text",
"report_file",
),
}),
("Tasdiqlash", {
"fields": ("approved_at",),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)

View File

@@ -0,0 +1,63 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import EvaluationrequestModel
@admin.register(EvaluationrequestModel)
class EvaluationrequestAdmin(ModelAdmin):
list_display = (
"id",
"user",
"rate_type",
"object_type",
"customer_inn_number",
"owner_inn_number",
"status",
"need_delivering",
"created_at",
)
list_filter = ("status", "rate_type", "object_type", "need_delivering")
search_fields = (
"customer_inn_number",
"owner_inn_number",
"tex_passport",
"user__phone",
)
readonly_fields = ("created_at", "updated_at")
fieldsets = (
("Asosiy", {
"fields": (
"user",
"rate_type",
"object_type",
"status",
),
}),
("Buyurtmachi", {
"fields": (
("customer_inn_number", "owner_inn_number"),
"tex_passport",
),
}),
("Baholash parametrlari", {
"fields": (
("value_determined", "rate_goal"),
("property_rights", "form_ownership"),
),
}),
("Qo'shimcha (truck_car)", {
"classes": ("collapse",),
"fields": ("worked_hours", "chassi"),
}),
("Yetkazish", {
"fields": (
"need_delivering",
("location_lat", "location_lng"),
),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)

View File

@@ -0,0 +1,73 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import ValuationModel
@admin.register(ValuationModel)
class ValuationAdmin(ModelAdmin):
list_display = (
"id",
"conclusion_number",
"evaluation_type",
"status",
"customer",
"assigned_to",
"estimated_price",
"final_price",
"payment_status",
"created_at",
)
list_filter = (
"evaluation_type",
"status",
"payment_status",
"evaluation_purpose",
"is_courier_delivery",
)
search_fields = (
"conclusion_number",
"customer__first_name",
"customer__last_name",
"customer__org_name",
"customer__inn",
)
readonly_fields = ("created_at", "updated_at")
autocomplete_fields = ("customer", "property_owner", "created_by", "assigned_to")
fieldsets = (
("Asosiy ma'lumotlar", {
"fields": (
"conclusion_number",
("evaluation_type", "evaluation_subtype"),
"evaluation_purpose",
"status",
),
}),
("Bog'lanishlar", {
"fields": (
"customer",
"property_owner",
"created_by",
"assigned_to",
),
}),
("Narx va To'lov", {
"fields": (
("estimated_price", "final_price"),
"payment_status",
),
}),
("Yetkazib berish", {
"fields": (
"is_courier_delivery",
"courier_extra_amount",
),
}),
("Qo'shimcha", {
"fields": ("notes",),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)

View File

@@ -0,0 +1,61 @@
from django.contrib import admin
from unfold.admin import ModelAdmin
from core.apps.evaluation.models import VehicleModel
@admin.register(VehicleModel)
class VehicleAdmin(ModelAdmin):
list_display = (
"id",
"brand",
"model",
"license_plate",
"manufacture_year",
"color",
"fuel_type",
"condition",
"mileage",
)
list_filter = (
"condition",
"manufacture_year",
)
search_fields = (
"brand__name",
"model__name",
"license_plate",
"vin_number",
"engine_number",
"tech_passport_number",
)
readonly_fields = ("created_at", "updated_at")
autocomplete_fields = ("brand", "model", "color", "fuel_type", "body_type", "position")
fieldsets = (
("Texnik passport", {
"fields": (
("tech_passport_series", "tech_passport_number"),
("tech_passport_issued_date", "tech_passport_issued_by"),
),
}),
("Transport ma'lumotlari", {
"fields": (
("brand", "model"),
"license_plate",
("manufacture_year", "color"),
("vin_number", "engine_number"),
"position",
),
}),
("Texnik holat", {
"fields": (
("fuel_type", "body_type"),
"condition",
"mileage",
),
}),
("Tizim", {
"classes": ("collapse",),
"fields": ("created_at", "updated_at"),
}),
)

View File

@@ -0,0 +1,9 @@
from django.apps import AppConfig
class ModuleConfig(AppConfig):
default_auto_field = "django.db.models.BigAutoField"
name = "core.apps.evaluation"
def ready(self):
import core.apps.evaluation.signals # noqa

View File

@@ -0,0 +1,48 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class AutoObjectType(models.TextChoices):
LIGHTWEIGHT_AUTO = "lightweight_auto", _("Yengil automobil")
TRUCK_CAR = "truck_car", _("Yuk automobil")
SPECIAL_TECH = "special_tech", _("Maxsus texnika")
class AutoEvaluationStatus(models.TextChoices):
CREATED = "yaratildi", _("Yaratildi")
EVALUATOR_ASSIGNED = "baxolovchi_biriktirildi", _("Baholovchi biriktirildi")
EVALUATED = "baxolandi", _("Baholandi")
REJECTED = "rad_etildi", _("Rad etildi")
APPROVED = "tasdiqlandi", _("Tasdiqlandi")
class ObjectOwnerType(models.IntegerChoices):
INDIVIDUAL = 1, _("Jismoniy shaxs")
LEGAL = 2, _("Yuridik shaxs")
class RateType(models.IntegerChoices):
CREDIT_COLLATERAL = 1, _("Kredit ta'minoti sifatida garovga qo'yish")
SALE_PURPOSE = 2, _("Sotish maqsadida bozor qiymatini aniqlash")
TAX_PURPOSE = 3, _("Soliqqa tortish maqsadida")
OTHER = 4, _("Boshqa")
class LocationHighways(models.IntegerChoices):
CENTER = 1, _("Tuman/Shahar markazi")
FAR_FROM_CENTER = 2, _("Tuman/shahar markazidan uzoqda")
class LocationConvenience(models.IntegerChoices):
POPULATED_AREA = 1, _("Aholi gavjum hudud")
MARKET_AREA = 2, _("Bozor hududi")
class AutoCarType(models.IntegerChoices):
HATCHBACK = 1, _("Xetchbek")
UNIVERSAL = 2, _("Universal")
class AutoCarWheel(models.IntegerChoices):
FOUR_BY_FOUR = 1, _("4x4")

View File

@@ -0,0 +1,13 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class DocumentType(models.TextChoices):
CERTIFICATE = "certificate", _("Certificate")
PASSPORT = "passport", _("Passport")
TECH_PASSPORT = "tech_passport", _("Tech Passport")
CADASTRAL = "cadastral", _("Cadastral Document")
PHOTO = "photo", _("Photo")
CONTRACT = "contract", _("Contract")
POWER_OF_ATTORNEY = "power_of_attorney", _("Power of Attorney")
OTHER = "other", _("Other")

View File

@@ -0,0 +1,10 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class EvaluationEventType(models.TextChoices):
ORDER_CREATED = "order_created", _("Buyurtma yaratildi")
STATUS_CHANGED = "status_changed", _("Status o'zgartirildi")
EVALUATOR_ASSIGNED = "evaluator_assigned", _("Baholovchi biriktirildi")
DOCUMENT_UPLOADED = "document_uploaded", _("Hujjat yuklandi")
PAYMENT_MADE = "payment_made", _("To'lov qilindi")

View File

@@ -0,0 +1,19 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class MovablePropertyCategory(models.TextChoices):
EQUIPMENT = "equipment", _("Equipment")
MACHINERY = "machinery", _("Machinery")
FURNITURE = "furniture", _("Furniture")
ELECTRONICS = "electronics", _("Electronics")
COMMODITY = "commodity", _("Commodity/Goods")
OTHER = "other", _("Other")
class MovablePropertyCondition(models.TextChoices):
NEW = "new", _("New")
GOOD = "good", _("Good")
AVERAGE = "average", _("Average")
POOR = "poor", _("Poor/Needs repair")
SCRAP = "scrap", _("Scrap")

View File

@@ -0,0 +1,17 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class QuickEvaluationStatus(models.TextChoices):
CREATED = "created", _("Created")
EVALUATOR_ASSIGNED = "evaluator_assigned", _("Evaluator assigned")
EVALUATED = "evaluated", _("Evaluated")
REJECTED = "rejected", _("Rejected")
APPROVED = "approved", _("Approved")
class CarType(models.TextChoices):
LIGHTWEIGHT = "lightweight", _("Lightweight")
TRUCK = "truck", _("Truck")
BUS = "bus", _("Bus")
MOTO = "moto", _("Moto")

View File

@@ -0,0 +1,18 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class PropertyType(models.TextChoices):
APARTMENT = "apartment", _("Apartment")
HOUSE = "house", _("House")
OFFICE = "office", _("Office")
LAND = "land", _("Land")
COMMERCIAL = "commercial", _("Commercial")
INDUSTRIAL = "industrial", _("Industrial")
class RealEstateCondition(models.TextChoices):
NEW = "new", _("New (Rough finish)")
FINISHED = "finished", _("Finished (Standard/Euro)")
REPAIR_REQUIRED = "repair_required", _("Needs repair")
UNDER_CONSTRUCTION = "under_construction", _("Under construction")

View File

@@ -0,0 +1,16 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class ReferenceType(models.TextChoices):
BRAND = "brand", _("Brand")
MARKA = "marka", _("Marka")
COLOR = "color", _("Color")
FUEL_TYPE = "fuel_type", _("Fuel type")
BODY_TYPE = "body_type", _("Body type")
CAR_POSITION = "car_position", _("Car position")
STATE_CAR = "state_car", _("Car state")
EVALUATION_PURPOSE = "evaluation_purpose", _("Evaluation purpose")
DETERMINED_VALUE = "determined_value", _("Determined value type")
PROPERTY_RIGHTS = "property_rights", _("Property rights")
OWNERSHIP_FORM = "ownership_form", _("Ownership form")

View File

@@ -0,0 +1,21 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class EvaluationRateType(models.TextChoices):
AUTO = "auto", _("Automobil")
REAL_ESTATE = "real_estate", _("Ko'chmas mulk")
EQUIPMENT = "equipment", _("Uskuna")
class RequestObjectType(models.TextChoices):
LIGHTWEIGHT_AUTO = "lightweight_auto", _("Yengil automobil")
TRUCK_CAR = "truck_car", _("Yuk automobil")
SPECIAL_TECH = "special_tech", _("Maxsus texnika")
class RequestStatus(models.TextChoices):
PENDING = "pending", _("Kutilmoqda")
IN_PROGRESS = "in_progress", _("Jarayonda")
COMPLETED = "completed", _("Bajarildi")
REJECTED = "rejected", _("Rad etildi")

View File

@@ -0,0 +1,33 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class EvaluationPurpose(models.TextChoices):
SALE = "sale", _("Sale")
BANK = "bank", _("Bank/Loan")
INSURANCE = "insurance", _("Insurance")
INHERITANCE = "inheritance", _("Inheritance")
COURT = "court", _("Court/Judicial")
OTHER = "other", _("Other")
class EvaluationType(models.TextChoices):
AUTO = "auto", _("Auto")
REAL_ESTATE = "real_estate", _("Real Estate")
MOVABLE_PROPERTY = "movable_property", _("Movable Property")
class ValuationStatus(models.TextChoices):
DRAFT = "draft", _("Draft")
PENDING = "pending", _("Pending")
IN_REVIEW = "in_review", _("In Review")
APPROVED = "approved", _("Approved")
REJECTED = "rejected", _("Rejected")
PAID = "paid", _("Paid")
COMPLETED = "completed", _("Completed")
class PaymentStatus(models.TextChoices):
UNPAID = "unpaid", _("Unpaid")
PENDING = "pending", _("Pending")
PAID = "paid", _("Paid")

View File

@@ -0,0 +1,28 @@
from django.db import models
from django.utils.translation import gettext_lazy as _
class FuelType(models.TextChoices):
PETROL = "petrol", _("Petrol")
DIESEL = "diesel", _("Diesel")
GAS = "gas", _("Gas")
ELECTRIC = "electric", _("Electric")
HYBRID = "hybrid", _("Hybrid")
class BodyType(models.TextChoices):
HATCHBACK = "hatchback", _("Hatchback")
SEDAN = "sedan", _("Sedan")
UNIVERSAL = "universal", _("Universal")
COUPE = "coupe", _("Coupe")
CABRIOLET = "cabriolet", _("Cabriolet")
LIFTBACK = "liftback", _("Liftback")
MINIVAN = "minivan", _("Minivan")
CROSSOVER = "crossover", _("Crossover")
class VehicleCondition(models.TextChoices):
EXCELLENT = "excellent", _("Excellent")
GOOD = "good", _("Good")
AVERAGE = "average", _("Average")
NEEDS_REPAIR = "needs_repair", _("Needs repair")

View File

View File

@@ -0,0 +1,13 @@
from .auto import * # noqa
from .customer import * # noqa
from .document import * # noqa
from .documentcategory import * # noqa
from .history import * # noqa
from .movable import * # noqa
from .quick import * # noqa
from .real_estate import * # noqa
from .reference import * # noqa
from .report import * # noqa
from .request import * # noqa
from .valuation import * # noqa
from .vehicle import * # noqa

View File

@@ -0,0 +1,45 @@
from django_filters import rest_framework as filters
from core.apps.evaluation.models import AutoEvaluationModel
class AutoevaluationFilter(filters.FilterSet):
status = filters.CharFilter(method="filter_status")
object_type = filters.CharFilter(field_name="object_type", lookup_expr="exact")
object_owner_type = filters.NumberFilter(field_name="object_owner_type", lookup_expr="exact")
rate_type = filters.NumberFilter(field_name="rate_type", lookup_expr="exact")
value_determined = filters.NumberFilter(field_name="value_determined", lookup_expr="exact")
property_rights = filters.NumberFilter(field_name="property_rights", lookup_expr="exact")
form_ownership = filters.NumberFilter(field_name="form_ownership", lookup_expr="exact")
object_location_province = filters.CharFilter(
field_name="object_location_province", lookup_expr="icontains"
)
client = filters.NumberFilter(field_name="valuation__customer", lookup_expr="exact")
created_from = filters.DateFilter(field_name="created_at", lookup_expr="gte")
created_to = filters.DateFilter(field_name="created_at", lookup_expr="lte")
rate_date_from = filters.DateFilter(field_name="rate_date", lookup_expr="gte")
rate_date_to = filters.DateFilter(field_name="rate_date", lookup_expr="lte")
def filter_status(self, queryset, name, value):
if value:
statuses = [s.strip() for s in value.split(",") if s.strip()]
return queryset.filter(status__in=statuses)
return queryset
class Meta:
model = AutoEvaluationModel
fields = [
"status",
"object_type",
"object_owner_type",
"rate_type",
"value_determined",
"property_rights",
"form_ownership",
"object_location_province",
"client",
"created_from",
"created_to",
"rate_date_from",
"rate_date_to",
]

View File

@@ -0,0 +1,18 @@
from django_filters import rest_framework as filters
from core.apps.evaluation.models import CustomerModel, PropertyOwnerModel
class CustomerFilter(filters.FilterSet):
class Meta:
model = CustomerModel
fields = []
class PropertyOwnerFilter(filters.FilterSet):
class Meta:
model = PropertyOwnerModel
fields = []

View File

@@ -0,0 +1,18 @@
from django_filters import rest_framework as filters
from core.apps.evaluation.models import DocumentModel, ValuationDocumentModel
class ValuationdocumentFilter(filters.FilterSet):
class Meta:
model = ValuationDocumentModel
fields = []
class DocumentFilter(filters.FilterSet):
class Meta:
model = DocumentModel
fields = [
]

View File

@@ -0,0 +1,10 @@
from django_filters import rest_framework as filters
from core.apps.evaluation.models import DocumentcategoryModel
class DocumentcategoryFilter(filters.FilterSet):
class Meta:
model = DocumentcategoryModel
fields = [
]

View File

@@ -0,0 +1,54 @@
from django_filters import rest_framework as filters
from core.apps.evaluation.choices.history import EvaluationEventType
from core.apps.evaluation.models import AutoevaluationhistoryModel, QuickevaluationhistoryModel
class AutoevaluationhistoryFilter(filters.FilterSet):
auto_evaluation = filters.NumberFilter(
field_name="auto_evaluation", lookup_expr="exact"
)
event_type = filters.ChoiceFilter(
field_name="event_type",
choices=EvaluationEventType.choices,
)
created_from = filters.DateTimeFilter(
field_name="created_at", lookup_expr="gte"
)
created_to = filters.DateTimeFilter(
field_name="created_at", lookup_expr="lte"
)
class Meta:
model = AutoevaluationhistoryModel
fields = [
"auto_evaluation",
"event_type",
"created_from",
"created_to",
]
class QuickevaluationhistoryFilter(filters.FilterSet):
quick_evaluation = filters.NumberFilter(
field_name="quick_evaluation", lookup_expr="exact"
)
event_type = filters.ChoiceFilter(
field_name="event_type",
choices=EvaluationEventType.choices,
)
created_from = filters.DateTimeFilter(
field_name="created_at", lookup_expr="gte"
)
created_to = filters.DateTimeFilter(
field_name="created_at", lookup_expr="lte"
)
class Meta:
model = QuickevaluationhistoryModel
fields = [
"quick_evaluation",
"event_type",
"created_from",
"created_to",
]

View File

@@ -0,0 +1,13 @@
# from django_filters import rest_framework as filters
# from core.apps.evaluation.models import MovablePropertyEvaluationModel
# class MovablepropertyevaluationFilter(filters.FilterSet):
# # name = filters.CharFilter(field_name="name", lookup_expr="icontains")
# class Meta:
# model = MovablePropertyEvaluationModel
# fields = [
# "name",
# ]

View File

@@ -0,0 +1,31 @@
from django_filters import rest_framework as filters
from core.apps.evaluation.models import QuickEvaluationModel
class QuickevaluationFilter(filters.FilterSet):
status = filters.CharFilter(method="filter_status")
car_type = filters.CharFilter(field_name="car_type", lookup_expr="exact")
state_car = filters.NumberFilter(field_name="state_car", lookup_expr="exact")
created_from = filters.DateFilter(field_name="created_at", lookup_expr="gte")
created_to = filters.DateFilter(field_name="created_at", lookup_expr="lte")
year_from = filters.NumberFilter(field_name="car_manufactured_date", lookup_expr="gte")
year_to = filters.NumberFilter(field_name="car_manufactured_date", lookup_expr="lte")
def filter_status(self, queryset, name, value):
if value:
statuses = [s.strip() for s in value.split(",") if s.strip()]
return queryset.filter(status__in=statuses)
return queryset
class Meta:
model = QuickEvaluationModel
fields = [
"status",
"car_type",
"state_car",
"created_from",
"created_to",
"year_from",
"year_to",
]

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