1238 Commits

Author SHA1 Message Date
Philip
15c9d589fe serializer 2024-03-05 20:06:38 +03:00
Philip
fa116c08c1 fix task 2024-03-05 13:47:36 +03:00
Philip
4f9129e718 sample 2024-02-28 14:10:20 +03:00
Philip
df45f3a704 not found 2024-02-25 13:41:26 +03:00
Philip
90ef093fca supscriptions 2024-02-24 19:54:57 +03:00
4331b26542 0.1.362 fix language bug redirect to main page 2024-02-22 18:54:15 +03:00
SDE
dd8a8ddd53 0.12.26 get_phone_valid_error change 2024-02-21 17:12:45 +03:00
SBD
abb0d488eb 14 2024-02-21 15:10:34 +03:00
SBD
be97b848d0 14 2024-02-21 14:56:25 +03:00
SBD
6bc112d689 13 2024-02-21 14:37:31 +03:00
SBD
dc534a0c51 12 2024-02-21 14:21:48 +03:00
SDE
e71ed05e6c 0.12.25 timezone in chat messages 2024-02-21 14:12:03 +03:00
SBD
2048ed6baf 11 2024-02-20 19:31:10 +03:00
SBD
c146bd6155 10 2024-02-20 18:57:17 +03:00
SBD
98665abf4d 9 2024-02-20 18:27:36 +03:00
SBD
9954140d3a 9 2024-02-20 17:43:08 +03:00
SBD
fafee63a64 7 2024-02-20 17:35:01 +03:00
c623d69767 0.1.361 comment red marker on mobile_header temp 2024-02-12 17:22:24 +03:00
670eb28bc0 0.1.360 add red marker on mobile_header temp 2024-02-12 17:17:21 +03:00
144ff286f6 0.1.359 add red marker on mobile_header temp 2024-02-12 17:12:44 +03:00
05f94de0b4 Merge remote-tracking branch 'origin/main' 2024-02-12 16:09:24 +03:00
c4650ce603 0.1.358 upd profile_button width 2024-02-12 16:09:13 +03:00
SDE
3971a8ee23 0.12.24 timezone in chat messages 2024-02-09 20:33:04 +03:00
SDE
a3ba6bc783 0.12.23 timezone in chat messages 2024-02-09 18:18:21 +03:00
SDE
b87df02714 0.12.22 timezone in chat messages 2024-02-09 18:01:11 +03:00
SDE
713695cf7d 0.12.21 timezone in chat messages 2024-02-09 17:00:28 +03:00
SDE
a25f30eda7 0.12.20 timezone in chat messages 2024-02-09 16:57:27 +03:00
SDE
eddb3a1858 0.12.19 timezone in chat messages 2024-02-09 15:12:10 +03:00
SDE
5a89200f1d Merge remote-tracking branch 'origin/main' 2024-02-08 14:51:27 +03:00
SDE
2f47a9e3db 0.12.18 registration mail to user 2024-02-08 14:51:19 +03:00
155b6272ec 0.1.357 upd mobile_styles.css for menu_profile 2024-02-08 14:20:50 +03:00
SDE
1479584bfc 0.12.17 change_avatar_confirm_ajax RequestDataTooBig 2024-02-05 23:02:11 +03:00
SDE
f86be5bd97 Merge remote-tracking branch 'origin/main' 2024-02-05 22:41:58 +03:00
SDE
c87a7095ad 0.12.16 change_avatar_confirm_ajax RequestDataTooBig 2024-02-05 22:41:50 +03:00
ac4df7a5f7 0.1.356 upd text_block 2024-02-05 14:34:15 +03:00
SDE
c48839ff8c 0.12.15 subscribe mailing 2024-02-02 18:50:51 +03:00
SDE
ea296e3f05 0.12.14 subscribe mailing 2024-02-02 18:15:51 +03:00
SDE
8124ed62fe 0.12.13 subscribe mailing 2024-02-02 18:08:28 +03:00
SDE
cc643d2641 0.12.12 subscribe mailing 2024-02-02 18:06:35 +03:00
SDE
6de42c5ba9 Merge remote-tracking branch 'origin/main' 2024-02-02 17:44:50 +03:00
SDE
36b7f4dee7 0.12.11 subscribe mailing 2024-02-02 17:44:43 +03:00
2328f09023 0.1.355 mailingSubscribeRequired functional UPD 2024-02-02 17:41:01 +03:00
60faeeace9 Merge remote-tracking branch 'origin/main' 2024-02-02 17:37:35 +03:00
abe53dd88b 0.1.354 mailingSubscribeRequired functional 2024-02-02 17:37:25 +03:00
SDE
c76a18c5ff Merge remote-tracking branch 'origin/main' 2024-02-02 15:51:45 +03:00
SDE
3725ce1882 0.12.10 routes order 2024-02-02 15:51:37 +03:00
6a69ff02b1 0.1.353 add font-display for lcp test 2024-02-02 15:04:30 +03:00
6ba41af305 0.1.352 small bug_fix 2024-02-02 12:50:59 +03:00
SDE
5815a08b55 0.12.9 subscribe mailing 2024-02-01 19:13:26 +03:00
SDE
7805161829 0.12.8 subscribe mailing 2024-02-01 19:07:48 +03:00
6fa31f3866 Merge remote-tracking branch 'origin/main' 2024-02-01 18:51:01 +03:00
0763faf224 0.1.351 upd footer 2024-02-01 18:50:28 +03:00
SDE
a3d3e12467 0.12.7 subscribe mailing 2024-02-01 18:43:16 +03:00
SDE
ab22a3ec88 0.12.6 subscribe mailing 2024-02-01 18:32:29 +03:00
SDE
9d0a059909 0.12.5 subscribe mailing 2024-02-01 18:24:02 +03:00
SDE
1011f112b2 0.12.4 subscribe mailing 2024-02-01 17:49:57 +03:00
SDE
d07ab2c71f Merge remote-tracking branch 'origin/main' 2024-02-01 17:46:55 +03:00
SDE
96bfef04a8 0.12.3 subscribe mailing 2024-02-01 17:46:44 +03:00
SBD
779dd7e93d 7 2024-02-01 15:06:05 +03:00
SBD
0abcb34829 7 2024-02-01 14:58:26 +03:00
SBD
33c028207a 6 2024-02-01 14:36:08 +03:00
SBD
64b3e40ed0 6 2024-02-01 14:14:35 +03:00
SBD
0023676b28 Merge remote-tracking branch 'origin/main' 2024-02-01 13:26:53 +03:00
SBD
b90039f21a 5 2024-02-01 13:26:42 +03:00
b5f24ebf2e 0.1.350 small upd for .css 2024-01-31 17:49:03 +03:00
SBD
f99a010e4a 5 2024-01-31 17:17:53 +03:00
SBD
a4944360a6 5 2024-01-31 17:13:15 +03:00
SBD
a2b5d81c98 5 2024-01-31 17:11:12 +03:00
SBD
13b7c2572d Merge remote-tracking branch 'origin/main' 2024-01-31 16:57:06 +03:00
SBD
47b12882ba 5 2024-01-31 16:56:57 +03:00
SDE
c008aa585b Merge remote-tracking branch 'origin/main' 2024-01-31 16:39:12 +03:00
SDE
406b5e8480 0.12.2 registration mail 2024-01-31 16:39:03 +03:00
SBD
584a196784 5 2024-01-31 15:20:50 +03:00
SBD
618f7751d1 5 2024-01-31 15:20:20 +03:00
SBD
faaea1129a 5 2024-01-31 15:11:39 +03:00
SBD
dd7afc28f7 5 2024-01-31 15:00:48 +03:00
SBD
9ecf5ce073 5 2024-01-31 14:55:04 +03:00
SBD
422373f00e 5 2024-01-31 14:46:18 +03:00
SBD
60636daeb8 Merge remote-tracking branch 'origin/main' 2024-01-31 14:40:19 +03:00
SBD
9d47f4c2bc 5 2024-01-31 14:40:11 +03:00
SDE
4d6dbddd28 Merge remote-tracking branch 'origin/main' 2024-01-31 14:37:49 +03:00
SDE
3f00ff39d2 0.12.1 fix form from and to place fields choices 2024-01-31 14:37:41 +03:00
SBD
bff6a81586 Merge remote-tracking branch 'origin/main' 2024-01-31 14:12:55 +03:00
SBD
9cb8036d3c 3 2024-01-31 14:12:44 +03:00
SDE
a43de1fa91 Merge remote-tracking branch 'origin/main' 2024-01-31 13:44:18 +03:00
SDE
93717bee2d 0.12.11 del mobile markers for response 2024-01-31 13:44:08 +03:00
SBD
8db1d6fdce 3 2024-01-31 13:41:47 +03:00
SBD
b41f8c7eca 3 2024-01-31 13:37:31 +03:00
SBD
c19405d32a Merge remote-tracking branch 'origin/main' 2024-01-31 13:24:02 +03:00
SBD
7b75c533a8 3 2024-01-31 13:23:50 +03:00
SDE
649dbab901 0.12.10 fix allauth google cancel 2024-01-26 19:02:17 +03:00
SDE
1a4498d19f 0.12.9 fix allauth google cancel 2024-01-26 19:00:47 +03:00
SDE
2786ef454d Merge remote-tracking branch 'origin/main' 2024-01-26 18:26:52 +03:00
SDE
961c0dd2a5 0.12.8 switch off push for static pages 2024-01-26 18:26:37 +03:00
SBD
0a0835d3a6 3 2024-01-26 18:20:25 +03:00
SBD
bc87d10d59 Merge remote-tracking branch 'origin/main' 2024-01-26 18:14:50 +03:00
SBD
792693848b 2 2024-01-26 18:14:41 +03:00
SDE
de229c5f78 0.12.7 chat fixes 2024-01-26 17:45:48 +03:00
SDE
03f9a836e6 0.12.6 chat fixes 2024-01-26 17:09:42 +03:00
SBD
91751574cc 1 2024-01-26 16:31:58 +03:00
d5453dada6 0.1.349 small upd 2024-01-26 14:37:50 +03:00
22adb18a39 Merge remote-tracking branch 'origin/main' 2024-01-26 13:59:36 +03:00
0a3c40dd5d 0.1.348 upd input_list for main page 2024-01-26 13:58:51 +03:00
SBD
b29825d62b correct titles 2024-01-26 13:29:05 +03:00
c9bda8aab2 Merge remote-tracking branch 'origin/main' 2024-01-26 13:06:30 +03:00
284a4b064a 0.1.347 update show_contact button behavior 2024-01-26 13:06:17 +03:00
SBD
6f20f53d75 ws changes 2024-01-26 12:47:46 +03:00
SDE
473a047af9 Merge remote-tracking branch 'origin/main' 2024-01-25 18:36:29 +03:00
SDE
481f9a881f 0.12.5 google auth fix 2024-01-25 18:36:15 +03:00
6ceedddbc1 0.1.346 add border for input_list. Change color and width for scroll-bar v2 2024-01-25 17:05:01 +03:00
bd9e0fad48 0.1.345 add border for input_list. Change color and width for scroll-bar 2024-01-25 16:50:36 +03:00
6b118d00b6 0.1.344 hide div with phone number 2024-01-25 15:39:15 +03:00
01e221196f 0.1.343 small bug fix with buttons style v.2 2024-01-25 14:57:26 +03:00
ccf016999b 0.1.342 small bug fix with buttons style 2024-01-25 14:51:49 +03:00
SBD
cb79e47796 Merge remote-tracking branch 'origin/main' 2024-01-23 18:05:57 +03:00
SBD
4076bd6065 ws changes 2024-01-23 18:05:45 +03:00
e54c258007 Merge remote-tracking branch 'origin/main' 2024-01-23 17:15:15 +03:00
c3b7401255 0.1.341 upd text for main page, customer/mover page. Add new png for main page 2024-01-23 17:15:04 +03:00
SDE
9af72d30f4 0.12.4 ws fix 2024-01-22 18:10:32 +03:00
SDE
dbe948ae5f Merge remote-tracking branch 'origin/main' 2024-01-22 18:09:04 +03:00
SDE
885d36a90c 0.12.3 ws fix 2024-01-22 18:08:55 +03:00
SBD
7acfbc0113 Merge remote-tracking branch 'origin/main' 2024-01-22 17:41:33 +03:00
SBD
43c5faf988 took wss 2024-01-22 17:41:17 +03:00
SDE
437142fba0 Merge remote-tracking branch 'origin/main' 2024-01-22 17:32:10 +03:00
SDE
acf7b702ee 0.12.2 ws fix 2024-01-22 17:32:03 +03:00
74ca1be884 Merge remote-tracking branch 'origin/main' 2024-01-19 13:18:30 +03:00
26b987d6ce 0.1.340 upd text and add href for customer button 2024-01-19 13:18:09 +03:00
SDE
3f7f1d88ed 0.12.1 addsense register 2024-01-18 20:13:52 +03:00
SDE
59225b1688 Merge remote-tracking branch 'origin/main' 2024-01-18 20:11:54 +03:00
SDE
108aee2c43 0.12.0 addsense register 2024-01-18 20:11:45 +03:00
9062ce32a1 Merge remote-tracking branch 'origin/main' 2024-01-18 14:09:46 +03:00
7c74a0f3fe 0.1.339 slice name/surname if it longer then 5 character 2024-01-18 14:08:48 +03:00
SDE
4fb3a12f47 0.11.22 push w link 2024-01-18 13:15:28 +03:00
SDE
59bc25f31c 0.11.21 push w link 2024-01-16 17:25:40 +03:00
SDE
87fbe7852c 0.11.20 push w link 2024-01-16 16:53:01 +03:00
SDE
793d283d97 0.11.19 Google Auth 2024-01-15 19:31:30 +03:00
SDE
651ae18345 0.11.18 Google Auth 2024-01-15 17:44:59 +03:00
SDE
67eb32968e 0.11.17 Google Auth 2024-01-15 17:43:18 +03:00
SDE
c6d41513c0 0.11.16 Google Auth 2024-01-15 17:27:48 +03:00
SDE
67ba698b60 0.11.15 Google Auth 2024-01-15 17:06:53 +03:00
SDE
15d1c00ac6 0.11.14 Google Auth 2024-01-15 16:56:18 +03:00
SDE
9e9a82ffc0 0.11.13 Google Auth 2024-01-15 16:51:38 +03:00
SDE
a0eb5210ab 0.11.12 Google Auth 2024-01-15 16:47:53 +03:00
SDE
3dacc0316e Merge remote-tracking branch 'origin/main' 2024-01-15 16:36:59 +03:00
SDE
e2c347c912 0.11.11 Google Auth 2024-01-15 16:36:51 +03:00
758f8b1f55 0.1.338 upd style for cards_item 2024-01-15 13:43:38 +03:00
SDE
b446a8f519 0.11.10 Google Auth 2024-01-13 21:24:58 +03:00
SDE
c8ba0dd770 0.11.9 Google Auth 2024-01-13 21:15:05 +03:00
SDE
f13a1329ca 0.11.8 Google Auth 2024-01-13 21:13:06 +03:00
SDE
4b3604098f 0.11.7 Google Auth 2024-01-13 21:12:04 +03:00
SDE
defbf6746f 0.11.6 Google Auth 2024-01-13 18:04:01 +03:00
SDE
bf3f26ec4f 0.11.5 Google Auth 2024-01-13 18:01:34 +03:00
SDE
76018333b0 0.11.4 Google Auth 2024-01-13 17:29:21 +03:00
SDE
e6e345b9fe 0.11.3 Google Auth 2024-01-13 17:27:32 +03:00
SDE
eafccabb66 0.11.2 Google Auth 2024-01-13 17:26:25 +03:00
SDE
f2e0628de1 0.11.1 Google Auth 2024-01-13 17:05:57 +03:00
SDE
2e1f3a10ab 0.11.0 Google Auth 2024-01-13 14:49:32 +03:00
bf18c96dbd 0.1.337 add google authorization button 2024-01-13 14:12:25 +03:00
23718a5f3f 0.1.336 upd cards_item for main page 2024-01-13 13:46:58 +03:00
c6a1d5bdcf Merge remote-tracking branch 'origin/main' 2024-01-12 17:42:00 +03:00
f75e94f706 0.1.335 upd cards_wrapper on main page 2024-01-12 17:41:48 +03:00
SBD
11c9bb6c23 took wss 2024-01-12 17:01:50 +03:00
SBD
b1ff9c47da Merge remote-tracking branch 'origin/main' 2024-01-12 16:49:47 +03:00
SBD
35c09dc70b check post csrf8 2024-01-12 16:49:38 +03:00
SDE
f0efae5987 0.10.7 fix 403 2024-01-12 16:44:51 +03:00
SDE
65120cd2d4 0.10.6 fix 403 2024-01-12 16:41:43 +03:00
SBD
231d062814 check post csrf7 2024-01-12 16:32:00 +03:00
SBD
615922a881 check post csrf6 2024-01-12 16:28:49 +03:00
SBD
61b2b824d5 check post csrf5 2024-01-12 16:21:11 +03:00
SBD
317445998a check post csrf4 2024-01-12 16:16:27 +03:00
SBD
4ad3813499 check post csrf3 2024-01-12 16:11:35 +03:00
SBD
9a88243323 Merge remote-tracking branch 'origin/main' 2024-01-12 15:58:42 +03:00
SBD
df0f32c71a check post csrf2 2024-01-12 15:58:34 +03:00
SDE
4ddd402442 0.10.5 fix 403 2024-01-12 15:55:09 +03:00
SBD
a05c4b3898 Merge remote-tracking branch 'origin/main' 2024-01-12 15:50:54 +03:00
SBD
a3ab0973c9 check post csrf1 2024-01-12 15:50:43 +03:00
SDE
0f801aa44b 0.10.4 monkey patching fix 403 2024-01-12 02:28:51 +03:00
SBD
7bc386bc44 Merge remote-tracking branch 'origin/main' 2024-01-11 19:37:48 +03:00
SBD
e148c60d70 add cookie 3 2024-01-11 19:37:39 +03:00
SDE
4667352ec4 0.10.3 fix websocet with ssl 2024-01-11 19:34:34 +03:00
SBD
e7cf694d88 add cookie 2 2024-01-11 19:31:14 +03:00
SBD
b2abb3046b add cookie 2024-01-11 19:10:30 +03:00
SDE
6c1011e59e 0.10.2 mail alert by new routes 2024-01-11 19:03:25 +03:00
ad909b98bf 0.1.334 save checked after reload in receive_msg_by_email 2024-01-11 15:27:14 +03:00
9d2e35246a 0.1.333 upd 404_page and create conditions for unfound routes 2024-01-11 15:14:45 +03:00
48dc573c0f 0.1.334 upd text for unfinded routes 2024-01-11 13:45:20 +03:00
SBD
e559660912 Merge remote-tracking branch 'origin/main' 2024-01-09 22:01:13 +03:00
SBD
aa3bec304a correct work curtain 2024-01-09 22:01:00 +03:00
c7bc22813f 0.1.333 upd 404_page and create conditions for unfound routes 2024-01-09 13:03:30 +03:00
SDE
46e73db10a 0.10.1 browser push messages 2024-01-08 15:08:46 +03:00
SDE
ad451c2ae0 0.10.0 browser push messages 2024-01-08 14:54:59 +03:00
SDE
0b5b557a35 0.9.0 404 prepare 2024-01-08 13:22:11 +03:00
SBD
fb9228c432 correct work curtain 2024-01-06 14:48:31 +03:00
SBD
fbcbe93042 correct work curtain 2024-01-06 14:37:34 +03:00
ca44deb077 0.0.332 upd text on main_page 2024-01-04 14:50:25 +03:00
61403bfac2 0.0.331 set white background-color for input_list 2024-01-03 13:20:25 +03:00
SBD
190efa4b74 mobile === false 2023-12-29 21:30:03 +05:00
SBD
ca06836b11 mobile === false 2023-12-29 21:06:08 +05:00
SBD
b31241872f mobile === false 2023-12-29 19:45:43 +05:00
SBD
f80e2844bf mobile === false 2023-12-29 12:49:13 +05:00
1dae86f0e7 0.0.330 add color for title in my_routes 2023-12-21 16:28:42 +03:00
0c146caeef 0.0.329 upd logic for from_to_country inputs in b_new_route 2023-12-20 14:18:31 +03:00
410733211b 0.0.328 fix bug in lalble b_new_route.html | upd style title for routes on main page | commented out the code in b_chats.html for i icon 2023-12-20 14:03:59 +03:00
14df8969c2 0.0.327 upd title on main page 2023-12-18 16:08:09 +03:00
SBD
4388414ac2 correct work icons right or left of the curtain 2023-12-17 08:42:50 +03:00
SBD
791ac8d436 correct work curtain at routes_find_routes 2023-12-17 08:39:47 +03:00
a3d6f498b1 0.0.326 upd change_profile 2023-12-15 16:56:14 +03:00
SDE
b8fdd61948 0.8.43 change profile validation 2023-12-15 16:12:34 +03:00
SDE
4437372d7c 0.8.42 change profile validation 2023-12-15 16:03:27 +03:00
SDE
f8d29d80b7 0.8.41 change profile validation 2023-12-15 15:56:51 +03:00
SDE
36fd9599af 0.8.40 change profile validation 2023-12-15 15:46:46 +03:00
65eccde487 0.0.326 upd change_profile 2023-12-15 15:44:23 +03:00
SDE
037b4cc562 0.8.39 change profile validation 2023-12-15 15:28:29 +03:00
SDE
9c971a6fa4 0.8.38 change profile validation 2023-12-15 15:11:35 +03:00
SBD
a3faa17754 Merge remote-tracking branch 'origin/main' 2023-12-13 17:59:25 +03:00
SBD
085f905125 correct work curtain at routes_find_routes 2023-12-13 17:57:00 +03:00
dc16ced786 0.0.325 hide scroll for mozilla 2023-12-12 17:06:05 +03:00
d5cba26098 0.0.324 2023-12-12 16:50:52 +03:00
001dd2cb87 0.0.323 2023-12-12 16:34:00 +03:00
16e860a29f 0.0.322 upd lable for b_new_route.html 2023-12-12 16:01:01 +03:00
SDE
6a5331d8eb 0.8.37 owner_type initial 2023-12-07 18:35:06 +03:00
aff0b0fe98 0.0.321 2023-12-07 16:54:57 +03:00
fd3612c370 0.0.320 2023-12-06 18:03:48 +03:00
d14a46d3d7 0.0.319 upd create_route, header_buttons alight 2023-12-06 15:51:55 +03:00
SBD
d7ace77de8 0.8.482 2023-12-05 22:17:49 +03:00
SBD
885e4722af 0.8.481 2023-12-05 21:11:36 +03:00
SBD
72ed6369d8 0.8.479 2023-12-05 20:24:07 +03:00
b23e440efd 0.0.318 work with datarangepicker.js 2023-12-05 20:16:44 +03:00
75ddb002fd Merge remote-tracking branch 'origin/main' 2023-12-05 18:52:58 +03:00
28a36335ce 0.0.317 locale for datarangepicker.js 2023-12-05 18:52:52 +03:00
SDE
fba225aa70 Merge remote-tracking branch 'origin/main' 2023-12-05 17:43:09 +03:00
SDE
af800ac84c 0.8.36 check dates when route create 2023-12-05 17:43:01 +03:00
87e90d7152 0.0.316 add new language flags 2023-12-05 16:58:32 +03:00
c25942a6ca 0.0.315 test with viewport 2023-12-05 15:24:46 +03:00
692419816f 0.0.314 test with viewport 2023-12-05 15:21:21 +03:00
3a109340fd 0.0.313 test with viewport 2023-12-05 14:59:44 +03:00
5925ecb975 0.0.312 test with viewport 2023-12-05 14:54:21 +03:00
325acd3580 0.0.311 add red color for changed input in from_to_addres_point 2023-12-05 14:38:25 +03:00
3ac85784a9 0.0.310 2023-12-05 13:48:41 +03:00
5c4e715970 Merge remote-tracking branch 'origin/main' 2023-12-05 13:39:38 +03:00
89b57feb4e 0.0.309 upd avatar for routes 2023-12-05 13:37:17 +03:00
SDE
fc194d3f85 0.8.35 order for city / country 2023-12-05 13:25:31 +03:00
SDE
12af0ea238 Merge remote-tracking branch 'origin/main' 2023-12-05 12:47:30 +03:00
SDE
6e758cf62e 0.8.34 fix route_search 2023-12-05 12:47:23 +03:00
f7783c070b Merge remote-tracking branch 'origin/main' 2023-12-05 12:45:26 +03:00
SBD
fb3cd30db4 0.8.478 2023-12-04 17:23:27 +03:00
ff949a8205 0.0.308 2023-12-03 21:02:13 +03:00
SBD
624653f581 Merge remote-tracking branch 'origin/main' 2023-12-03 20:57:56 +03:00
SBD
f3e5f02f07 0.8.477 2023-12-03 20:57:38 +03:00
d0792e9e84 Merge remote-tracking branch 'origin/main' 2023-12-03 20:56:38 +03:00
f752eb5d4a 0.0.307 clear errors for DT in create route. Change default select for main 2023-12-03 20:56:29 +03:00
SBD
f0861fde84 Merge remote-tracking branch 'origin/main' 2023-12-03 20:53:59 +03:00
SBD
4e7aa09c21 0.8.476 2023-12-03 20:53:48 +03:00
SDE
e68a0dc151 Merge remote-tracking branch 'origin/main' 2023-12-03 20:49:14 +03:00
SDE
fc37bea98a 0.8.33 tickets text 2023-12-03 20:49:07 +03:00
SBD
8a91e611ac Merge remote-tracking branch 'origin/main' 2023-12-03 20:43:24 +03:00
SBD
7208db6981 0.8.475 2023-12-03 20:43:13 +03:00
SDE
447a78cd3a 0.8.32 tickets text 2023-12-03 20:37:01 +03:00
SBD
95dff519b8 0.8.474 2023-12-03 20:36:20 +03:00
SBD
62aeebae95 0.8.473 2023-12-03 20:22:20 +03:00
SBD
c2e03728f8 0.8.472 2023-12-03 20:00:32 +03:00
SBD
a64fbab270 0.8.471 2023-12-03 19:59:15 +03:00
SBD
00ea16f631 0.8.470 2023-12-03 19:50:51 +03:00
SBD
e3aafcdfe9 0.8.469 2023-12-03 19:35:52 +03:00
SBD
421a3a2dab Merge remote-tracking branch 'origin/main' 2023-12-03 19:27:11 +03:00
SBD
61e4f7e450 0.8.468 2023-12-03 19:26:56 +03:00
SDE
3b52cab162 0.8.31 fix registration form 2023-12-03 19:07:49 +03:00
fe9be61772 Merge remote-tracking branch 'origin/main' 2023-12-03 18:59:00 +03:00
0818f880cc 0.0.306 upd registration error_msg 2023-12-03 18:58:54 +03:00
SBD
cca062efec Merge remote-tracking branch 'origin/main' 2023-12-03 18:33:34 +03:00
SBD
2eb7ac85d3 0.8.467 2023-12-03 18:33:23 +03:00
SDE
3469a3923c 0.8.30 fix registration form 2023-12-03 18:07:39 +03:00
SDE
20fb49b3e4 0.8.30 fix registration form 2023-12-03 17:47:29 +03:00
SDE
afe8feebc2 Merge remote-tracking branch 'origin/main' 2023-12-03 17:43:13 +03:00
SDE
ae6a68d85b 0.8.30 check password 2023-12-03 17:43:01 +03:00
SBD
d80c45acd5 0.8.466 2023-12-03 17:40:28 +03:00
3a9ac2c289 Merge remote-tracking branch 'origin/main' 2023-12-03 17:38:59 +03:00
2d68836724 0.0.305 set test_banners for news 2023-12-03 17:38:53 +03:00
SDE
0deb85b5a3 0.8.29 admin airport change city 2023-12-03 17:25:21 +03:00
SBD
7b38f6e3b3 Merge remote-tracking branch 'origin/main' 2023-12-03 17:21:23 +03:00
SBD
43f050f070 0.8.465 2023-12-03 17:21:12 +03:00
098b7c4fb5 Merge remote-tracking branch 'origin/main' 2023-12-03 17:15:32 +03:00
eae56199e0 0.0.304 rewrite news widget 2023-12-03 17:15:23 +03:00
SDE
71e8f0f465 Merge remote-tracking branch 'origin/main' 2023-12-03 17:12:49 +03:00
SBD
66d7183278 0.8.464 2023-12-03 17:12:35 +03:00
SDE
92b92f2d65 0.8.29 admin airport change city 2023-12-03 17:12:32 +03:00
SBD
d5ab7d82eb 0.8.463 2023-12-03 17:09:16 +03:00
SBD
1ebd9f9c0b Merge remote-tracking branch 'origin/main' 2023-12-03 16:53:30 +03:00
SBD
7fd2c60ddb 0.8.462 2023-12-03 16:53:23 +03:00
SDE
f068182557 0.8.28 switch off passanger in airport route 2023-12-03 16:37:24 +03:00
SBD
02945b0691 Merge remote-tracking branch 'origin/main' 2023-12-03 16:09:32 +03:00
SBD
21b959e6a3 0.8.461 2023-12-03 16:09:24 +03:00
SDE
747091c744 0.8.27 one article breadcrumbs 2023-12-03 15:39:52 +03:00
7b70052ff1 0.0.303 2023-12-03 15:26:08 +03:00
SBD
49fd26bc4c Merge remote-tracking branch 'origin/main' 2023-12-03 15:12:18 +03:00
SBD
77f365ef45 Merge remote-tracking branch 'origin/main' 2023-12-03 15:12:11 +03:00
SDE
6d34d46e38 0.8.26 one article breadcrumbs 2023-12-03 15:12:04 +03:00
SBD
970c2e0837 0.8.460 2023-12-03 15:11:33 +03:00
SDE
b87549d567 0.8.25 msgs admin list change 2023-12-03 15:06:47 +03:00
SBD
78abdb2fef 0.8.459 2023-12-03 14:46:34 +03:00
SBD
f73ebb08c9 Merge remote-tracking branch 'origin/main' 2023-12-03 14:39:16 +03:00
SBD
628e8eec09 0.8.458 2023-12-03 14:39:07 +03:00
66ddcaec5b Merge remote-tracking branch 'origin/main' 2023-12-03 14:38:29 +03:00
3901c82aae 0.0.302 clear ID of hide inputs in create/find routes 2023-12-03 14:38:23 +03:00
SBD
9f682b66a5 0.8.457 2023-12-03 14:24:15 +03:00
SBD
c74aff7b91 0.8.456 2023-12-03 13:52:49 +03:00
SBD
1be50b1277 Merge remote-tracking branch 'origin/main' 2023-12-03 13:34:01 +03:00
SDE
af00d1a54b 0.8.25 profile unredeaded messages 2023-12-03 13:32:21 +03:00
SBD
dd4ad82248 0.8.455 2023-12-03 13:18:49 +03:00
SBD
38c08cbced Merge remote-tracking branch 'origin/main' 2023-12-03 12:59:15 +03:00
SBD
ed2c74a280 0.8.454 2023-12-03 12:59:05 +03:00
SDE
90ab1bb6b4 Merge remote-tracking branch 'origin/main' 2023-12-03 12:55:22 +03:00
SDE
3e2220e5f2 0.8.24 articles main 3 elements 2023-12-03 12:55:10 +03:00
SBD
33e27620b1 0.8.453 2023-12-03 12:46:44 +03:00
SBD
fbdef2a1ec 0.8.452 2023-12-03 12:34:24 +03:00
SBD
1834639b50 0.8.451 2023-12-03 12:22:05 +03:00
f9606a3c88 0.0.301 initialization in com_of 2023-12-02 17:07:25 +03:00
SDE
c803d7abbb 0.8.23 fix png black bg 2023-12-02 16:26:53 +03:00
SDE
efd75817bc 0.8.22 admin options change 2023-12-02 16:09:56 +03:00
SDE
36fff15e2b 0.8.21 admin options change 2023-12-02 16:07:12 +03:00
SDE
7573bfb344 Merge remote-tracking branch 'origin/main' 2023-12-02 15:59:40 +03:00
SDE
0c33d638ba 0.8.21 fix problem ckeditor uploads images 2023-12-02 15:59:29 +03:00
SBD
7d3da34e22 0.8.450 2023-12-02 15:55:47 +03:00
SDE
0e2723a367 Merge remote-tracking branch 'origin/main' 2023-12-02 15:42:34 +03:00
SDE
b102c44031 0.8.20 fix problem ckeditor uploads images 2023-12-02 15:42:25 +03:00
SBD
4b480c35e5 Merge remote-tracking branch 'origin/main' 2023-12-02 15:32:00 +03:00
SBD
8e7bcd8fd8 0.8.449 2023-12-02 15:31:50 +03:00
SDE
c60c545031 0.8.19 enable for subscribe in admin 2023-12-02 15:12:23 +03:00
SDE
51d075f799 0.8.18 enable for subscribe in admin 2023-12-02 15:10:17 +03:00
SDE
db54ec1650 Merge remote-tracking branch 'origin/main' 2023-12-02 15:02:48 +03:00
SDE
14b362442f 0.8.18 change pillow version for ckeditor 2023-12-02 15:02:39 +03:00
SBD
fcfbeece87 0.8.448 2023-12-02 15:01:48 +03:00
SBD
aea501aedb 0.8.447 2023-12-02 14:58:16 +03:00
SBD
f1d175fe84 Merge remote-tracking branch 'origin/main' 2023-12-02 14:56:40 +03:00
SBD
0f432c4fd9 0.8.446 2023-12-02 14:56:30 +03:00
b53e19c439 0.0.301 initialization in com_of 2023-12-02 14:43:23 +03:00
SDE
bebd20abf0 Merge remote-tracking branch 'origin/main' 2023-12-02 14:06:05 +03:00
SDE
5dc2293212 0.8.17 admin switch off functional 2023-12-02 14:05:57 +03:00
37f480c01e Merge remote-tracking branch 'origin/main' 2023-12-02 14:05:19 +03:00
f16efa24b0 0.0.300 fix errors in console 2023-12-02 14:05:14 +03:00
SDE
e11d05eb02 Merge remote-tracking branch 'origin/main' 2023-12-02 13:55:34 +03:00
SDE
32cd45106d 0.8.16 admin switch off functional 2023-12-02 13:55:26 +03:00
7b95893b34 Merge remote-tracking branch 'origin/main' 2023-12-02 13:52:50 +03:00
b6769f6350 0.0.299 2023-12-02 13:52:43 +03:00
SDE
5650b2d6b9 Merge remote-tracking branch 'origin/main' 2023-12-02 13:46:02 +03:00
SDE
f0fe8edf80 0.8.15 fix options 2023-12-02 13:45:54 +03:00
b30df5b6ed 0.0.298 2023-12-02 13:44:07 +03:00
0ef26556a2 0.0.297 remove errors after typing in ALL fields 2023-12-02 00:19:15 +03:00
ea92feab27 0.0.296 remove errors after typing in fields 2023-12-02 00:06:17 +03:00
1720e156d2 0.0.295 fix disabled buttons in different forms 2023-12-01 21:34:57 +03:00
9ed06e741e 0.0.294 2023-12-01 17:09:56 +03:00
d39a3a78d0 0.0.293 add loader for buttons when form send 2023-12-01 17:06:09 +03:00
09dc2984f3 0.0.292 upd error messages for forms 2023-12-01 15:28:14 +03:00
SDE
660e3f8a99 0.8.14
processing forms
2023-12-01 12:51:30 +03:00
SDE
cc9797eb71 0.8.13
processing forms
2023-12-01 12:30:34 +03:00
6a8aa73e63 0.0.291 2023-12-01 12:16:52 +03:00
689495e410 0.0.290 2023-12-01 12:10:17 +03:00
415fad4f4e Merge remote-tracking branch 'origin/main' 2023-12-01 12:02:59 +03:00
396db7a439 0.0.289 2023-12-01 12:02:52 +03:00
SDE
61bea5639b 0.8.12
processing forms
2023-12-01 11:36:11 +03:00
SDE
c9d6812d11 0.8.11
processing forms
2023-12-01 11:32:52 +03:00
18dd435678 0.0.288 2023-12-01 11:24:32 +03:00
a7a4112312 0.0.287 2023-12-01 11:14:04 +03:00
cf8cb9a6a4 0.0.286 2023-12-01 10:55:47 +03:00
b31f1ddb0f Merge remote-tracking branch 'origin/main' 2023-12-01 10:53:17 +03:00
382ddd48e4 0.0.285 2023-12-01 10:53:08 +03:00
SDE
0b57fa9238 0.8.10
fix send_message_ajax
2023-12-01 02:10:42 +03:00
SDE
fe7721270b 0.8.9
fix ResponseInterceptionMiddleware
2023-12-01 02:07:17 +03:00
49b35abb0b 0.0.284 2023-12-01 01:57:00 +03:00
41dbb6bad7 0.0.284 add send form from footer 2023-12-01 01:13:49 +03:00
fde640e997 0.0.283 add success_msg for forms, clear forms 2023-12-01 00:34:42 +03:00
SBD
fd1f4ff9b1 Merge remote-tracking branch 'origin/main' 2023-11-30 18:11:33 +03:00
SBD
3a4c1b12a6 0.8.445 2023-11-30 18:11:18 +03:00
ce68dbee8a 0.0.282 2023-11-30 18:07:04 +03:00
SDE
1976a4c1dc 0.8.8
processing send messages by mail
2023-11-30 17:46:18 +03:00
0dc3419651 0.0.281 2023-11-30 17:43:25 +03:00
7769608e03 Merge remote-tracking branch 'origin/main' 2023-11-30 17:26:02 +03:00
9772d7670b 0.0.280 upd confirm_remove button 2023-11-30 17:25:56 +03:00
SDE
36e9edb51b 0.8.7
processing send messages by mail
2023-11-30 16:03:37 +03:00
6191980dc7 0.0.278 2023-11-30 15:01:22 +03:00
7179b67137 0.0.278 2023-11-30 14:56:25 +03:00
65362e34b8 Merge remote-tracking branch 'origin/main' 2023-11-30 14:43:46 +03:00
ec76d336f3 0.0.277 upd carrier_card 2023-11-30 14:43:39 +03:00
SBD
12d5e4546d 0.8.444 2023-11-30 14:42:50 +03:00
SBD
4a97b02010 0.8.443 2023-11-30 14:28:37 +03:00
SDE
aa492f6ca4 0.8.6
fix find routes
2023-11-30 14:24:50 +03:00
SBD
7547d2cb89 0.8.442 2023-11-30 14:22:01 +03:00
SBD
3febf729b6 0.8.441 2023-11-30 14:13:25 +03:00
SDE
fbfdec2380 0.8.5
create_or_change_route_ajax return route_id
2023-11-30 14:06:57 +03:00
SBD
fb665b409c 0.8.440 2023-11-30 14:04:30 +03:00
SBD
f86e76615d 0.8.439 2023-11-30 13:49:45 +03:00
SBD
7cfe3f699a Merge remote-tracking branch 'origin/main' 2023-11-30 13:43:18 +03:00
SBD
03ab2ae2cd 0.8.438 2023-11-30 13:43:07 +03:00
SDE
ad1016a427 0.8.4
mail request_offer_ajax
2023-11-30 13:19:47 +03:00
SDE
6fc40452d9 0.8.3
request_offer_ajax
2023-11-30 08:15:02 +03:00
SDE
de098bdf55 0.8.2
fix articles
2023-11-30 07:41:22 +03:00
dcfd66324e Merge remote-tracking branch 'origin/main' 2023-11-29 22:09:17 +03:00
ff962b9658 0.0.276 2023-11-29 22:09:12 +03:00
SBD
006ff44858 Merge remote-tracking branch 'origin/main' 2023-11-29 22:03:42 +03:00
SBD
f9fdcc5314 0.8.437 2023-11-29 22:03:31 +03:00
e49605dea6 0.0.275 add data-set for forms in advertisement and partners pages 2023-11-29 22:00:03 +03:00
e429f48320 0.0.274 2023-11-29 21:48:25 +03:00
43ef016751 0.0.273 2023-11-29 21:28:42 +03:00
SBD
4a59adc73f Merge remote-tracking branch 'origin/main' 2023-11-29 21:24:07 +03:00
SBD
2ebc28519e 0.8.436 2023-11-29 21:23:59 +03:00
SDE
23de94b8cd 0.8.1
options init
2023-11-29 20:51:36 +03:00
42cf20137b 0.0.272 2023-11-29 20:50:24 +03:00
d5bb15e695 Merge remote-tracking branch 'origin/main' 2023-11-29 20:39:58 +03:00
039985fe5f 0.0.271 2023-11-29 20:39:49 +03:00
SBD
d618c10e8b Merge remote-tracking branch 'origin/main' 2023-11-29 20:34:10 +03:00
SBD
069f2094f0 0.8.434 2023-11-29 20:34:01 +03:00
SDE
2b6bbba265 0.8.0
options init
2023-11-29 20:18:22 +03:00
f7e032a5bc Merge remote-tracking branch 'origin/main' 2023-11-29 20:11:56 +03:00
0d394fb6b7 0.0.270 2023-11-29 20:11:51 +03:00
SBD
9635a1dcf2 0.8.433 2023-11-29 20:06:33 +03:00
SBD
6ae3a88d7b Merge remote-tracking branch 'origin/main' 2023-11-29 20:05:45 +03:00
SBD
512499ab12 0.8.432 2023-11-29 20:05:37 +03:00
b8e3092fc4 0.0.269 2023-11-29 20:03:49 +03:00
4b9e5e5f34 Merge remote-tracking branch 'origin/main' 2023-11-29 19:44:26 +03:00
a7d9dfe79a 0.0.268 all child are replaced 2023-11-29 19:44:20 +03:00
SDE
cc503ec6d1 Merge remote-tracking branch 'origin/main' 2023-11-29 19:21:22 +03:00
SDE
7eb8edbe1f 0.7.96
fix articles page
2023-11-29 19:21:15 +03:00
SBD
1f4ccaec4a 0.8.431 2023-11-29 19:11:26 +03:00
SBD
66e0edebc5 0.8.430 2023-11-29 19:11:08 +03:00
SBD
ebdc84f7e1 0.8.429 2023-11-29 19:05:40 +03:00
SBD
cd1b8d4579 0.8.428 2023-11-29 19:04:57 +03:00
SBD
a733649521 Merge remote-tracking branch 'origin/main' 2023-11-29 18:28:41 +03:00
SBD
01d72c5f45 0.8.427 2023-11-29 18:28:31 +03:00
SDE
55681c7fb8 0.7.95
meta names
2023-11-29 18:15:12 +03:00
SDE
4a19726a99 Merge remote-tracking branch 'origin/main' 2023-11-29 18:04:18 +03:00
SDE
61f26299b8 0.7.94
meta names
2023-11-29 18:04:06 +03:00
SBD
8a6317da2b 0.8.426 2023-11-29 18:01:03 +03:00
SBD
1124149cf8 0.8.425 2023-11-29 17:56:45 +03:00
426071fe15 Merge remote-tracking branch 'origin/main' 2023-11-29 17:52:55 +03:00
c97aa44723 0.0.267 replace all child for class or id 2023-11-29 17:52:49 +03:00
SDE
de6bd9682e Merge remote-tracking branch 'origin/main' 2023-11-29 17:51:12 +03:00
SDE
16e7ea3109 0.7.93
meta names
2023-11-29 17:50:59 +03:00
41fa14351a Merge remote-tracking branch 'origin/main' 2023-11-29 17:43:19 +03:00
35f135a10b 0.0.266 replace all child for class or id 2023-11-29 17:43:13 +03:00
SBD
037abac188 Merge remote-tracking branch 'origin/main' 2023-11-29 17:39:04 +03:00
SBD
fecd32a254 0.8.424 2023-11-29 17:38:53 +03:00
446af5e438 0.0.265 2023-11-29 17:30:26 +03:00
SDE
71ecd65814 0.7.92
FAQ show from admin
2023-11-29 17:26:59 +03:00
SBD
3e101493dc Merge remote-tracking branch 'origin/main' 2023-11-29 16:55:35 +03:00
SBD
453d4b7349 0.8.423 2023-11-29 16:55:26 +03:00
40a8bc158a Merge remote-tracking branch 'origin/main' 2023-11-29 16:55:24 +03:00
2c711ed370 0.0.264 upd previous_next_news widget 2023-11-29 16:55:17 +03:00
SDE
fc1654dedb 0.7.91
routes paging
2023-11-29 16:52:20 +03:00
SDE
dfec56fef9 0.7.90
articles paging
2023-11-29 16:38:12 +03:00
SDE
942db49daf Merge remote-tracking branch 'origin/main' 2023-11-29 16:36:18 +03:00
SDE
d4562e7ca4 0.7.89
articles paging
2023-11-29 16:36:10 +03:00
SBD
3a0845fe2a 0.8.422 2023-11-29 16:34:30 +03:00
SBD
3740ac642f Merge remote-tracking branch 'origin/main' 2023-11-29 16:33:37 +03:00
SBD
c1ccf00302 0.8.421 2023-11-29 16:33:28 +03:00
SDE
4400a6b13f 0.7.88
articles paging
2023-11-29 16:33:21 +03:00
SDE
66fbf984e9 0.7.87
articles paging
2023-11-29 16:29:48 +03:00
SBD
30ebc6037a Merge remote-tracking branch 'origin/main' 2023-11-29 16:26:17 +03:00
SBD
ac60b08f2d 0.8.420 2023-11-29 16:26:00 +03:00
f32e127148 0.0.263 resize to mobile since 1180px 2023-11-29 16:18:13 +03:00
SBD
8e3b798bce 0.8.419 2023-11-29 15:58:18 +03:00
SBD
57dddf528b 0.8.418 2023-11-29 15:52:31 +03:00
SBD
35d5e05f00 Merge remote-tracking branch 'origin/main' 2023-11-29 15:51:58 +03:00
SBD
4ecc89a4e7 0.8.417 2023-11-29 15:51:50 +03:00
SDE
87bc1be15a 0.7.86
fix left curtain arrows
2023-11-29 15:46:33 +03:00
SDE
b1c599e71d Merge remote-tracking branch 'origin/main' 2023-11-29 15:44:05 +03:00
SDE
da3fb36a22 0.7.85
fix css error
2023-11-29 15:43:45 +03:00
SBD
ded7761c91 Merge remote-tracking branch 'origin/main' 2023-11-29 15:41:59 +03:00
SBD
d4de2ef739 0.8.416 2023-11-29 15:41:45 +03:00
SDE
7637f6fdd1 Merge remote-tracking branch 'origin/main' 2023-11-29 15:38:11 +03:00
SDE
f663954f03 0.7.84
fix left curtain icon
2023-11-29 15:38:04 +03:00
SBD
f2fd95b04e 0.8.415 2023-11-29 15:24:12 +03:00
SBD
3880b97cae 0.8.414 2023-11-29 15:22:33 +03:00
SBD
2fb00ee112 0.8.413 2023-11-29 15:14:20 +03:00
SBD
dc92877beb 0.8.412 2023-11-29 15:05:13 +03:00
SBD
d9103e62a7 0.8.411 2023-11-29 15:00:28 +03:00
e5a8c3cb8f 0.0.262 add switch on main page 2023-11-29 14:59:20 +03:00
97b00d94cf 0.0.261 style updates 2023-11-28 17:47:52 +03:00
SBD
381f0a8490 0.8.410 2023-11-28 15:29:42 +03:00
SBD
b8b3fd02fa 0.8.409 2023-11-28 15:25:31 +03:00
SBD
3b244f4de0 Merge remote-tracking branch 'origin/main' 2023-11-28 14:48:23 +03:00
SBD
c4d101275a 0.8.408 2023-11-28 14:48:14 +03:00
c9b21ded98 0.0.260 bug fix 2023-11-28 14:33:24 +03:00
SBD
f5115f82fb 0.8.407 2023-11-28 14:29:29 +03:00
SBD
d6a6b4f319 0.8.406 2023-11-27 18:28:44 +03:00
SBD
f3e418cf68 Merge remote-tracking branch 'origin/main' 2023-11-27 18:24:05 +03:00
SBD
a74a0c1f57 0.8.405 2023-11-27 18:23:51 +03:00
0f10e274e6 0.0.259 small upd for styles 2023-11-27 18:21:33 +03:00
SBD
57a419eeaa 0.8.404 2023-11-27 18:17:20 +03:00
SBD
aa18a3c542 0.8.403 2023-11-25 22:03:54 +03:00
SBD
dea8d064d4 0.8.402 2023-11-25 21:54:45 +03:00
SBD
d022fb231f Merge remote-tracking branch 'origin/main' 2023-11-25 21:49:33 +03:00
SBD
415784bf06 0.8.401 2023-11-25 21:49:24 +03:00
SDE
246974c1b8 0.7.83
fix ticket first msg
2023-11-25 18:10:38 +03:00
SDE
34d2fea31f 0.7.82
correct padding
2023-11-25 17:17:10 +03:00
SDE
0571de3dd0 0.7.82
loader correct
2023-11-25 17:14:17 +03:00
SBD
4e2d453ff1 0.8.400 2023-11-25 16:48:26 +03:00
SDE
f4ba38654b 0.7.81
curtain right rule for mobile into get params
2023-11-25 16:40:07 +03:00
SDE
7831973cdb 0.7.80
FAQ admin
2023-11-25 16:31:10 +03:00
SBD
4f2c7aeb2c 0.8.399 2023-11-25 15:37:37 +03:00
SBD
237666d96f 0.8.398 2023-11-25 15:34:54 +03:00
SBD
f6d0486a19 0.8.397 2023-11-25 15:26:30 +03:00
SBD
200a9c308c 0.8.396 2023-11-25 15:14:09 +03:00
SBD
28d3138c43 0.8.395 2023-11-25 14:07:03 +03:00
SBD
0d41c95e3d 0.8.394 2023-11-25 13:53:23 +03:00
SBD
e5ea92df3d 0.8.393 2023-11-25 13:21:53 +03:00
SBD
9f7550eaae 0.8.392 2023-11-25 13:14:55 +03:00
SBD
3e88458fce 0.8.391 2023-11-25 13:08:15 +03:00
SBD
a5cbf8c128 0.8.390 2023-11-25 12:58:06 +03:00
SBD
704b88320a Merge remote-tracking branch 'origin/main' 2023-11-25 12:48:50 +03:00
SBD
0aae04eca8 0.8.398 2023-11-25 12:48:15 +03:00
3f1cd5431f 0.0.258 small_upd for articles 2023-11-24 19:35:11 +03:00
SBD
5081b6ce3e 0.8.397 2023-11-24 17:36:54 +03:00
SBD
7e7fb437a6 0.8.396 2023-11-24 17:19:45 +03:00
SBD
addca29f0f 0.8.395 2023-11-24 17:13:25 +03:00
SBD
f1775237fb Merge remote-tracking branch 'origin/main' 2023-11-24 16:49:19 +03:00
SBD
f09ff3f7df 0.8.394 2023-11-24 16:48:38 +03:00
4259f84764 Merge remote-tracking branch 'origin/main' 2023-11-24 16:39:56 +03:00
707bbf1863 0.0.257 return form in create_new_route 2023-11-24 16:38:24 +03:00
SBD
a989f85b2b 0.8.393 2023-11-24 16:37:29 +03:00
SBD
b62396a907 Merge remote-tracking branch 'origin/main' 2023-11-24 16:34:21 +03:00
SBD
998c91c727 0.8.393 2023-11-24 16:34:13 +03:00
64b8e74612 Merge remote-tracking branch 'origin/main' 2023-11-24 16:09:47 +03:00
55eff16b9c 0.0.256 2023-11-24 16:09:38 +03:00
SBD
31857131be Merge remote-tracking branch 'origin/main' 2023-11-24 16:08:18 +03:00
SBD
77de950af8 0.8.392 2023-11-24 16:08:09 +03:00
176c84cf2d 0.0.255 2023-11-24 13:33:38 +03:00
15ea6a982c 0.0.254 upd filds in support 2023-11-23 16:55:45 +03:00
6230b77473 0.0.253 small rewrite for button with name in header 2023-11-23 01:10:46 +03:00
2f81a73078 0.0.252 add type_transport icons for finded routes 2023-11-22 16:33:16 +03:00
cf83ae059e Merge remote-tracking branch 'origin/main' 2023-11-22 16:31:01 +03:00
4418d4ad6f 0.0.250 fix bug with the same icons in the filter 2023-11-22 16:30:53 +03:00
SBD
239b6a0da1 0.8.391 2023-11-22 15:24:21 +03:00
SBD
cc02495459 Merge remote-tracking branch 'origin/main' 2023-11-22 15:22:26 +03:00
SBD
153d0380de 0.8.390 2023-11-22 15:21:49 +03:00
87fb930c63 0.0.250 fix bug with menu_profile 2023-11-22 14:39:31 +03:00
SBD
38a8cef3d9 0.8.388 2023-11-21 23:14:16 +03:00
SBD
6f7551abbf 0.8.387 2023-11-21 22:43:42 +03:00
SBD
64d6860e58 0.8.386 2023-11-21 22:22:02 +03:00
SBD
88e6c7eee8 0.8.385 2023-11-21 21:53:10 +03:00
SBD
75c5553852 0.8.384 2023-11-21 21:46:03 +03:00
SBD
9d1815c21f 0.8.383 2023-11-21 21:44:22 +03:00
SBD
c93cda4eaf 0.8.382 2023-11-21 20:53:14 +03:00
SBD
cce969d870 0.8.381 2023-11-21 20:30:07 +03:00
SBD
0b177d845c 0.8.380 2023-11-21 20:23:41 +03:00
SBD
42f288e0e4 0.8.379 2023-11-21 20:19:17 +03:00
3948c79891 0.0.249 add cursor pointer 2023-11-21 19:09:13 +03:00
c8a28de46a 0.0.248 add translate for placeholders 2023-11-21 18:49:50 +03:00
c660d8cb72 0.0.247 add translate for words with letter А 2023-11-21 18:29:02 +03:00
SDE
a2688c4a15 Merge remote-tracking branch 'origin/main' 2023-11-21 17:37:21 +03:00
SDE
9d2e9b7acd 0.7.80
FAQ admin
2023-11-21 17:37:12 +03:00
f37ed72b39 0.0.246 2023-11-21 13:41:11 +03:00
6fd3c11f06 0.0.245 small upd for w_carrier_card 2023-11-20 22:42:05 +03:00
414ffc703d 0.0.244 upd w_carrier_card.html and mobile_styles.css for this widget 2023-11-20 20:58:49 +03:00
f7a587b7a5 0.0.243 upd bug from 17:11 to 17:52 2023-11-20 15:20:40 +03:00
534defa36b 0.0.242 upd css for create route 2023-11-20 00:44:07 +03:00
1fbf846270 0.0.241 upd carrier-card 2023-11-20 00:21:12 +03:00
e6a1cd967e Merge remote-tracking branch 'origin/main' 2023-11-19 22:41:46 +03:00
c9adf1a785 0.0.240 2023-11-19 22:41:41 +03:00
SBD
8380b7e66e Merge remote-tracking branch 'origin/main' 2023-11-19 21:18:32 +03:00
SBD
e01c7e81f2 0.8.378 2023-11-19 21:18:23 +03:00
4751dd9079 Merge remote-tracking branch 'origin/main' 2023-11-19 20:03:32 +03:00
f82ae16e31 0.0.239 fix menu_profile, main search widget, benefit_img, header hide menu, header, title in cusmer page 2023-11-19 20:03:26 +03:00
SBD
8a00446b5c Merge remote-tracking branch 'origin/main' 2023-11-19 19:46:40 +03:00
SBD
39f37bcd23 0.8.377 2023-11-19 19:46:30 +03:00
c8e4df8150 0.0.238 bug fix - footer, faq, my_profile 2023-11-19 19:00:23 +03:00
1e9e2cb048 Merge remote-tracking branch 'origin/main' 2023-11-19 18:12:24 +03:00
06661cb187 0.0.237 2023-11-19 18:12:19 +03:00
SBD
fade021048 Merge remote-tracking branch 'origin/main' 2023-11-19 18:04:01 +03:00
SBD
9d90138847 0.8.376 2023-11-19 18:03:49 +03:00
b7a677d79d 0.0.236 bug fix tg 14:26 2023-11-19 17:45:10 +03:00
SBD
3660f5af6d 0.8.375 2023-11-19 17:07:08 +03:00
SBD
61e1c35f02 Merge remote-tracking branch 'origin/main' 2023-11-19 17:05:30 +03:00
SBD
89721ce797 0.8.374 2023-11-19 17:05:21 +03:00
55b4c0ac11 Merge remote-tracking branch 'origin/main' 2023-11-19 16:44:32 +03:00
70df8a53c8 0.0.235 add scroll to element in static pages 2023-11-19 16:44:26 +03:00
SBD
3311ab5b37 0.8.373 2023-11-19 15:49:39 +03:00
SBD
49ad367a70 0.8.372 2023-11-19 15:26:55 +03:00
SBD
cc3a5c4a4f 0.8.371 2023-11-19 15:09:39 +03:00
SBD
f7af979b7e 0.8.370 2023-11-19 14:58:02 +03:00
SBD
6f06f96a94 0.8.369 2023-11-19 14:52:52 +03:00
a3ef86ba37 0.0.234 small bug fix 2023-11-19 14:47:21 +03:00
SBD
d6da292d5f 0.8.368 2023-11-19 14:38:10 +03:00
SBD
ccb8c6585c 0.8.367 2023-11-19 14:20:59 +03:00
SBD
ea567008c6 0.8.366 2023-11-18 21:55:28 +03:00
SBD
6cbde10b11 Merge remote-tracking branch 'origin/main' 2023-11-18 18:39:43 +03:00
SBD
7e23776501 0.8.365 2023-11-18 18:39:25 +03:00
e6a91ebc72 Merge remote-tracking branch 'origin/main' 2023-11-18 18:38:58 +03:00
06a2e883fb 0.0.233 add margin for header and main_content 2023-11-18 18:38:53 +03:00
SBD
00b0e00fd3 Merge remote-tracking branch 'origin/main' 2023-11-18 18:38:20 +03:00
SBD
8086d5344e 0.8.364 2023-11-18 18:38:07 +03:00
ea76733305 Merge remote-tracking branch 'origin/main' 2023-11-18 18:36:05 +03:00
f0f8af48b3 0.0.232 add margin for header and main_content 2023-11-18 18:35:59 +03:00
SBD
9bc02c7752 Merge remote-tracking branch 'origin/main' 2023-11-18 18:29:38 +03:00
SBD
3067535fe0 0.8.363 2023-11-18 18:29:28 +03:00
cc6bd781e0 Merge remote-tracking branch 'origin/main' 2023-11-18 18:24:30 +03:00
10a1cb096d 0.0.231 small upd for mobile_styles.css 2023-11-18 18:24:24 +03:00
SBD
47c228d048 0.8.362 2023-11-18 18:10:31 +03:00
SBD
11c35c9abd 0.8.361 2023-11-18 17:58:11 +03:00
SBD
ba8bde1153 0.8.360 2023-11-18 17:30:15 +03:00
SBD
7c991024b7 0.8.359 2023-11-18 17:12:52 +03:00
SBD
710d500980 Merge remote-tracking branch 'origin/main' 2023-11-18 16:41:11 +03:00
SBD
cde0b55aab 0.8.358 2023-11-18 16:40:50 +03:00
SDE
6660db1a89 Merge remote-tracking branch 'origin/main' 2023-11-18 16:34:41 +03:00
SBD
8f76b484ba 0.8.357 2023-11-18 16:30:27 +03:00
SDE
b8790be7fe 0.7.79
create_or_change_route_ajax add route id to response
2023-11-18 16:26:49 +03:00
bd4a2e7507 0.0.230 2023-11-18 15:51:45 +03:00
70002f4efb 0.0.229 scroll to element after edit 2023-11-18 15:02:26 +03:00
d40eed3c5d 0.0.228 2023-11-18 13:44:36 +03:00
130d601e54 Merge remote-tracking branch 'origin/main' 2023-11-18 13:43:59 +03:00
SBD
7ae8d0ce48 0.8.356 2023-11-18 13:43:52 +03:00
SBD
494ff26fdd 0.8.355 2023-11-18 13:02:18 +03:00
330a582a26 0.0.227 upd styles(boris).css 2023-11-18 12:36:14 +03:00
SBD
54d5669b3b 0.8.354 2023-11-18 12:31:48 +03:00
SBD
a7491c545c 0.8.353 2023-11-18 12:28:31 +03:00
6515979251 0.0.361 2023-11-17 19:58:09 +03:00
a97d9ec2bc 0.0.360 2023-11-17 17:10:07 +03:00
2685e80161 0.0.227 upd styles(boris).css 2023-11-16 17:27:49 +03:00
ea94031545 0.0.226 upd mobile_styles.css 2023-11-16 17:26:16 +03:00
298e3861cf 0.0.225 upd mobile_styles.css 2023-11-16 16:50:15 +03:00
ce230e80a5 0.0.224 upd mobile_styles.css 2023-11-15 17:27:02 +03:00
b12ae238d3 0.0.223 2023-11-14 19:51:57 +03:00
d482570221 0.0.359 2023-11-12 16:37:27 +03:00
dde8c5fdd9 0.0.358 2023-11-12 16:30:19 +03:00
259d6662fe 0.0.357 2023-11-12 16:06:24 +03:00
e4849ec659 0.0.356 2023-11-12 15:52:39 +03:00
88f679cd41 0.0.356 2023-11-12 15:52:32 +03:00
f97eeb83c2 0.0.355 2023-11-11 19:05:46 +03:00
155e5bdf57 0.0.354 2023-11-11 19:03:12 +03:00
SDE
e78c9a1efb 0.7.78
scroll into view commit from boris
2023-11-11 18:56:46 +03:00
4b74dd8f19 0.0.353 2023-11-11 18:49:23 +03:00
c3be6d3e83 0.0.352 2023-11-11 18:33:53 +03:00
50baa907bd Merge remote-tracking branch 'origin/main' 2023-11-11 18:27:48 +03:00
40207ab256 0.0.351 2023-11-11 18:27:29 +03:00
0733851e4e 0.0.222 upd mobile for main in 1024 2023-11-11 18:10:40 +03:00
1aea397f9a 0.0.350 2023-11-11 15:33:07 +03:00
335af09596 0.0.349 2023-11-11 15:30:32 +03:00
95c3c062fd 0.0.221 upd menu_profile under 700px 2023-11-11 15:22:36 +03:00
f47e3e550c 0.0.220 upd menu_profile under 700px 2023-11-11 15:19:54 +03:00
d3d0dc5a22 0.0.219 2023-11-11 15:15:56 +03:00
ff4c573941 0.0.218 996 px mobile 2023-11-11 15:12:13 +03:00
8d52620d32 0.0.348 2023-11-11 14:26:17 +03:00
5856fbb670 0.0.347 2023-11-11 14:14:27 +03:00
7cb8a2de39 0.0.217 add mobile 575px 2023-11-11 13:51:54 +03:00
d57be37c34 0.0.217 upd header height 2023-11-11 12:12:14 +03:00
28a14aaf76 0.0.346 new scroll in menu 2023-11-11 00:31:09 +03:00
359ed5770d 0.0.216 add mobile 575px 2023-11-10 21:12:45 +03:00
fe093aa9ac 0.0.215 add mobile 575px 2023-11-10 20:45:46 +03:00
338a9ed08a Merge remote-tracking branch 'origin/main' 2023-11-10 20:21:22 +03:00
a8c954e8d9 0.0.214 upd mobile_styles.css 2023-11-10 20:21:15 +03:00
50ee432965 0.0.346 2023-11-10 20:00:21 +03:00
4b3e3b8401 0.0.345 2023-11-10 19:55:52 +03:00
2abf0f62e9 0.0.344 2023-11-10 19:50:18 +03:00
69dd70d42d 0.0.343 2023-11-10 19:14:45 +03:00
405cae84fe 0.0.342 2023-11-10 19:14:00 +03:00
ae9902965a 0.0.213 upd for support 2023-11-10 18:33:42 +03:00
SDE
0c20d7d0f4 0.7.77
dashboard page
2023-11-10 18:06:05 +03:00
d4e3b75bcb 0.0.342 2023-11-10 17:33:58 +03:00
bbd3fd51dc 0.0.212 upd for dropdown_menu and change_language 2023-11-10 17:21:27 +03:00
9ecc961e42 0.0.341 2023-11-10 16:24:43 +03:00
dd2cc62d01 0.0.340 2023-11-10 16:21:08 +03:00
7e316bbc73 0.0.211 small upd for menu_profile 2023-11-10 14:53:12 +03:00
e455a19ff3 0.0.210 small upd for menu_profile 2023-11-10 14:27:49 +03:00
618064d16a 0.0.209 my_subscribe 360px 2023-11-09 12:16:12 +03:00
05705a91cf 0.0.208 info_profile section 360px 2023-11-08 17:40:45 +03:00
ec8ede8e28 0.0.207 small upd 2023-11-08 16:06:15 +03:00
6131d2534a 0.0.206 add media 360px for create_route, my_routes 2023-11-08 16:03:32 +03:00
775ffe42d7 0.0.205 Boris, please read my comments in 2 files 2023-11-08 13:51:13 +03:00
400621572f 0.0.339 2023-11-07 17:08:44 +03:00
fe4539c9ed 0.0.338 2023-11-07 14:56:00 +03:00
a19017351b 0.0.337 2023-11-07 14:18:25 +03:00
e1a6fdd21f 0.0.336 2023-11-07 13:17:42 +03:00
264c5ae372 0.0.335 2023-11-07 12:54:23 +03:00
0a00e950b6 0.0.334 2023-11-06 17:32:00 +03:00
b12f2e784f 0.0.333 2023-11-06 16:41:48 +03:00
bc170e982f 0.0.332 2023-11-06 16:40:29 +03:00
c68c7b6109 0.0.331 2023-11-06 16:31:07 +03:00
ac95ac04bc 0.0.330 2023-11-06 16:23:53 +03:00
a23ccb7ac4 0.0.329 2023-11-06 15:58:49 +03:00
09c74d8f85 0.0.328 2023-11-06 15:01:04 +03:00
4c747a0c5e 0.0.204 2023-11-06 14:51:20 +03:00
3aa4b774bd 0.0.327 2023-11-06 14:10:17 +03:00
93cf3aa695 0.0.203 2023-11-06 13:46:32 +03:00
e75dd6c552 0.0.326 2023-11-06 13:03:14 +03:00
6ea03098b2 Merge remote-tracking branch 'origin/main' 2023-11-06 13:00:47 +03:00
2cef14d2d8 0.0.202 2023-11-06 13:00:41 +03:00
b7b6750ad2 0.0.325 2023-11-06 12:59:44 +03:00
a729070654 0.0.201 2023-11-06 12:36:46 +03:00
af42956f60 Merge remote-tracking branch 'origin/main' 2023-11-05 21:56:44 +03:00
ad8518a4c3 0.0.200 fix interlace 2023-11-05 21:56:38 +03:00
SDE
1c1867610d 0.7.76
unread chat message markers fix
2023-11-05 21:51:47 +03:00
SDE
8c3ebb00e5 0.7.75
unread chat message markers fix
2023-11-05 21:49:41 +03:00
abba3e6e10 0.0.325 2023-11-05 19:17:30 +03:00
058fe769b9 0.0.324 2023-11-05 19:15:41 +03:00
4b037443a6 0.0.323 2023-11-05 19:04:34 +03:00
28db905e14 0.0.322 2023-11-05 18:55:48 +03:00
SDE
41d7a996fd 0.7.74
profile curtain reformat
2023-11-05 18:51:05 +03:00
SDE
114a65746c 0.7.73
profile curtain reformat
2023-11-05 18:48:58 +03:00
6dd0cf7724 0.0.321 2023-11-05 17:56:34 +03:00
8e5a018c2a 0.0.320 2023-11-05 17:49:38 +03:00
052f236206 0.0.319 2023-11-05 17:20:40 +03:00
da3774527f Merge remote-tracking branch 'origin/main' 2023-11-05 14:20:54 +03:00
93401402ca 0.0.318 2023-11-05 14:20:43 +03:00
c1ea4788a4 0.0.199 small upd for my_routes 2023-11-03 18:27:44 +03:00
b32a79f966 0.0.198 media 360px all static page 2023-11-03 14:55:44 +03:00
cd748dc371 0.0.317 2023-11-02 17:58:35 +03:00
defce7a85c 0.0.316 2023-11-02 17:53:51 +03:00
63d5b62229 0.0.315 2023-11-02 17:45:18 +03:00
1acda2bb11 0.0.314 2023-11-02 17:25:31 +03:00
db8f5e16e3 0.0.313 2023-11-02 17:24:12 +03:00
c5c8fb8689 0.0.197 media 360px for user_profile v2 2023-11-02 17:23:57 +03:00
b11478b4c0 0.0.196 media 360px for user_profile v1 2023-11-02 16:47:54 +03:00
26463c1853 Merge remote-tracking branch 'origin/main' 2023-11-02 15:35:12 +03:00
b0b6513bb7 0.0.195 media 360px for user_profile 2023-11-02 15:35:05 +03:00
0c6135d786 0.0.312 2023-11-02 14:04:29 +03:00
0167baec84 0.0.311 2023-11-02 13:54:46 +03:00
cb5ab935ee 0.0.310 2023-11-02 13:44:13 +03:00
96efd045b4 0.0.194 add header width 100% v2 2023-11-02 13:14:25 +03:00
e1846e0564 0.0.193 add header width 100% 2023-11-02 13:09:44 +03:00
ed809290ca 0.0.192 media 360px for_customers page 2023-11-01 17:41:44 +03:00
0b2b9e184f 0.0.191 media 360px login and registration pages 2023-11-01 16:06:04 +03:00
4dd32820a3 0.0.190 media 360px header for not user 2023-11-01 15:13:13 +03:00
ffa6de7138 0.0.189 media 360px header for not user 2023-11-01 15:10:21 +03:00
189b9233ab 0.0.309 2023-10-31 22:07:27 +03:00
8c05b987ed 0.0.308 2023-10-31 21:10:34 +03:00
ed00864447 0.0.188 media 360px main 2023-10-31 17:46:07 +03:00
ad9e6aa6b9 0.0.187 media 360px footer 2023-10-31 17:20:01 +03:00
04daddefa7 0.0.186 small changes in footer 2023-10-31 09:58:19 +03:00
acf51fe626 0.0.307 2023-10-30 23:52:22 +03:00
9d44384d22 0.0.306 2023-10-30 15:06:24 +03:00
a435e85d4c 0.0.305 2023-10-30 09:30:29 +03:00
c6edd2c533 0.0.304 2023-10-29 22:20:02 +03:00
928c737ead 0.0.303 2023-10-27 19:07:25 +03:00
794d30ebf1 0.0.302 2023-10-27 18:17:12 +03:00
0864a07cdf 0.0.301 2023-10-27 18:02:18 +03:00
b64f58c237 0.0.300 2023-10-27 17:59:21 +03:00
cc86400957 Merge remote-tracking branch 'origin/main'
# Conflicts:
#	static/js/user_profile.js
2023-10-27 17:21:28 +03:00
b8c282f95f 0.0.299 2023-10-27 17:19:40 +03:00
47e39a1d9c 0.0.185 media 360px v.3.3 2023-10-26 17:27:03 +03:00
308e9c1df2 0.0.184 media 360px v.3.2 2023-10-26 16:43:10 +03:00
d3fd7b7428 0.0.183 media 360px v.3.1 2023-10-26 16:26:29 +03:00
31833000bb 0.0.182 media 360px v.3 2023-10-26 13:11:51 +03:00
02f7edade5 0.0.298 2023-10-25 16:27:08 +03:00
371457735a 0.0.297 2023-10-25 13:55:25 +03:00
ea65d62be8 0.0.181 media 320px v.2.1 2023-10-24 17:44:19 +03:00
d1b8a7a4aa 0.0.180 media 320px v.2 2023-10-24 16:46:11 +03:00
9b94b56c6e 0.0.179 media 320px v.2 2023-10-24 16:42:00 +03:00
e07cf3f9d6 0.0.178 media 320px v.1 2023-10-23 19:34:16 +03:00
906856b01b 0.0.296 2023-10-22 17:46:04 +03:00
SDE
4ac3349377 0.7.71
chat avatars
2023-10-22 17:42:21 +03:00
SDE
3a9b421503 0.7.70
chat avatars
2023-10-22 17:34:33 +03:00
SDE
c844389f09 0.7.69
profile change
2023-10-22 17:12:01 +03:00
SDE
444f23f9b2 0.7.68
profile change
2023-10-22 16:52:32 +03:00
721f2916cd Merge remote-tracking branch 'origin/main' 2023-10-22 16:51:21 +03:00
5d707853b7 0.0.295 2023-10-22 16:50:58 +03:00
SDE
19621752b5 0.7.67
profile change
2023-10-22 16:46:25 +03:00
SDE
f0e8cb5101 0.7.66
profile change photo
2023-10-22 14:53:32 +03:00
ac2de4ddcc 0.0.294 2023-10-22 14:51:47 +03:00
5471696d62 0.0.293 2023-10-22 14:35:23 +03:00
SDE
adee3383ce 0.7.65
profile change photo
2023-10-22 14:21:18 +03:00
SDE
0ab9631e0f Merge remote-tracking branch 'origin/main' 2023-10-22 14:18:35 +03:00
SDE
cc377963e0 0.7.64
profile change photo
2023-10-22 14:18:22 +03:00
5c22478108 0.0.292 2023-10-22 14:13:06 +03:00
51598dc022 0.0.291 2023-10-22 13:52:44 +03:00
f620cd26af 0.0.290 2023-10-22 13:46:56 +03:00
1f3a8a258b 0.0.289 2023-10-22 13:41:12 +03:00
SDE
19983bf7a1 Merge remote-tracking branch 'origin/main' 2023-10-22 13:04:21 +03:00
SDE
47ffae4b82 0.7.63
user_subscribe in all views
2023-10-22 13:04:05 +03:00
487647029a 0.0.288
add profile page
2023-10-21 23:42:19 +03:00
SDE
89ee4960c2 0.7.62
user_subscribe in all views
2023-10-20 21:43:43 +03:00
9c95358305 0.0.286 2023-10-20 21:40:27 +03:00
SDE
71c4ce41f0 0.7.61
user_subscribe in all views
2023-10-20 20:07:11 +03:00
f60c96e9fd 0.0.177 upd header 2023-10-19 14:46:08 +03:00
9ec605bed1 0.0.176 upd header 2023-10-19 14:27:20 +03:00
6ac082c7df 0.0.175 upd header 2023-10-19 09:50:12 +03:00
f552d6c0f2 0.0.174 change width in .header_big_background 2023-10-19 09:27:32 +03:00
52c65f598d 0.0.285 2023-10-18 18:05:55 +03:00
9b12ca8ff3 0.0.284 2023-10-18 17:37:45 +03:00
31ba8a86bf 0.0.283 2023-10-18 17:36:55 +03:00
fe90ab8f50 0.0.282 2023-10-18 17:17:03 +03:00
58acad0eb8 0.0.281 2023-10-18 17:00:14 +03:00
3ce22bcb32 0.0.173 upd style.css for header, add switcher for languages 2023-10-18 16:16:45 +03:00
SDE
71846ade42 0.7.60
localization for ajax
2023-10-18 13:30:48 +03:00
SDE
9c24034776 0.7.59
localization for ajax
2023-10-18 12:06:17 +03:00
735a84bf9a 0.0.280 2023-10-06 18:50:41 +03:00
SDE
b65a668e77 0.7.58
articles paging
2023-10-06 18:49:40 +03:00
SDE
ac2b1e43df 0.7.57
articles paging
2023-10-06 18:42:08 +03:00
SDE
63cea80a20 0.7.56
articles paging
2023-10-06 18:05:26 +03:00
67a51c882a 0.0.279 2023-10-06 18:00:54 +03:00
21b873c80d 0.0.172 2023-10-05 19:46:53 +03:00
SDE
c86ebc7aa3 0.7.55
articles paging
2023-10-05 19:46:02 +03:00
SDE
32f65de458 0.7.54
articles paging
2023-10-05 19:42:13 +03:00
63d3ffddd4 0.0.278 2023-10-05 19:38:56 +03:00
70f6ccc0da 0.0.277 2023-10-05 19:37:05 +03:00
5fa470bd9c 0.0.276 2023-10-05 19:29:12 +03:00
SDE
01414ca4c6 Merge remote-tracking branch 'origin/main' 2023-10-05 19:28:05 +03:00
SDE
c781453055 0.7.53
articles page
2023-10-05 19:27:57 +03:00
8b4b06c0f7 Merge remote-tracking branch 'origin/main' 2023-10-05 19:27:15 +03:00
0737446ea1 0.0.172 2023-10-05 19:27:07 +03:00
SDE
5f71eb5e0b Merge remote-tracking branch 'origin/main'
# Conflicts:
#	templates/pages/p_articles.html
2023-10-05 19:22:10 +03:00
SDE
b89b9ed017 0.7.52
articles page
2023-10-05 19:21:27 +03:00
0df3067416 0.0.172 2023-10-05 19:12:36 +03:00
f850ef5c31 0.0.171 2023-10-05 19:02:06 +03:00
a520c3872a 0.0.275 2023-10-05 19:00:02 +03:00
16c32ebdfb 0.0.170 add button for pagination in news 2023-10-05 18:55:50 +03:00
b0640e88f9 0.0.169 add position fixed for HEADER 2023-10-05 18:20:36 +03:00
6121b6ceb1 0.0.168 add 2 block in static pages 2023-10-03 18:21:12 +03:00
2d865d9c3d 0.0.167 small upgrade for static pages 2023-10-03 17:39:50 +03:00
95f92efa70 0.0.166 change input type from select to radio for checkbox_cargo_type 2023-10-03 16:57:13 +03:00
SDE
840a61511e 0.7.51
articles breadcrumbs
2023-10-03 15:53:08 +03:00
a1ed21cd17 0.0.165 upd style for a teg for news 2023-10-03 15:19:36 +03:00
c2b9b0c85b 0.0.165 a teg for news 2023-10-03 15:14:53 +03:00
2cc45c7efc 0.0.164 add article news 2023-10-03 13:41:01 +03:00
6dfc41fc0d 0.0.163 upd news page 2023-10-02 15:37:34 +03:00
f421e31de1 0.0.162 style for public pages 2023-10-01 15:48:14 +03:00
53c5b2b53e 0.0.161 upd footer max-width 2023-10-01 13:51:32 +03:00
c024b74f5b 0.0.160 upd tab on main page 2023-10-01 13:36:35 +03:00
598bcaaea4 0.0.274 2023-09-30 14:41:51 +03:00
571c0d08e0 0.0.273 2023-09-27 17:00:24 +03:00
a56c8c2609 Merge remote-tracking branch 'origin/main' 2023-09-26 17:17:42 +03:00
724c450120 0.0.159 upd footer 2023-09-26 17:17:33 +03:00
SDE
1794911d27 0.7.50
i18n localization problem test
2023-09-26 16:33:47 +03:00
01c7b01c55 Merge remote-tracking branch 'origin/main' 2023-09-26 15:42:26 +03:00
4bf5488d79 0.0.158 2023-09-26 15:42:21 +03:00
9d2b523f29 0.0.272
sorry
2023-09-26 15:19:35 +03:00
SDE
9eb079c5bc 0.7.49
i18n makemessages
2023-09-26 14:36:27 +03:00
fc8c28203c 0.0.157 rewrite some css 2023-09-26 14:19:34 +03:00
SDE
a03d9b3dec 0.7.48
i18n makemessages
2023-09-26 12:06:46 +03:00
SDE
641e3da1d0 Merge remote-tracking branch 'origin/main' 2023-09-26 12:05:41 +03:00
SDE
15b2110617 0.7.48
i18n makemessages
2023-09-26 12:05:31 +03:00
SDE
7a8f3bdbb5 0.7.47
fix admin base models
2023-09-26 11:26:10 +03:00
65c90e1f11 0.0.271 2023-09-25 16:36:57 +03:00
dd7795d845 0.0.156 disabled from 87 to 92 line 2023-09-24 18:00:46 +03:00
5af924556e 0.0.155 upd select css 2023-09-24 17:50:11 +03:00
cec55a36ed Merge remote-tracking branch 'origin/main' 2023-09-24 17:11:16 +03:00
09ec97ec2f 0.0.154 upd select css 2023-09-24 17:10:57 +03:00
32b8116357 0.0.270 2023-09-24 16:45:48 +03:00
c3079f4a65 Merge remote-tracking branch 'origin/main' 2023-09-24 16:40:28 +03:00
78148def17 0.0.153 rewrite css for selct 2023-09-24 16:40:23 +03:00
754f0ecc43 Merge remote-tracking branch 'origin/main' 2023-09-24 16:35:22 +03:00
a0e8698aa5 0.0.269 2023-09-24 16:35:16 +03:00
ff14b4aa2a Merge remote-tracking branch 'origin/main' 2023-09-24 14:57:29 +03:00
c0e6bcf7cb 0.0.152 fix main page 2023-09-24 14:57:24 +03:00
71e34c6b03 0.0.268 2023-09-24 14:52:06 +03:00
f5208def43 0.0.267 2023-09-24 13:56:41 +03:00
4f28e5d5a4 0.0.266 2023-09-24 13:55:00 +03:00
0a4329ea67 0.0.265 2023-09-24 13:16:11 +03:00
df5279e3f6 Merge remote-tracking branch 'origin/main' 2023-09-23 18:01:25 +03:00
cba94595f1 0.0.264 2023-09-23 18:01:19 +03:00
SDE
ed38aeed45 0.7.47
fix admin base models
2023-09-23 17:50:41 +03:00
SDE
57f5392203 0.7.46
fix never mind
2023-09-23 16:54:28 +03:00
2b95eceea9 0.0.263 2023-09-23 16:39:02 +03:00
e4f017df2a 0.0.262 2023-09-23 16:36:14 +03:00
SDE
20e6357127 0.7.45
reformat datetime picker
2023-09-23 16:32:30 +03:00
31ad83e561 0.0.261 2023-09-23 16:19:40 +03:00
SDE
06219e8f69 0.7.44
dates in route search
2023-09-23 15:46:21 +03:00
2f5485cc25 0.0.260 2023-09-23 15:43:35 +03:00
cf70c543e3 0.0.259 2023-09-23 15:37:11 +03:00
463f63af19 0.0.258 2023-09-23 15:30:37 +03:00
84620e74f8 0.0.257 2023-09-23 14:54:12 +03:00
573b8ec0c3 Merge remote-tracking branch 'origin/main' 2023-09-22 20:23:55 +03:00
4aa659d206 0.0.256 2023-09-22 20:23:49 +03:00
02e6733b13 0.0.151 small upd in static pages 2023-09-22 19:16:44 +03:00
7c6891e46f Merge remote-tracking branch 'origin/main' 2023-09-22 17:11:53 +03:00
35a953b546 0.0.150 remove h2 and upd headers in static 2023-09-22 17:11:47 +03:00
52edbd867d 0.0.149 upd main page 2023-09-22 16:35:04 +03:00
SDE
9f2ec3e79f 0.7.43
images in ckeditor
2023-09-22 15:14:16 +03:00
SDE
4ec85b85cb 0.7.42
images in ckeditor
2023-09-22 14:58:06 +03:00
4888fc7211 0.0.255 2023-09-22 13:25:21 +03:00
SDE
ca1d177565 Merge remote-tracking branch 'origin/main' 2023-09-22 12:45:21 +03:00
SDE
0b608e1b3e 0.7.41
main page cut count articles
2023-09-22 12:45:10 +03:00
5ae0e75a96 0.0.148 add faq on main page 2023-09-21 17:50:58 +03:00
54105c47f0 0.0.147 add tabs on main page 2023-09-21 13:28:45 +03:00
0f9ce1abd6 0.0.147 add switch 2023-09-21 11:52:46 +03:00
c322053b02 0.0.146 2023-09-20 17:32:24 +03:00
dab09b193d 0.0.145 upd header 2023-09-20 12:48:37 +03:00
97fb0e3e13 0.0.144 2023-09-19 17:43:18 +03:00
7abad9c43d 0.0.143 upd static pages 2023-09-19 16:58:12 +03:00
ce67fddcc9 0.0.142 upd static pages 2023-09-19 16:12:13 +03:00
b33664d3c1 0.0.141 swap url senders/movers 2023-09-19 14:01:44 +03:00
d0359907d7 0.0.140 fix small bugs 2023-09-19 13:38:41 +03:00
914133e73e 0.0.139 upd for_customer page 2023-09-18 17:19:01 +03:00
SDE
de34cedacf 0.7.40
add subscribes for static pages
2023-09-18 17:16:45 +03:00
SDE
c4d0975313 0.7.39
add subscribes for static pages
2023-09-18 16:35:48 +03:00
SDE
2fffff65c4 0.7.38
fix print chat msg in console
2023-09-18 15:23:57 +03:00
SDE
d78ac25828 0.7.37
route widget add time
2023-09-18 15:01:44 +03:00
SDE
4a267122e2 0.7.36
chat userlist show last_msg file
2023-09-18 14:36:28 +03:00
8a8fdbb8ec 0.0.138 little upd styles.css 2023-09-18 14:19:04 +03:00
ef8e664276 Merge remote-tracking branch 'origin/main' 2023-09-18 13:53:39 +03:00
ab84352918 0.0.137 small upd styles.css 2023-09-18 13:53:29 +03:00
SDE
51f15e2f67 0.7.35
robots.txt
2023-09-18 13:44:05 +03:00
SDE
e3fb527fe6 0.7.34
robots.txt
2023-09-18 13:41:04 +03:00
96c1b23362 0.0.254 2023-09-18 00:25:09 +03:00
SDE
9b7fbe8fd3 0.7.33
files in chat download
2023-09-17 19:17:48 +03:00
2d25fd3e38 Merge remote-tracking branch 'origin/main' 2023-09-17 18:56:43 +03:00
e850bceacf 0.0.253 2023-09-17 18:54:56 +03:00
SDE
da7b1cb549 0.7.32
files in chat download
2023-09-17 18:31:50 +03:00
1747b0ebb5 0.0.252 2023-09-17 18:11:02 +03:00
SDE
eebdd35ec8 0.7.31
files in chat messages
2023-09-17 17:50:33 +03:00
87b95f50fb 0.0.251 2023-09-17 17:38:12 +03:00
d09ca59279 0.0.250 2023-09-17 17:18:56 +03:00
8e6312f1ad 0.0.249 2023-09-17 15:03:32 +03:00
b221e7a330 0.0.248 2023-09-16 20:13:42 +03:00
d60d03f666 0.0.247 2023-09-16 20:04:28 +03:00
d90449f0fe 0.0.246 2023-09-16 19:10:08 +03:00
677eb001bf 0.0.245 2023-09-16 17:46:10 +03:00
4f5b7c5c08 0.0.245 2023-09-16 17:41:47 +03:00
SDE
b94d8e1d87 0.7.30
js file upload routines
2023-09-16 17:21:29 +03:00
4f46a01469 Merge remote-tracking branch 'origin/main' 2023-09-16 17:06:35 +03:00
ae4d871d90 0.0.244 2023-09-16 17:06:27 +03:00
SDE
bae7e05077 0.7.29
fix search routes insert txt address points
2023-09-16 16:19:22 +03:00
636d97250b 0.0.243 2023-09-16 16:18:15 +03:00
6097d5a2bd 0.0.136 upd about_us 2023-09-14 14:22:04 +03:00
2dbbc7dcaf 0.0.135 add contacts page 2023-09-14 13:08:14 +03:00
11506903cc 0.0.242 2023-09-13 17:21:51 +03:00
ec23d2ac37 0.0.241 2023-09-13 16:53:26 +03:00
1e00d941d1 Merge remote-tracking branch 'origin/main' 2023-09-13 12:44:42 +03:00
214eb0f7ef 0.0.134 upd textarea 2023-09-13 12:44:37 +03:00
SDE
a24d1ac987 Merge remote-tracking branch 'origin/main' 2023-09-13 12:29:00 +03:00
SDE
7345e70568 0.7.28
fix date fields in query get_routes_Dict
2023-09-13 12:28:51 +03:00
9e9c645799 Merge remote-tracking branch 'origin/main' 2023-09-13 12:28:01 +03:00
8539e9ce30 0.0.133 add counter for feedback 2023-09-13 12:27:53 +03:00
SDE
21fb8d0955 0.7.27
receive files for chat
2023-09-13 11:54:20 +03:00
2dc85fdb6f 0.0.132 2023-09-13 09:40:56 +03:00
b99f34fad1 0.0.131 remove error message from arrival_div after check 2023-09-13 09:40:41 +03:00
34a789337c 0.0.240 2023-09-12 21:24:38 +03:00
29416b838e 0.0.239 2023-09-10 19:48:49 +03:00
f5b00402c9 0.0.238 2023-09-10 19:39:07 +03:00
10e38edab8 0.0.237 2023-09-10 19:02:11 +03:00
e456c2606f 0.0.237 2023-09-10 15:43:42 +03:00
36037c3cfa 0.0.236 2023-09-10 15:04:51 +03:00
d14e2dcbbe 0.0.235 2023-09-10 14:43:14 +03:00
b0f138489d 0.0.234 2023-09-08 17:56:48 +03:00
31379e0949 0.0.233 2023-09-06 21:40:36 +03:00
d078a10f64 0.0.232 2023-09-06 21:31:51 +03:00
691b38f725 0.0.130 bug fix remove_route after tap to cancel_button 2023-09-06 13:13:08 +03:00
6c6801c1f0 0.0.231 2023-09-05 21:56:08 +03:00
cc00322a04 Merge remote-tracking branch 'origin/main' 2023-09-05 20:07:06 +03:00
77f96a39b6 0.0.230 2023-09-05 20:06:56 +03:00
7002bd2600 0.0.129 add bad remove_route_button design 2023-09-05 18:39:49 +03:00
ff47788d36 Merge remote-tracking branch 'origin/main' 2023-09-05 17:08:03 +03:00
9d226e3f6f 0.0.128 remove_route func v0.1 2023-09-05 17:07:57 +03:00
SDE
fa2c3ee569 0.7.26
country_n_city_str
2023-09-05 16:34:58 +03:00
SDE
704c44a636 0.7.25
country_n_city_str
2023-09-05 16:17:01 +03:00
52a0105806 Merge remote-tracking branch 'origin/main' 2023-09-05 14:43:40 +03:00
ad66adefbf 0.0.127 upd font-family 2023-09-05 14:43:32 +03:00
SDE
68978d09ac 0.7.24
del route
2023-09-05 14:43:18 +03:00
28504cbc45 0.0.127 bug fix cargo_type in create_new_route 2023-09-04 19:09:09 +03:00
f2580cf766 0.0.126 upd inf_about_moving.html 2023-09-04 15:36:26 +03:00
60a21740db 0.0.125 add url replace menu_profile 2023-09-04 14:46:20 +03:00
SDE
528ab0a8e2 0.7.23
profile static pages
2023-09-04 13:39:42 +03:00
SDE
1af65b01f6 0.7.22
fix user_page tpl
2023-09-04 12:21:15 +03:00
SDE
deadb0c73c 0.7.21
sub_footer links
2023-09-04 12:17:17 +03:00
SDE
ebc9bacbf1 0.7.20
header links
2023-09-04 12:13:56 +03:00
SDE
7dd1fe5dfe 0.7.19
profile static pages
2023-09-04 12:08:25 +03:00
436cd9aa58 0.0.124 upd partners and for customer page 2023-09-04 10:42:20 +03:00
27db6bd53f 0.0.124 upd about_service static_page 2023-09-04 08:33:07 +03:00
aef46d5d9c 0.0.229 2023-09-03 15:06:56 +03:00
1f330f3660 0.0.228 2023-09-03 14:52:42 +03:00
8b41767751 Merge remote-tracking branch 'origin/main' 2023-09-03 14:47:57 +03:00
e49de29785 0.0.227 2023-09-03 14:47:29 +03:00
SDE
bc9b47f651 Merge remote-tracking branch 'origin/main' 2023-09-03 14:39:51 +03:00
SDE
be6313d2d0 0.7.18
connect form on static_pages
2023-09-03 14:39:45 +03:00
0d60283b36 0.0.226 2023-09-03 14:34:23 +03:00
SDE
d81688eb59 Merge remote-tracking branch 'origin/main' 2023-09-03 14:32:49 +03:00
SDE
df03f16c0a 0.7.17
connect form on static_pages
2023-09-03 14:32:43 +03:00
2ea763265e 0.0.225 2023-09-03 14:29:57 +03:00
cc52dc3fa5 0.0.224 2023-09-03 14:19:23 +03:00
SDE
df370606a6 0.7.16
connect form on static_pages
2023-09-03 14:13:44 +03:00
SDE
18dd7ff1f1 0.7.15
add owner_type
2023-09-02 19:34:06 +03:00
835cce2d45 0.0.223 2023-09-02 19:26:30 +03:00
5999a67257 0.0.222 2023-09-02 19:21:55 +03:00
63879809fd 0.0.221 2023-09-02 19:18:36 +03:00
SDE
45fb9d0060 0.7.14
main page send owner_type
2023-09-02 19:02:02 +03:00
SDE
22932459cc 0.7.13
find_routes block change
2023-09-02 18:53:21 +03:00
0dca0557c7 Merge remote-tracking branch 'origin/main' 2023-09-02 18:40:13 +03:00
a46bbdec84 0.0.220 2023-09-02 18:40:06 +03:00
SDE
cb926522bb Merge remote-tracking branch 'origin/main' 2023-09-02 18:04:22 +03:00
SDE
26d2a5699d 0.7.12
richedit sets
2023-09-02 18:04:15 +03:00
5947b6fcb9 0.0.219 2023-09-02 18:04:07 +03:00
SDE
e59028a11a 0.7.12
article picture
2023-09-01 16:32:50 +03:00
SDE
1f71c4eba1 0.7.11
article picture
2023-09-01 16:31:10 +03:00
SDE
bbf660a2eb Merge remote-tracking branch 'origin/main' 2023-09-01 16:30:13 +03:00
SDE
0efe073a2a 0.7.10
article picture
2023-09-01 16:30:04 +03:00
40b18a2a89 Merge remote-tracking branch 'origin/main' 2023-09-01 16:27:43 +03:00
cee09d18db 0.0.123 add about_service static_page 2023-09-01 16:27:12 +03:00
SDE
7fc8beeb00 0.7.9
article picture
2023-09-01 16:26:40 +03:00
SDE
bd5317fa27 0.7.8
article picture
2023-09-01 16:24:47 +03:00
SDE
f7d4bbf2b3 Merge remote-tracking branch 'origin/main' 2023-09-01 15:29:49 +03:00
SDE
3015a3e8f2 0.7.7
links for footer
2023-09-01 15:29:41 +03:00
227aba33ee 0.0.122 upd static_page advertisement 2023-09-01 15:25:48 +03:00
770289f1ae 0.0.121 add static_page advertisement 2023-08-31 19:16:11 +03:00
SDE
7f2e216db4 0.7.6
fix subscribe_for_user
2023-08-31 19:09:18 +03:00
c8a902998f 0.0.218 2023-08-31 18:56:13 +03:00
c23ccf6212 0.0.217 2023-08-31 18:53:39 +03:00
SDE
1b809790a2 0.7.5
fix subscribe_for_user
2023-08-31 18:47:06 +03:00
d5d702c431 0.0.216 2023-08-31 18:27:55 +03:00
SDE
2757ea4d95 0.7.4
static pages
2023-08-31 14:10:07 +03:00
SDE
79c32a013e 0.7.3
static pages
2023-08-31 13:36:27 +03:00
SDE
ecdf99a187 0.7.2
static pages
2023-08-31 13:29:04 +03:00
SDE
fa06af7695 0.7.1
static pages
2023-08-31 13:23:55 +03:00
SDE
853706b36b 0.7.0
static pages
2023-08-31 12:59:45 +03:00
SDE
dcfe131237 Merge remote-tracking branch 'origin/main' 2023-08-31 12:28:38 +03:00
SDE
075d2915d3 0.6.13
subscribe_now ajax
2023-08-31 12:28:29 +03:00
5081aca37d 0.0.215 2023-08-30 16:54:40 +03:00
335101166c 0.0.214 2023-08-30 16:48:38 +03:00
a48777343c 0.0.213 2023-08-30 16:30:22 +03:00
SDE
8c80f43819 0.6.12
colors for subscribe
2023-08-30 16:14:33 +03:00
SDE
15b1c9b5eb Merge remote-tracking branch 'origin/main' 2023-08-30 16:08:32 +03:00
SDE
1298765964 0.6.11
profile subscribe view
2023-08-30 16:08:24 +03:00
7dba4864e9 0.0.211 2023-08-30 16:02:03 +03:00
fcf3063e09 0.0.210 2023-08-30 16:00:08 +03:00
f1bda3ce68 0.0.209 2023-08-30 15:31:59 +03:00
aa6fce9f3d 0.0.208 2023-08-30 15:20:30 +03:00
e852a2b358 0.0.207 2023-08-30 15:03:27 +03:00
SDE
db3d47b36f 0.6.10
profile subscribe view
2023-08-30 14:01:52 +03:00
SDE
7fbc7b6e9d 0.6.9
profile subscribe view
2023-08-30 14:01:15 +03:00
73888ec864 0.0.206 2023-08-30 13:45:56 +03:00
53548046b3 0.0.205 2023-08-30 13:37:25 +03:00
879f452690 0.0.204 2023-08-30 13:28:03 +03:00
bf96a97d0b 0.0.203 2023-08-30 13:16:06 +03:00
SDE
d7c1585b21 0.6.8
find routes page
2023-08-30 13:15:42 +03:00
SDE
d36a59b2a0 Merge remote-tracking branch 'origin/main' 2023-08-30 13:12:45 +03:00
SDE
ee7b6b39d5 0.6.7
find routes page
2023-08-30 13:12:36 +03:00
24215a9526 0.0.202 2023-08-30 13:09:19 +03:00
b4e8a730a0 Merge remote-tracking branch 'origin/main' 2023-08-30 13:07:56 +03:00
44b1b8ac56 0.0.201 2023-08-30 13:04:18 +03:00
SDE
51d5ed1dae Merge remote-tracking branch 'origin/main' 2023-08-30 12:39:05 +03:00
SDE
34737e04bc 0.6.6
fix create route
2023-08-30 12:38:56 +03:00
c3163a8571 0.0.200 2023-08-30 12:31:52 +03:00
411568fecd 0.0.199 2023-08-30 12:29:26 +03:00
e19c9610e8 Merge remote-tracking branch 'origin/main' 2023-08-29 18:55:04 +03:00
6c0b187d25 0.0.198 2023-08-29 18:54:45 +03:00
SDE
252ca6f494 0.6.5
find routes
2023-08-29 18:47:35 +03:00
SDE
e206e7e64f 0.6.4
fix routes models
2023-08-29 18:39:35 +03:00
SDE
2d42d03dc8 0.6.3
fix routes models
2023-08-29 18:37:12 +03:00
SDE
01863bcf38 0.6.2
fix routes models
2023-08-29 18:34:11 +03:00
1f3bb2dce9 Merge remote-tracking branch 'origin/main' 2023-08-29 18:10:12 +03:00
96f48f88df 0.0.196 2023-08-29 18:10:03 +03:00
3312b825fc 0.0.120 error message for wrong data in arrival 2023-08-29 17:53:40 +03:00
e97d52d8a7 Merge remote-tracking branch 'origin/main' 2023-08-29 17:27:27 +03:00
0a2019e833 0.0.119 reset arrival datetime-local 2023-08-29 17:27:21 +03:00
6198ccaa97 0.0.195 2023-08-29 17:21:17 +03:00
63f0fe8dd9 0.0.194 2023-08-29 17:10:35 +03:00
00799916c6 0.0.192 2023-08-29 17:09:31 +03:00
SDE
2fce92acf8 0.6.1
routes filter pagination
2023-08-29 17:06:15 +03:00
fdeb2a4ae1 0.0.193 2023-08-29 17:05:10 +03:00
SDE
59fb628ced 0.6.0
SubscribesApp
2023-08-29 16:57:09 +03:00
1d56d81a3e Merge remote-tracking branch 'origin/main' 2023-08-29 16:31:58 +03:00
19f62a0593 0.0.118 2023-08-29 16:31:52 +03:00
SDE
26e923ab58 0.5.5
find route limit slice
2023-08-29 16:30:31 +03:00
58f42c4651 0.0.192 2023-08-29 16:22:49 +03:00
a470bc3325 Merge remote-tracking branch 'origin/main' 2023-08-29 15:52:12 +03:00
b5652acad6 0.0.191 2023-08-29 15:52:07 +03:00
46bc6ea65a Merge remote-tracking branch 'origin/main' 2023-08-29 15:21:14 +03:00
f7fa3ef681 Merge remote-tracking branch 'origin/main' 2023-08-29 15:20:45 +03:00
270c56b1b5 0.0.190 2023-08-29 15:20:39 +03:00
380dc4d77c 0.0.117 select my_routes after send new_route 2023-08-29 15:15:58 +03:00
ca01e1a728 Merge remote-tracking branch 'origin/main' 2023-08-29 15:01:48 +03:00
1f39da2b45 0.0.116 disabled for send_new_route 2023-08-29 15:01:36 +03:00
bac7b7a2d5 Merge remote-tracking branch 'origin/main' 2023-08-29 14:53:46 +03:00
4cd6d1d0f5 0.0.189 2023-08-29 14:53:33 +03:00
8b55479d0a Merge remote-tracking branch 'origin/main'
# Conflicts:
#	templates/blocks/profile/b_new_route.html
2023-08-29 13:54:15 +03:00
b9efd41a48 0.0.115 success set min datatime-local 2023-08-29 13:53:18 +03:00
SDE
90f880ee70 0.5.4
find route limit slice
2023-08-29 13:33:53 +03:00
SDE
c8c0f43d4d 0.5.3
find route limit slice
2023-08-29 13:31:00 +03:00
de0446c799 0.0.188 2023-08-29 12:47:38 +03:00
4ee4d3f836 Merge remote-tracking branch 'origin/main' 2023-08-29 12:42:19 +03:00
408da784e1 0.0.114 set min datatime-local 2023-08-29 12:42:13 +03:00
5e77e204ba Merge remote-tracking branch 'origin/main' 2023-08-29 12:27:28 +03:00
3a087616ee 0.0.183 2023-08-29 12:27:22 +03:00
SDE
e97dc7fcee 0.5.2
find route limit by movers
2023-08-29 12:03:33 +03:00
SDE
1873933416 0.5.1
ArticlesApp
2023-08-29 11:47:09 +03:00
45001b5d3c 0.0.182 2023-08-28 18:29:59 +03:00
db0a40de71 0.0.181 2023-08-28 18:14:20 +03:00
fc4095a7e4 Merge remote-tracking branch 'origin/main' 2023-08-28 18:06:01 +03:00
ec8fded136 0.0.180 2023-08-28 18:05:53 +03:00
SDE
237524be8e 0.5.0
ArticlesApp
2023-08-28 18:05:50 +03:00
4354d1369d 0.0.112 2023-08-28 17:21:03 +03:00
0457f1f476 0.0.111 2023-08-28 15:48:38 +03:00
05ea2a4076 0.0.110 mover, customer buttons 2023-08-28 15:24:25 +03:00
8eaf664de6 0.0.178 2023-08-28 14:59:55 +03:00
3b615607a9 0.0.109 2023-08-28 14:52:18 +03:00
0e810aa790 0.0.108 add dropdown_menu 2023-08-28 14:35:25 +03:00
f92f7b6ba8 0.0.177 2023-08-28 13:43:58 +03:00
fcb5df09b1 0.0.176 2023-08-28 13:19:06 +03:00
SDE
188866eda1 0.4.5
routes filter
2023-08-25 15:31:11 +03:00
SDE
f1246b3b27 0.4.4
routes filter
2023-08-25 15:19:53 +03:00
2fcc7321cb 0.0.175 2023-08-25 15:16:35 +03:00
e8b465be5a Merge remote-tracking branch 'origin/main' 2023-08-25 15:11:21 +03:00
ca03dcaa6e 0.0.174 2023-08-25 15:10:54 +03:00
SDE
fb31feac39 0.4.3
routes filter
2023-08-25 14:36:03 +03:00
f5726c8e2e 0.0.173 2023-08-25 14:23:11 +03:00
2d1a3464e7 0.0.172 2023-08-25 14:16:15 +03:00
16eda7c0af 0.0.171 2023-08-25 14:12:51 +03:00
1e152c7dab 0.0.170 2023-08-24 18:50:47 +03:00
SDE
6a2debcc2d 0.4.2
routes filter
2023-08-24 18:00:01 +03:00
SDE
ec07a69c1e Merge remote-tracking branch 'origin/main' 2023-08-24 17:53:10 +03:00
SDE
df02cc1529 0.4.0
routes filter
2023-08-24 17:53:01 +03:00
0d1ca2bb51 0.0.169 2023-08-24 17:34:52 +03:00
b0de8e9159 0.0.168 2023-08-24 17:00:11 +03:00
3ca5f61221 Merge remote-tracking branch 'origin/main' 2023-08-24 16:55:14 +03:00
2a56bb093b 0.0.167 2023-08-24 16:55:08 +03:00
SDE
30b090c527 0.3.35
support chat v3
2023-08-24 16:48:13 +03:00
4f294455cc 0.0.166 2023-08-24 16:47:24 +03:00
fa360267a0 0.0.165 2023-08-24 15:18:58 +03:00
40fc623123 0.0.164 2023-08-24 13:16:05 +03:00
f1596e1856 0.0.163 2023-08-24 13:10:38 +03:00
SDE
66f348d4a0 0.3.34
support chat v3
2023-08-24 12:50:53 +03:00
1e59047071 0.0.162 2023-08-24 12:48:16 +03:00
2f31fa3e10 0.0.161 2023-08-24 12:31:33 +03:00
SDE
27f8663051 0.3.33
support chat v3
2023-08-24 12:20:37 +03:00
SDE
cf79c78d41 0.3.32
support chat v3
2023-08-23 14:50:51 +03:00
SDE
82fa7edc18 0.3.31
support chat v3
2023-08-18 16:11:41 +03:00
SDE
48da2b2848 0.3.30
support chat v3
2023-08-18 15:49:20 +03:00
80a87fb8e7 0.0.160 2023-08-18 15:37:28 +03:00
00952a0e3e 0.0.159 2023-08-18 15:09:04 +03:00
945d401004 0.0.158 2023-08-18 13:25:31 +03:00
fcc86d079a 0.0.157 2023-08-18 13:18:41 +03:00
c6a8862b43 0.0.156 2023-08-18 13:17:05 +03:00
SDE
352daf8c36 0.3.29
support chat v3
2023-08-18 13:10:28 +03:00
0be71f7497 0.0.155 2023-08-18 13:03:13 +03:00
393d2de7bc 0.0.154 2023-08-18 12:42:36 +03:00
cd8b59e225 0.0.153 2023-08-17 18:00:24 +03:00
SDE
e36cb78a15 Merge remote-tracking branch 'origin/main' 2023-08-17 17:39:01 +03:00
SDE
a06830ae9d 0.3.28
support chat v3
2023-08-17 17:38:54 +03:00
573ca32fe1 Merge remote-tracking branch 'origin/main' 2023-08-17 17:38:31 +03:00
a3e5c50940 0.0.107 rewrite footer without display:flex 2023-08-17 17:38:26 +03:00
ddf9f74db5 0.0.152 2023-08-17 17:11:47 +03:00
e6bc30cb80 Merge remote-tracking branch 'origin/main' 2023-08-17 16:25:44 +03:00
20ed037311 0.0.106 2023-08-17 16:25:19 +03:00
9562796884 Merge remote-tracking branch 'origin/main' 2023-08-17 16:21:06 +03:00
0ea84c3db4 0.0.151 2023-08-17 16:21:00 +03:00
a2925ad45c Merge remote-tracking branch 'origin/main' 2023-08-17 16:08:36 +03:00
895df67b1c 0.0.105 2023-08-17 16:08:20 +03:00
SDE
00f99ce667 0.3.27
support chat v3
2023-08-17 15:04:07 +03:00
4db864ea7b 0.0.104 2023-08-17 14:48:01 +03:00
7dc7ead417 Merge remote-tracking branch 'origin/main' 2023-08-17 14:32:11 +03:00
5c3bffdda4 0.0.103 2023-08-17 14:32:03 +03:00
dd8551c0c6 0.0.150 2023-08-17 14:24:34 +03:00
SDE
d976ce9c89 Merge remote-tracking branch 'origin/main' 2023-08-16 18:02:52 +03:00
SDE
4a8d2ad8c8 0.3.26
support chat v3
2023-08-16 18:02:39 +03:00
1018b4b318 0.0.102 2023-08-16 16:08:40 +03:00
3c17276e27 0.0.101 2023-08-16 15:52:34 +03:00
bf451393b6 0.0.149 2023-08-16 15:45:34 +03:00
4fa6ce6141 Merge remote-tracking branch 'origin/main' 2023-08-16 15:36:14 +03:00
51a65743d1 0.0.148 2023-08-16 15:36:06 +03:00
SDE
60bfa0fadb Merge remote-tracking branch 'origin/main' 2023-08-16 15:33:54 +03:00
SDE
e86aa76002 0.3.25
support chat v3
2023-08-16 15:33:46 +03:00
2d48ecc855 Merge remote-tracking branch 'origin/main' 2023-08-16 15:27:10 +03:00
a73adde79a 0.0.100 2023-08-16 15:27:05 +03:00
4d8ee4bfcc Merge remote-tracking branch 'origin/main' 2023-08-16 15:15:22 +03:00
0acfaca8f6 0.0.147 2023-08-16 15:15:17 +03:00
9dd4577bc7 0.0.99 remove display:flex 2023-08-16 15:09:00 +03:00
66c142a4d6 0.0.98 2023-08-16 13:30:21 +03:00
54126265b2 0.0.97 preloader fix 2023-08-16 12:07:19 +03:00
ebe09382c0 Merge remote-tracking branch 'origin/main' 2023-08-15 17:56:46 +03:00
957b8c9041 0.0.97 preloader for addres_point 2023-08-15 17:56:38 +03:00
e8e9d6af41 0.0.146 2023-08-15 17:50:11 +03:00
SDE
31c51d6c0e Merge remote-tracking branch 'origin/main' 2023-08-15 17:38:30 +03:00
SDE
64fd9c7b17 0.3.24
support chat v3
2023-08-15 17:38:23 +03:00
9e0b725b2f Merge remote-tracking branch 'origin/main' 2023-08-15 17:36:55 +03:00
3588bd14e3 0.0.96 2023-08-15 17:36:46 +03:00
SDE
4f016990ea 0.3.23
support chat v3
2023-08-15 17:34:26 +03:00
571e4b0cc1 0.0.145 2023-08-15 17:30:03 +03:00
SDE
f5361f679a 0.3.22
support chat v3
2023-08-15 17:16:40 +03:00
SDE
662138ac00 Merge remote-tracking branch 'origin/main' 2023-08-15 16:23:24 +03:00
SDE
62c7694c50 0.3.21
support chat v3
2023-08-15 16:23:16 +03:00
4c6e2200c0 0.0.144 2023-08-15 16:17:52 +03:00
SDE
9d0998fb2b 0.3.20
support chat v3
2023-08-15 16:05:41 +03:00
e9d4d71c9d Merge remote-tracking branch 'origin/main' 2023-08-15 15:51:57 +03:00
a970f06dc8 0.0.95 2023-08-15 15:50:49 +03:00
7cbe8db366 0.0.143 2023-08-15 15:25:28 +03:00
8e07b3c304 0.0.142 2023-08-15 15:20:37 +03:00
9e3306f1be 0.0.141 2023-08-15 15:16:32 +03:00
122e041b08 Merge remote-tracking branch 'origin/main' 2023-08-15 15:10:47 +03:00
35da421ad2 0.0.94 2023-08-15 15:10:40 +03:00
cc82bb8f36 Merge remote-tracking branch 'origin/main' 2023-08-15 12:51:27 +03:00
4b05dcc7a7 0.0.140 2023-08-15 12:51:21 +03:00
e280283e40 0.0.93 2023-08-15 12:45:36 +03:00
b13c76816d 0.0.92 2023-08-15 12:42:27 +03:00
cbee5af4ed 0.0.91 new error msg 2023-08-15 12:20:35 +03:00
26877620b1 0.0.139 2023-08-15 11:09:34 +03:00
3c3daf13cd 0.0.138 2023-08-14 21:35:13 +03:00
SDE
87b8b96f15 0.3.19
support chat v3
2023-08-14 19:15:26 +03:00
509052cc61 0.0.137 2023-08-14 18:57:53 +03:00
SDE
d573df0359 0.3.18
support chat v3
2023-08-14 18:39:45 +03:00
SDE
5890415b06 0.3.17
support chat v3
2023-08-14 18:12:32 +03:00
9bef9366b3 0.0.136 2023-08-14 18:08:18 +03:00
SDE
9ed3e7d0dd 0.3.16
support chat v3
2023-08-14 17:44:52 +03:00
SDE
e2bed36c5f 0.3.15
support chat v3
2023-08-14 17:32:00 +03:00
SDE
11cfcda4fb 0.3.14
support chat v3 switch to redis
2023-08-14 17:14:20 +03:00
12213d83c4 0.0.135 2023-08-14 15:14:33 +03:00
SDE
24f26dd0ea 0.3.13
support chat v3
2023-08-14 14:54:22 +03:00
3465563b27 0.0.134 2023-08-14 14:14:05 +03:00
42a5452fc5 0.0.133 2023-08-13 20:06:18 +03:00
56bc23375d 0.0.132 2023-08-13 19:48:55 +03:00
05cd51ffc0 0.0.130 2023-08-13 19:41:52 +03:00
SDE
bea4c1420b 0.3.12
chat v3
2023-08-13 19:24:50 +03:00
1c569b7358 0.0.129 2023-08-13 18:51:33 +03:00
SDE
3ece611ea0 Merge remote-tracking branch 'origin/main' 2023-08-13 18:39:35 +03:00
SDE
604a9c198a 0.3.11
chat v3
2023-08-13 18:39:29 +03:00
8b03b33e22 0.0.128 2023-08-13 18:38:14 +03:00
0edc2c3b0b 0.0.127 2023-08-13 18:28:38 +03:00
SDE
2e2b19d5e0 0.3.10
chat v3
2023-08-13 18:25:54 +03:00
SDE
2bf5b18197 0.3.9
chat v3
2023-08-13 17:52:21 +03:00
5e79bf8263 0.0.126 2023-08-13 17:27:08 +03:00
25b785c5ea 0.0.125 2023-08-13 17:02:35 +03:00
SDE
c1b8896799 0.3.8
chat v3
2023-08-13 17:00:54 +03:00
af5ed27902 0.0.124 2023-08-13 15:48:08 +03:00
SDE
9954efee98 0.3.7
chat v3
2023-08-13 15:37:38 +03:00
SDE
5d806d1498 0.3.6
chat v3
2023-08-13 14:50:35 +03:00
2dd03e4d2f 0.0.123 2023-08-13 14:42:52 +03:00
eb0c143166 0.0.122 2023-08-13 13:41:44 +03:00
SDE
6d70fad14b 0.3.5
chat v3
2023-08-13 13:26:26 +03:00
SDE
a5362bd2ad 0.3.4
chat v3
2023-08-13 13:25:21 +03:00
0a8151f8fa 0.0.121 2023-08-12 16:20:08 +03:00
437ee3066f 0.0.120 2023-08-12 14:37:27 +03:00
f5c709eb7b Merge remote-tracking branch 'origin/main' 2023-08-12 13:49:35 +03:00
0a8d836d56 0.0.119 2023-08-12 13:49:27 +03:00
SDE
c6cc191e17 Merge remote-tracking branch 'origin/main' 2023-08-12 13:49:14 +03:00
SDE
fd99e96d93 0.3.3
chat v3
2023-08-12 13:49:05 +03:00
640e68d096 0.0.118 2023-08-12 13:48:52 +03:00
SDE
cff935e633 0.3.2
chat v3
2023-08-12 13:42:20 +03:00
1d64583697 0.0.117 2023-08-12 13:14:26 +03:00
7c7452a93d 0.0.116 2023-08-12 13:00:27 +03:00
03882addc1 0.0.115 2023-08-12 12:55:38 +03:00
9ea81709eb 0.0.114 2023-08-12 12:50:38 +03:00
27728a24a1 0.0.113 2023-08-12 12:42:39 +03:00
SDE
ffbdd5aeb0 0.3.1
chat v3
2023-08-12 12:32:39 +03:00
SDE
78ade08e65 0.3.0
chat v3
2023-08-11 23:25:28 +03:00
SDE
aeb90e106e 0.2.24
chat v2
2023-08-11 22:55:57 +03:00
4f91767c8a 0.0.112 2023-08-11 19:45:40 +03:00
bb94eba766 0.0.111 2023-08-11 19:31:57 +03:00
SDE
9e9cd03a21 0.2.23
chat v2
2023-08-11 19:23:51 +03:00
7eeabdca79 0.0.110 2023-08-11 19:04:45 +03:00
dcd7c36f66 0.0.109 2023-08-11 19:04:10 +03:00
cf5a404b46 0.0.108 2023-08-11 19:02:26 +03:00
SDE
e60c18c2e2 0.2.22
chat v2
2023-08-11 18:58:07 +03:00
274 changed files with 43172 additions and 2671 deletions

1
.gitignore vendored
View File

@@ -414,4 +414,5 @@ fabric.properties
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
celerybeat-schedule.db

0
ArticlesApp/__init__.py Normal file
View File

157
ArticlesApp/admin.py Normal file
View File

@@ -0,0 +1,157 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
from django.utils.translation import gettext_lazy as _
from ArticlesApp.models import ArticleModel, UserPageModel
from sets.admin import *
# class Admin_ArticlesPageSets(Admin_BaseIconModel):
#
# fieldsets = (
# (None, {
# 'classes': ['wide'],
# 'fields': (
# 'name', 'url', 'description', 'picture'
# )
#
#
# }),
# ('Управление отображением страницы', {
# 'classes': ['wide'],
# 'fields': (
# # ('serviceBlockStateShow'),
# # ('articlesCountInBlock'),
# # ('advicesCountInBlock'),
# )
# }),
#
# ('SEO', {
# 'classes': ['wide', 'collapse'],
# 'fields': ('seo_title', 'seo_description','seo_keywords', 'seo_text')
# }),
#
# )
#
# prepopulated_fields = {"url": ("name",)}
# list_display = ('name', 'url', 'seo_title', 'createDT')
# # list_editable = ('dish_price', 'allowForDelivery', 'dish_maxDiscount','dish_rating',
# # 'dish_enabled')
# # filter_horizontal = ['service_works',]
# # date_hierarchy = 'createDT'
# # list_filter = ('device_name', 'vendor',)
# list_display_links = ('name',)
# save_on_top = True
#
# def has_add_permission(self, request):
# return not ArticlesPageSets.objects.all().count() > 0
#
# def has_delete_permission(self, request, obj=None):
# if not request.user.is_superuser:
# return False
# else:
# return True
#
# admin.site.register(ArticlesPageSets,Admin_ArticlesPageSets)
class Admin_Article(Admin_Trans_BaseModelViewPage):
fieldsets = (
(None, {
'classes': ['wide'],
'fields': (('name'),
('url'),
# 'pub_DT',
'picture',
# ('devices'),
)
}),
(_('Статья'), {
'classes': ['wide'],
'fields': (
'description', 'text',
)
}),
('SEO', {
'classes': ['wide', 'collapse'],
'fields': ('seo_title', 'seo_description','seo_keywords')
}),
)
prepopulated_fields = {"url": ("name",)}
list_display = (
# 'image_thumb',
'name', 'url', 'order', 'createDT')
list_editable = ('order',) # 'allowForDelivery', 'dish_maxDiscount','dish_rating',
# 'dish_enabled')
# filter_horizontal = ['sites']
date_hierarchy = 'createDT'
list_filter = ('enable', 'createDT')
list_display_links = (
'name',
# 'image_thumb',
)
save_on_top = True
admin.site.register(ArticleModel, Admin_Article)
# class Admin_ArticleSliders(admin.TabularInline):
# model = ArticleModel.SliderSets.through
class Admin_UserPage(Admin_Trans_BaseModelViewPage):
fieldsets = (
(None, {
'classes': ['wide'],
'fields': (('name'),
('url'),
# 'pub_DT',
# ('devices'),
)
}),
(_('Статья'), {
'classes': ['wide'],
'fields': ('picture', 'description', 'text')
}),
('SEO', {
'classes': ['wide', 'collapse'],
'fields': ('seo_title', 'seo_description','seo_keywords')
}),
)
prepopulated_fields = {"url": ("name",)}
list_display = (
# 'image_thumb',
'name', 'url', 'order', 'seo_title', 'createDT')
list_editable = ('order', ) # 'allowForDelivery', 'dish_maxDiscount','dish_rating',
# 'dish_enabled')
# filter_horizontal = ['sites']
date_hierarchy = 'createDT'
list_filter = ('enable', 'createDT')
list_display_links = (
'name',
# 'image_thumb',
)
save_on_top = True
actions = ['create_copy']
def has_delete_permission(self, request, obj=None):
if request.user.is_superuser:
return True
if obj and obj.url in ('for-partners', 'dealers', 'contacts'):
return False
return True
admin.site.register(UserPageModel,Admin_UserPage)

45
ArticlesApp/funcs.py Normal file
View File

@@ -0,0 +1,45 @@
from .models import *
elements_on_page = 10
def get_articles(art_kwargs, request_Data=None, from_el=None, to_el=None):
if request_Data:
if not from_el and 'from_el' in request_Data:
from_el = request_Data['from_el']
if not to_el and 'to_el' in request_Data:
to_el = request_Data['to_el']
arts = ArticleModel.objects.filter(**art_kwargs).order_by('-createDT')
el_count = arts.count()
if from_el and to_el:
arts = arts[from_el:to_el]
elif from_el:
arts = arts[from_el:]
elif to_el:
arts = arts[:to_el]
else:
to_el = elements_on_page
arts = arts[:to_el]
last_block = False
if not to_el or to_el >= el_count:
last_block = True
if el_count - to_el > elements_on_page:
next_page_els_count = elements_on_page
else:
next_page_els_count = el_count - to_el
Dict = {
'articles': arts,
'last_block': last_block,
'last_el': to_el,
'next_page_els_count': next_page_els_count,
'elements_on_page': elements_on_page
}
return Dict

10
ArticlesApp/js_urls.py Normal file
View File

@@ -0,0 +1,10 @@
# coding=utf-8
from django.urls import path
# from AuthApp.js_views import *
# from AuthApp.import_funcs import *
from .js_views import *
urlpatterns = [
path('get_articles_block/', get_articles_block_ajax, name='get_articles_block_ajax'),
]

55
ArticlesApp/js_views.py Normal file
View File

@@ -0,0 +1,55 @@
import json
from django.shortcuts import render
from uuid import uuid1
from .models import *
from django.contrib import auth
from django.http import HttpResponse, Http404, JsonResponse
from django.template import loader, RequestContext
from django.contrib.auth.decorators import login_required
from BaseModels.mailSender import techSendMail
from django.utils.translation import gettext as _
from datetime import datetime
from django.template.loader import render_to_string
from django.urls import reverse
# from .forms import *
from .funcs import *
def get_articles_block_ajax(request):
if request.method != 'POST':
raise Http404
try:
data = request.POST.dict()
if not data and request.body:
data = json.loads(request.body)
art_kwargs = {}
Dict = get_articles(art_kwargs=art_kwargs, request_Data=data)
if 'errors' in Dict:
return JsonResponse(Dict, status=400)
html = render_to_string('blocks/articles/b_news_elements.html', Dict, request=request)
res_Dict = {
'html': html,
'last_block': Dict['last_block'],
'next_page_els_count': Dict['next_page_els_count'],
# 'form': RouteForm(initial=data)
}
return JsonResponse(res_Dict)
except Exception as e:
errors_Dict = {
'errors': {
'all__': f'{_("ошибка в запросе")} = {str(e)}'
}
}
return JsonResponse(errors_Dict, status=400)

View File

@@ -0,0 +1,103 @@
# Generated by Django 4.2.2 on 2023-08-28 17:17
import ckeditor.fields
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='ArticleModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_ru', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_en', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
('url', models.TextField(help_text='можно изменить адрес страницы (!!! ВНИМАНИЕ !!! поисковые системы потеряют страницу и найдут лишь спустя неделю...месяц)', unique=True, verbose_name='URL привязанной страницы')),
('title', models.TextField(blank=True, null=True, verbose_name='Заголовок')),
('description', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
('description_ru', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
('description_en', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
('picture', models.ImageField(blank=True, null=True, upload_to='uploads/', verbose_name='Картинка')),
('visible', models.BooleanField(default=True, verbose_name='Отображать')),
('background_image_left', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Левая подложка')),
('background_image_right', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Правая подложка')),
('seo_title', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
('seo_title_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
('seo_title_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
('seo_description', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
('seo_description_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
('seo_description_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
('seo_keywords', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
('seo_keywords_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
('seo_keywords_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
('seo_text', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
('seo_text_ru', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
('seo_text_en', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
('FAQ_title', models.CharField(blank=True, max_length=250, null=True, verbose_name='FAQ Заголовок')),
('text', ckeditor.fields.RichTextField(verbose_name='Текст')),
('text_ru', ckeditor.fields.RichTextField(null=True, verbose_name='Текст')),
('text_en', ckeditor.fields.RichTextField(null=True, verbose_name='Текст')),
],
options={
'verbose_name': 'Статья',
'verbose_name_plural': 'Статьи',
},
),
migrations.CreateModel(
name='UserPageModel',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_ru', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_en', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
('url', models.TextField(help_text='можно изменить адрес страницы (!!! ВНИМАНИЕ !!! поисковые системы потеряют страницу и найдут лишь спустя неделю...месяц)', unique=True, verbose_name='URL привязанной страницы')),
('title', models.TextField(blank=True, null=True, verbose_name='Заголовок')),
('description', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
('description_ru', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
('description_en', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
('picture', models.ImageField(blank=True, null=True, upload_to='uploads/', verbose_name='Картинка')),
('visible', models.BooleanField(default=True, verbose_name='Отображать')),
('background_image_left', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Левая подложка')),
('background_image_right', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Правая подложка')),
('seo_title', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
('seo_title_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
('seo_title_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
('seo_description', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
('seo_description_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
('seo_description_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
('seo_keywords', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
('seo_keywords_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
('seo_keywords_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
('seo_text', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
('seo_text_ru', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
('seo_text_en', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
('FAQ_title', models.CharField(blank=True, max_length=250, null=True, verbose_name='FAQ Заголовок')),
('text', ckeditor.fields.RichTextField(verbose_name='Текст')),
('text_ru', ckeditor.fields.RichTextField(null=True, verbose_name='Текст')),
('text_en', ckeditor.fields.RichTextField(null=True, verbose_name='Текст')),
],
options={
'verbose_name': 'Пользовательская страница',
'verbose_name_plural': 'Пользовательские страницы',
},
),
]

View File

@@ -0,0 +1,104 @@
# Generated by Django 4.2.2 on 2023-09-22 13:29
import ckeditor_uploader.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('ArticlesApp', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='articlemodel',
name='description',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='articlemodel',
name='description_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='articlemodel',
name='description_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='articlemodel',
name='seo_text',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='articlemodel',
name='seo_text_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='articlemodel',
name='seo_text_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='articlemodel',
name='text',
field=ckeditor_uploader.fields.RichTextUploadingField(verbose_name='Текст'),
),
migrations.AlterField(
model_name='articlemodel',
name='text_en',
field=ckeditor_uploader.fields.RichTextUploadingField(null=True, verbose_name='Текст'),
),
migrations.AlterField(
model_name='articlemodel',
name='text_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(null=True, verbose_name='Текст'),
),
migrations.AlterField(
model_name='userpagemodel',
name='description',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='userpagemodel',
name='description_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='userpagemodel',
name='description_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='userpagemodel',
name='seo_text',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='userpagemodel',
name='seo_text_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='userpagemodel',
name='seo_text_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='userpagemodel',
name='text',
field=ckeditor_uploader.fields.RichTextUploadingField(verbose_name='Текст'),
),
migrations.AlterField(
model_name='userpagemodel',
name='text_en',
field=ckeditor_uploader.fields.RichTextUploadingField(null=True, verbose_name='Текст'),
),
migrations.AlterField(
model_name='userpagemodel',
name='text_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(null=True, verbose_name='Текст'),
),
]

View File

38
ArticlesApp/models.py Normal file
View File

@@ -0,0 +1,38 @@
# -*- coding: utf-8 -*-
__author__ = 'sync'
from django.db import models
from BaseModels.base_models import BaseModelViewPage, BaseModel
# from ckeditor.fields import RichTextField
from ckeditor_uploader.fields import RichTextUploadingField
from django.utils.translation import gettext_lazy as _
# class ArticlesPageSets(BaseModelViewPage):
#
# class Meta:
# verbose_name = 'Настройки страницы архива статей'
# verbose_name_plural = 'Настройки страницы архива статей'
class UserPageModel(BaseModelViewPage):
# pub_DT = models.DateTimeField(verbose_name=u'Дата и время публикации', auto_created=True)
text = RichTextUploadingField(verbose_name=_('Текст'))
class Meta:
verbose_name=_("Пользовательская страница")
verbose_name_plural =_("Пользовательские страницы")
# unique_together = ('url', 'region')
# managed=True
# app_label = u'ArticlesApp'
class ArticleModel(BaseModelViewPage):
# pub_DT = models.DateTimeField(verbose_name=u'Дата и время публикации', auto_created=True)
text = RichTextUploadingField(verbose_name=_("Текст"))
class Meta:
verbose_name=_("Статья")
verbose_name_plural =_("Статьи")

View File

@@ -0,0 +1 @@
__author__ = 'SDE'

View File

@@ -0,0 +1,48 @@
__author__ = 'SDE'
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
# @register.filter('get_side_art')
@register.simple_tag
def get_side_art(last_el, counter, els_on_page):
cur_el = last_el - els_on_page + counter
if els_on_page < 4:
first_left_el = els_on_page + 1
else:
first_left_el = 4
if (cur_el - first_left_el) % 2:
return 'even'
else:
return 'odd'
# @register.filter()
# def get_numbers_list(from_el, to_el):
# res = range(from_el, to_el+1)
# return res
#
#
# def val_type(value):
# res = type(value)
# return res.__name__
# register.filter('val_type', val_type)
#
# @register.filter()
# def get_cols_table_data_for_row_when_cols3(value, row):
# el_count = 3
# from_el = (row-1) * el_count
# to_el = row * el_count
# part = list(value)[from_el:to_el]
# return part
# # register.filter('val_type', val_type)
#
#
# @register.filter
# @stringfilter
# def correct_for_tables(value):
# if value in ['None', '0.0']:
# return '-'
# return value

16
ArticlesApp/tests.py Normal file
View File

@@ -0,0 +1,16 @@
"""
This file demonstrates writing tests using the unittest module. These will pass
when you run "manage.py test".
Replace this with more appropriate tests for your application.
"""
from django.test import TestCase
class SimpleTest(TestCase):
def test_basic_addition(self):
"""
Tests that 1 + 1 always equals 2.
"""
self.assertEqual(1 + 1, 2)

View File

@@ -0,0 +1,26 @@
# -*- coding: utf-8 -*-
from modeltranslation.translator import translator, TranslationOptions, register
from .models import *
@register(ArticleModel)
class Article_TranslationOptions(TranslationOptions):
fields = (
'name', 'description', 'seo_title', 'seo_description', 'seo_keywords', 'seo_text',
'text'
)
# translator.register(ArticleModel, Article_TranslationOptions)
@register(UserPageModel)
class UserPage_TranslationOptions(TranslationOptions):
fields = (
'name', 'description', 'seo_title', 'seo_description', 'seo_keywords', 'seo_text',
'text'
)
# translator.register(UserPageModel, UserPage_TranslationOptions)

5
ArticlesApp/urls.py Normal file
View File

@@ -0,0 +1,5 @@
from django.urls import path
from .views import *
urlpatterns = [
]

View File

@@ -0,0 +1,9 @@
from django.urls import path
from .views import *
urlpatterns = [
path('articles/', ArticlesPageView, name=u'articles'),
path('articles/<int:year>/', ArticlesPageView, name=u'articles_by_year'),
path('article/<str:art_url>/', ArticlesOnePageView, name=u'article_one'),
path('info_page/<str:page_url>/', UserPageView, name=u'user_page'),
]

159
ArticlesApp/views.py Normal file
View File

@@ -0,0 +1,159 @@
# -*- coding: utf-8 -*-
from .models import *
from datetime import datetime, date
from django.http import Http404, HttpResponse
from django.template import loader
from .funcs import *
from GeneralApp.funcs import get_inter_http_respose
from django.utils.translation import gettext_lazy as _
# from django.contrib.auth.decorators import login_required
# from BaseModels.search_optimization.ld_json.ld_article_news import get_ld_article_news
# from django.contrib.sites.shortcuts import get_current_site
def get_flat_pages_links_Dict(site):
flat_pages_links = UserPageModel.objects.filter(
url__in=('for-partners', 'contacts'),
sites=site
).values_list('url', flat=True)
return {'flat_pages_links': flat_pages_links}
def get_article_breadcrumbs(request, art):
# print('get_article_breadcrumbs')
articles_add_count = 3
# half_count = articlesCountInBlock / 2
# arts_top = ArticleModel.objects.filter(enable=True, createDT__gt=art.createDT).order_by(
# 'createDT')[:articles_add_count]
# arts_down = ArticleModel.objects.filter(enable=True, createDT__lt=art.createDT).order_by(
# '-createDT')[:articles_add_count]
# if len(artListDown)<half_count:
# art_List = ArticleModel.objects.filter(enable=True, article_DT__gte=art.article_DT).order_by(
# 'article_DT')[:art.articlesCountInBlock-len(artListDown)]
breadcrumbs_arts = ArticleModel.objects.exclude(id=art.id).order_by('?')[:3]
Dict = {
'breadcrumbs_arts': breadcrumbs_arts
# 'arts_top': arts_top,
# 'arts_down': arts_down
}
return Dict
def get_user_pages_breadcrumbs(request, art):
from ArticlesApp.models import UserPageModel
# print('get_user_pages_breadcrumbs')
half_count = art.articlesCountInBlock / 2
art_List = UserPageModel.objects.filter(enable=True, article_DT__gte=art.article_DT).order_by(
'article_DT')[:half_count]
artListDown = UserPageModel.objects.filter(enable=True, article_DT__lt=art.article_DT).order_by(
'-article_DT')[:art.articlesCountInBlock-len(art_List)]
if len(artListDown)<half_count:
art_List = UserPageModel.objects.filter(enable=True, article_DT__gte=art.article_DT).order_by(
'article_DT')[:art.articlesCountInBlock-len(artListDown)]
art_List = list(art_List)
art_List.reverse()
artlist = art_List + list(artListDown)
# print('artlist',artlist)
return artlist
# @login_required(login_url='/admin/')
def ArticlesPageView(request, year=None):
kwargs = {}
if year:
try:
year = int(year)
kwargs.update({'createDT__year': year})
except:
raise Http404
Dict = get_articles(art_kwargs=kwargs)
Dict.update({
'page': {
'title': _('Страница списка новостей'),
'description': _('Все новости сайта tripwb.com'),
'keywords': _('Все новости сайта tripwb.com'),
}
})
t = loader.get_template('pages/p_articles.html')
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))
# @login_required(login_url='/admin/')
def UserPageView(request, page_url):
kwargs = {
'url': page_url
}
try:
art = UserPageModel.objects.get(**kwargs)
except UserPageModel.DoesNotExist:
raise Http404
# service = ServiceModel.objects.filter(enable=True, artDevs=art)[0]
# article_breadcrumbs = get_user_pages_breadcrumbs(request,art)
# print('article_breadcrumbs',article_breadcrumbs)
Dict = {
'page' : art,
'user_page' : True,
# 'service' : service,
# 'article_breadcrumbs' : article_breadcrumbs
}
t = loader.get_template('pages/p_user_page.html')
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))
# @login_required(login_url='/admin/')
def ArticlesOnePageView(request, art_url):
kwargs = {
'url': art_url
}
try:
art = ArticleModel.objects.get(**kwargs)
except ArticleModel.DoesNotExist:
raise Http404
# print('article_breadcrumbs',article_breadcrumbs)
Dict = {
'page' : art,
}
Dict.update(get_article_breadcrumbs(request, art))
t = loader.get_template('pages/p_article.html')
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))

View File

@@ -39,16 +39,17 @@ class Admin_ProfileInline(admin.StackedInline):
(None, {
'classes': ['wide'],
'fields': (
('account_type',),
# ('account_type',),
('enable',),
('phone',),
('country', 'city'),
('mailing_on', ),
('authMailCode',),
('birthdate'),
'comment', 'creator'
)
}),
('Дополнительно', {
(_('Дополнительно'), {
'classes': ['wide'],
'fields': (
('json_data',)
@@ -71,6 +72,10 @@ class Admin_ProfileInline(admin.StackedInline):
class Admin_User(UserAdmin):
def mailing_on(self, obj):
return obj.user_profile.mailing_on
mailing_on.boolean = True
fieldsets = (
(None, {
'classes': ['wide'],
@@ -91,61 +96,63 @@ class Admin_User(UserAdmin):
save_on_top = True
list_display = ['id', 'last_name', 'first_name', 'email', 'is_staff',
list_display = ['id', 'last_name', 'first_name', 'mailing_on', 'email', 'is_staff',
'is_active']
list_editable = ['is_staff', 'is_active']
list_display_links = ['first_name', 'last_name', 'email']
search_fields = ['first_name', 'last_name', 'email']
list_filter = ['user_profile__mailing_on', 'is_staff', 'is_active']
inlines = (Admin_ProfileInline,)
# actions = ['del_all_temp_users', ]
ordering = ['is_staff', 'last_name', 'first_name']
ordering = ['is_staff', '-id', 'last_name', 'first_name']
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, Admin_User)
class Admin_UserProfile(Admin_BaseIconModel):
fieldsets = (
(None, {
'classes': ['wide'],
'fields': (
'user', 'enable',
('account_type',),
('phone',),
('country', 'city'),
('authMailCode',),
('birthdate'),
'creator'
)
}),
('1С', {
'classes': ['wide'],
'fields': (
('name',),
)
}),
)
save_on_top = True
list_display = [
'id', 'user', 'enable', 'birthdate', 'modifiedDT', 'createDT'
]
list_editable = ['enable', 'birthdate']
list_display_links = ['id', ] # 'user__last_name', 'user__first_name']
search_fields = [
'id', 'user__last_name', 'user__first_name', 'user__email',
]
list_filter = ['enable', 'account_type']
# filter_horizontal = ['connected_mailings']
# raw_id_fields = ("favourites",)
verbose_name_plural = _(u'Профиль пользователя')
admin.site.register(UserProfile, Admin_UserProfile)
# class Admin_UserProfile(Admin_BaseIconModel):
#
# fieldsets = (
# (None, {
# 'classes': ['wide'],
# 'fields': (
# 'user', 'enable',
# ('account_type',),
# ('phone',),
# ('country', 'city'),
# ('authMailCode',),
# ('birthdate'),
# 'creator'
# )
# }),
# ('1С', {
# 'classes': ['wide'],
# 'fields': (
# ('name',),
# )
# }),
# )
#
# save_on_top = True
#
# list_display = [
# 'id', 'user', 'enable', 'birthdate', 'modifiedDT', 'createDT'
# ]
# list_editable = ['enable', 'birthdate']
# list_display_links = ['id', ] # 'user__last_name', 'user__first_name']
# search_fields = [
# 'id', 'user__last_name', 'user__first_name', 'user__email',
# ]
#
# list_filter = ['enable', 'account_type']
#
# # filter_horizontal = ['connected_mailings']
# # raw_id_fields = ("favourites",)
# verbose_name_plural = _(u'Профиль пользователя')
#
#
# admin.site.register(UserProfile, Admin_UserProfile)

View File

@@ -14,17 +14,78 @@ class LoginForm(forms.Form):
username = forms.CharField(required=True)
password = forms.CharField(required=True)
class RegistrationForm(forms.Form):
from .models import account_type_choices
account_type = forms.ChoiceField(choices=account_type_choices, initial='sender', required=True)
# account_type = forms.ChoiceField(choices=account_type_choices, initial='sender', required=True)
firstname = forms.CharField(required=False)
lastname = forms.CharField(required=False)
country = forms.CharField(required=False)
city = forms.CharField(required=False)
email = forms.EmailField()
password = forms.CharField(widget=forms.PasswordInput())
confirm_password = forms.CharField(widget=forms.PasswordInput())
tel = forms.CharField()
agreement = forms.BooleanField(initial=False, required=True)
def __init__(self, *args, **kwargs):
required_password = True
required_agreement = True
required_email = True
create_new_account = True
if 'not_required_password' in kwargs.keys() and kwargs['not_required_password']:
required_password = False
del kwargs['not_required_password']
if 'not_required_agreement' in kwargs.keys() and kwargs['not_required_agreement']:
required_agreement = False
del kwargs['not_required_agreement']
if 'not_required_email' in kwargs.keys() and kwargs['not_required_email']:
required_email = False
del kwargs['not_required_email']
if 'create_new_account' in kwargs.keys() and not kwargs['create_new_account']:
create_new_account = False
del kwargs['create_new_account']
super(RegistrationForm, self).__init__(*args, **kwargs)
self.fields['password'].required = required_password
self.fields['confirm_password'].required = required_password
self.fields['agreement'].required = required_agreement
self.fields['email'].required = required_email
self.create_new_account = create_new_account
def clean(self):
cleaned_data = super().clean()
# i = 0
# names = list(cleaned_data.keys())
# while i < len(names):
# if not cleaned_data[names[i]]:
# if self.fields[names[i]].required:
# self.add_error(names[i], _('Обязательное поле'))
# i += 1
if 'tel' in cleaned_data and 'tel' in cleaned_data:
from BaseModels.validators.form_field_validators import get_phone_valid_error
error = get_phone_valid_error(cleaned_data["tel"])
if error:
self.add_error('tel', error)
if cleaned_data and 'confirm_password' in cleaned_data and 'password' in cleaned_data:
if cleaned_data['confirm_password'] != cleaned_data['password']:
self.add_error("password", _('Пароль и подтверждение пароля не совпадают'))
self.add_error("confirm_password", _('Пароль и подтверждение пароля не совпадают'))
if self.create_new_account and cleaned_data and 'email' in cleaned_data:
users = User.objects.filter(email=cleaned_data['email'])
if users:
self.add_error('email', _("Пользователь с указанным email уже существует"))
# # class PersonForm(NgModelFormMixin, NgFormValidationMixin, NgModelForm, Bootstrap3ModelForm):
# #

View File

@@ -1,2 +1,96 @@
from django.template.loader import render_to_string
def get_user_timezone_Dict(user, request=None):
tz = None
if request:
tz = request.COOKIES.get("user_tz")
if not tz and user.is_authenticated:
tz = user.user_profile.get_timezone()
if not tz:
from django.conf import settings
tz = settings.TIME_ZONE
return {'user_tz': tz}
def get_dashboard_page_content_html(request):
from ChatServiceApp.funcs import get_unanswered_msgs_count_for_user
Dict = {
'unanswered_msgs_count': get_unanswered_msgs_count_for_user(request.user)
}
html = render_to_string('blocks/profile/b_profile_first_page.html', Dict, request=request)
return html
def get_profile_page_content_html(request, page_name, data):
if page_name == 'chat':
from ChatServiceApp.funcs import get_chat_page_content_html
return get_chat_page_content_html(request, data)
elif page_name == 'create_route_for_customer':
from RoutesApp.funcs import get_profile_new_route_page_html
return get_profile_new_route_page_html(request, {'owner_type': 'customer'})
elif page_name == 'create_route_for_mover':
from RoutesApp.funcs import get_profile_new_route_page_html
return get_profile_new_route_page_html(request, {'owner_type': 'mover'})
elif page_name == 'my_routes':
from RoutesApp.funcs import get_profile_my_routes_page_content_html
return get_profile_my_routes_page_content_html(request)
elif page_name == 'support':
return get_profile_support_page_content_html(request, data)
elif page_name == 'my_subscribe':
from SubscribesApp.funcs import get_profile_subscribe_page_content_html
return get_profile_subscribe_page_content_html(request)
elif page_name == 'change_profile':
return get_profile_change_page_content_html(request)
elif page_name == 'dashboard':
return get_dashboard_page_content_html(request)
else:
return None
def get_profile_change_page_content_html(request):
init_Dict = {
'firstname': request.user.first_name,
'lastname': request.user.last_name,
'email': request.user.email,
'tel': request.user.user_profile.phone,
'country': request.user.user_profile.country,
'city': request.user.user_profile.city,
}
from .forms import RegistrationForm
form = RegistrationForm(initial=init_Dict)
from SubscribesApp.funcs import get_cur_user_subscribe
Dict = {
'profileForm': form,
'user_subscribe': get_cur_user_subscribe(request.user)
}
html = render_to_string('blocks/profile/b_profile.html', Dict, request=request)
return html
def get_profile_support_page_content_html(request, data=None):
if request.user.is_staff:
from ChatServiceApp.funcs import get_ticketsDict_for_staff
Dict = get_ticketsDict_for_staff(request.user)
tpl_name = 'blocks/profile/b_support_chat.html'
else:
from ChatServiceApp.models import MsgGroup
tickets = MsgGroup.objects.filter(enable=True, owner=request.user).order_by('-modifiedDT')
Dict = {
'tickets': tickets,
}
tpl_name = 'blocks/profile/b_support_tickets.html'
Dict.update(get_user_timezone_Dict(request.user, request=request))
html = render_to_string(tpl_name, Dict, request=request)
return html

View File

@@ -14,13 +14,22 @@ urlpatterns = [
path('new_route_view/', new_route_view_ajax, name='new_route_view_ajax'),
path('dashboard/', dashboard_ajax, name='dashboard_ajax'),
path('my_routes/', my_routes_ajax, name='my_routes_ajax'),
path('subscribe/', subscribe_ajax, name='subscribe_ajax'),
# path('subscribe/', subscribe_ajax, name='subscribe_ajax'),
path('chats/', chats_ajax, name='chats_ajax'),
path('support_tickets/', support_tickets_ajax, name='support_tickets_ajax'),
path('change_profile/', change_profile_ajax, name='change_profile_ajax'),
path('change_profile_confirm/', change_profile_confirm_ajax, name='change_profile_confirm_ajax'),
path('change_avatar_confirm/', change_avatar_confirm_ajax, name='change_avatar_confirm_ajax'),
path('send_message/', send_message_ajax, name='send_message_ajax'),
path('mailing_subscribe/', mailing_subscribe_ajax, name='mailing_subscribe_ajax')
]

View File

@@ -11,18 +11,201 @@ from django.utils.translation import gettext as _
from datetime import datetime
from django.template.loader import render_to_string
from django.urls import reverse
from .funcs import *
from django.core.exceptions import ValidationError
import json
from django.core.files import File
import base64
from django.core.validators import validate_email
from django.urls import reverse
@login_required(login_url='/profile/login/')
def subscribe_ajax(request):
# @login_required(login_url='/profile/login/')
# def subscribe_ajax(request):
# if request.method != 'POST':
# raise Http404
#
# Dict = {
# }
#
# html = render_to_string('blocks/profile/b_subscribe.html', Dict, request=request)
# return JsonResponse({'html': html}, status=200)
def mailing_subscribe_ajax(request):
if request.method != 'POST':
raise Http404
Dict = {
}
try:
email = request.POST['email']
user = None
if request.user and request.user.is_authenticated:
user = request.user
user.user_profile.mailing_on = True
user.user_profile.save(update_fields=['mailing_on'])
return JsonResponse({
'status': 'sended',
'del_form': True,
'html': _('Подписка на рассылку для адреса ') + user.email + _(' одобрена')
})
if not user:
try:
user = User.objects.get(email=email)
except User.DoesNotExist:
user = None
if user:
redirect_url = f"{reverse('login_profile')}?mailingSubscribeRequired=true"
else:
redirect_url = f"{reverse('registration_page')}?mailingSubscribeRequired=true"
return JsonResponse({
'status': 'sended',
'redirect_url': redirect_url,
'email': email
})
except Exception as e:
return JsonResponse({
'status': 'error',
'html': str(e)
}, status=400)
def send_message_ajax(request):
if request.method != 'POST':
raise Http404
try:
data = request.POST
if not data and request.body:
data = request.body
from GeneralApp.funcs_options import get_options_by_opt_types, get_mail_send_options
sets = get_options_by_opt_types(['domain', 'project_name'], only_vals=True)
request_type = None
subject = _('Получен запрос')
if 'form_name' in data:
if data['form_name'] == 'msg_from_advertisement':
subject = _('Получен запрос на рекламу')
request_type = _('запрос на рекламу')
if data['form_name'] == 'msg_from_partners':
subject = _('Получен запрос на подключение к партнерской сети')
request_type = _('запрос на партнерство')
if data['form_name'] == 'msg_from_customer_service':
subject = _('Получен запрос в службу техподдержки')
request_type = _('запрос в техподдержку')
if data['form_name'] == 'msg_from_contacts':
subject = _('Получен запрос со страницы контактов')
request_type = _('запрос со страницы контактов')
if data['form_name'] == 'msg_from_about_service':
subject = _('Получен запрос со страницы О сервисе')
request_type = _('запрос со страницы о сервисе')
if data['form_name'] == 'msg_from_footer':
subject = _('Получен запрос на рассылку')
request_type = _('запрос на рассылку')
request_type_str = ''
name_str = ''
phone_str = ''
email_str = ''
msg_str = ''
form_type = 'one_field'
errors = {}
for name, val in data.items():
if not val:
errors.update({name: _('Обязательное поле')})
if name == 'form_name':
request_type_str = f'<b>{_("Тип запроса")}:</b> {request_type}<br>'
if name == 'name':
name_str = f'<b>{_("Имя")}:</b> {data["name"]}<br>'
if form_type == 'one_field':
form_type = 'two_fields'
if name =='phone':
from BaseModels.validators.form_field_validators import get_phone_valid_error
error = get_phone_valid_error(data["phone"])
if error:
errors.update({name: _(error)})
phone_str = f'<b>{_("Телефон")}:</b> {data["phone"]}<br>'
if name =='email':
try:
error = validate_email(data["email"])
except ValidationError as e:
error = e.message
if error:
errors.update({name: _(error)})
email_str = f'<b>{_("email")}:</b> {data["email"]}<br>'
if name =='text_msg':
msg_str = (f'<b>{_("Сообщение")}:</b><br>'
f'<div style="margin-left: 40px; line-height: 20px;">{data["text_msg"]}</div><br>')
form_type = 'full'
if errors:
Dict = {
'form': data.dict()
}
Dict['form'].update({
'errors': errors
})
tpl = 'forms/f_one_field_form.html'
if form_type == 'full':
tpl = 'forms/f_feedback_form.html'
elif form_type == 'two_fields':
tpl = 'forms/f_commercial_offer.html'
html = render_to_string(tpl, Dict, request)
return JsonResponse({'html': html}, status=400)
Dict = {
'logo': f'{sets["domain"]}/static/img/svg/LogoMobile.svg',
'project_name': sets['project_name'],
'message_title': subject,
'message_text': f'<p><b>{_("ДАННЫЕ ЗАПРОСА")}</b></p>'
f'<p style="padding-left: 20px; line-height: 30px;">'
f'{request_type_str}'
f'{name_str}'
f'{phone_str}'
f'{email_str}'
f'{msg_str}'
f'</p>'
}
html = render_to_string('mail/m_request_offer.html', Dict, request)
from BaseModels.mailSender import admin_send_mail_by_SMTPlib
mail_sets = get_mail_send_options()
to = [mail_sets['sender_email'], 'web@syncsystems.net']
res = admin_send_mail_by_SMTPlib(
mail_sets,
subject=subject,
from_email=mail_sets['sender_email'], to=to,
html_content=html
)
html = render_to_string('widgets/w_msg_send_success.html', Dict, request)
return JsonResponse({
'status': 'sended',
'html': html
})
except Exception as e:
return JsonResponse({
'status': 'error',
'html': str(e)
}, status=400)
html = render_to_string('blocks/profile/b_subscribe.html', Dict, request=request)
return JsonResponse({'html': html}, status=200)
@login_required(login_url='/profile/login/')
def chats_ajax(request):
@@ -49,6 +232,7 @@ def chats_ajax(request):
'receivers': receivers,
# 'messages': cur_chat_msgs
}
Dict.update(get_user_timezone_Dict(request.user, request=request))
html = render_to_string('blocks/profile/b_chats.html', Dict, request=request)
return JsonResponse({'html': html}, status=200)
@@ -58,33 +242,138 @@ def support_tickets_ajax(request):
if request.method != 'POST':
raise Http404
if request.user.is_staff:
from ChatServiceApp.funcs import get_ticketsDict_for_staff
Dict = get_ticketsDict_for_staff(request.user)
tpl_name = 'blocks/profile/b_support_chat.html'
else:
from ChatServiceApp.models import MsgGroup
tickets = MsgGroup.objects.filter(enable=True, owner=request.user).order_by('-modifiedDT')
html = get_profile_support_page_content_html(request)
Dict = {
'tickets': tickets,
}
tpl_name = 'blocks/profile/b_support_tickets.html'
html = render_to_string(tpl_name, Dict, request=request)
return JsonResponse({'html': html}, status=200)
@login_required(login_url='/profile/login/')
def change_avatar_confirm_ajax(request):
from django.core.files.base import ContentFile
from django.core.exceptions import RequestDataTooBig
if request.method != 'POST':
raise Http404
try:
file_data = json.loads(request.body)
head, content = file_data['file'].split(',')
content = base64.b64decode(content)
file = ContentFile(content)
request.user.user_profile.avatar.save(file_data['file_name'], file)
request.user.user_profile.save(update_fields=['avatar'])
except RequestDataTooBig:
msg = _('Слишком большой размер файла. Размер файла не должен быть больше 3МБ')
print(msg)
return JsonResponse({'error': msg}, status=400)
except Exception as e:
msg = f'change_avatar_confirm_ajax Error = {str(e)}'
print(msg)
return JsonResponse({'error': msg}, status=400)
return JsonResponse({'url': request.user.user_profile.avatar.url})
@login_required(login_url='/profile/login/')
def change_profile_confirm_ajax(request):
if request.method != 'POST':
raise Http404
data = request.POST
if not data:
data = json.loads(request.body)
from .forms import RegistrationForm
kwargs = {
'not_required_password': True,
'not_required_agreement': True,
'not_required_email': True,
'create_new_account': False,
}
form = RegistrationForm(data, **kwargs)
if not form.is_valid():
form.initial = data
Dict = {'profileForm': form}
html = render_to_string('blocks/profile/b_profile.html', Dict, request=request)
return JsonResponse({'html': html}, status=400)
data_for_save = {}
users = User.objects.filter(id=request.user.id)
if 'firstname' in data:
data_for_save.update({'first_name': data['firstname']})
if 'lastname' in data:
data_for_save.update({'last_name': data['lastname']})
if 'email' in data:
data_for_save.update({'email': data['email']})
data_for_save.update({'username': data['email']})
if data_for_save:
users.update(**data_for_save)
data_for_save = {}
password = None
confirm_password = None
if 'password' in data:
password = data['password']
if 'confirm_password' in data:
confirm_password = data['confirm_password']
if password and confirm_password:
if password != confirm_password:
errors = {
'password': _("Не совпадают пароли"),
'confirm_password': _("Не совпадают пароли"),
}
raise ValidationError(errors)
request.user.set_password(password)
request.user.save()
data_for_save = {}
user_profiles = UserProfile.objects.filter(user__in=users)
if 'country' in data:
data_for_save.update({'country': data['country']})
if 'city' in data:
data_for_save.update({'city': data['city']})
if 'tel' in data:
data_for_save.update({'phone': data['tel']})
if data_for_save:
user_profiles.update(**data_for_save)
html = get_profile_change_page_content_html(request)
return JsonResponse({'html': html}, status=200)
@login_required(login_url='/profile/login/')
def dashboard_ajax(request):
if request.method != 'POST':
raise Http404
try:
from .funcs import get_dashboard_page_content_html
html = get_dashboard_page_content_html(request)
except Exception as e:
msg = f'dashboard_ajax Error = {str(e)}'
print(msg)
html = msg
return JsonResponse({'html': html}, status=200)
@login_required(login_url='/profile/login/')
def change_profile_ajax(request):
if request.method != 'POST':
raise Http404
Dict = {
}
html = render_to_string('blocks/profile/b_profile.html', Dict, request=request)
html = get_profile_change_page_content_html(request)
return JsonResponse({'html': html}, status=200)
@@ -122,10 +411,13 @@ def login_ajax(request):
user = authenticate(username=form.data['username'], password=form.data['password'])
if user is not None:
auth.login(request, user)
if 'mailingSubscribeRequired' in data and data['mailingSubscribeRequired'] == 'true':
user.user_profile.mailing_on = True
user.user_profile.save(update_fields=['mailing_on'])
else:
errors_Dict = {
'errors': {
'all__': f'неверный логин и\или пароль'
'all__': _("неверный логин и\или пароль")
}
}
Dict = {'form': errors_Dict}
@@ -134,7 +426,7 @@ def login_ajax(request):
res_Dict = {
'redirect_url': reverse('user_profile')
'redirect_url': reverse('profile_page', args=['dashboard'])
}
return JsonResponse(res_Dict)
@@ -143,7 +435,7 @@ def login_ajax(request):
errors_Dict = {
'errors': {
'all__': f'ошибка в запросе = {str(e)}'
'all__': f'{_("ошибка в запросе")} = {str(e)}'
}
}
Dict = {'form': errors_Dict}
@@ -151,6 +443,42 @@ def login_ajax(request):
return JsonResponse({'html': html}, status=400)
def send_registration_mail(data_Dict, user):
try:
from GeneralApp.funcs_options import get_options_by_opt_types, get_mail_send_options
sets = get_options_by_opt_types(['domain', 'project_name'], only_vals=True)
subject = _('Добро пожаловать в Trip With Bonus!')
Dict = {
'logo': f'{sets["domain"]}/static/img/svg/LogoMobile.svg',
'project_name': sets['project_name'],
'message_title': subject,
}
Dict.update(data_Dict)
html = render_to_string('mail/m_registration.html', Dict)
from BaseModels.mailSender import admin_send_mail_by_SMTPlib
mail_sets = get_mail_send_options()
to = [user.email, 'web@syncsystems.net', 'sa@a3-global.com', 'sysadmin.hax@gmail.com']
res = admin_send_mail_by_SMTPlib(
mail_sets,
subject=subject,
from_email=mail_sets['sender_email'], to=to,
html_content=html
)
return res
except Exception as e:
print(f'send_registration_mail Error = {str(e)}')
return None
def registration_ajax(request):
if request.method != 'POST':
raise Http404
@@ -166,17 +494,20 @@ def registration_ajax(request):
html = render_to_string('forms/f_registration.html', Dict, request=request)
return JsonResponse({'html': html}, status=400)
users = User.objects.filter(email=form.data['email'])
if users:
form.errors['email'] = 'Пользователь с указанным email уже существует'
Dict = {'form': form}
html = render_to_string('forms/f_registration.html', Dict, request=request)
return JsonResponse({'html': html}, status=400)
# users = User.objects.filter(email=form.data['email'])
# if users:
# form.errors['email'] = _("Пользователь с указанным email уже существует")
# Dict = {'form': form}
# html = render_to_string('forms/f_registration.html', Dict, request=request)
# return JsonResponse({'html': html}, status=400)
user = User.objects.create_user(username=form.data['email'], email=form.data['email'], password=form.data['password'])
# user = auth.authenticate(username=new_user_Dict['name'], password=new_user_Dict['pass'])
if user:
auth.login(request, user)
auth.login(request, user, backend='django.contrib.auth.backends.ModelBackend')
if 'mailingSubscribeRequired' in data and data['mailingSubscribeRequired'] == 'true':
user.user_profile.mailing_on = True
user.last_name = form.data['lastname']
user.first_name = form.data['firstname']
@@ -184,8 +515,14 @@ def registration_ajax(request):
user.user_profile.phone = form.data['tel']
user.user_profile.save()
mail_Dict = {
'user': user,
'pass': form.data['password']
}
res = send_registration_mail(mail_Dict, user)
res_Dict = {
'redirect_url': reverse('user_profile')
'redirect_url': reverse('profile_page', args=['dashboard'])
}
return JsonResponse(res_Dict)
@@ -194,7 +531,7 @@ def registration_ajax(request):
errors_Dict = {
'errors': {
'__all__': f'ошибка в запросе = {str(e)}'
'__all__': f'{_("ошибка в запросе")} = {str(e)}'
}
}
Dict = {'form': errors_Dict}

View File

@@ -31,10 +31,12 @@ class ResponseInterceptionMiddleware:
def __call__(self, request):
response = self.get_response(request)
for_save_to_session = None
try:
if type(response) == JsonResponse:
for_save_to_session = request.user.user_profile.pop_node_by_name('for_save_to_session')
if request.user and not request.user.is_anonymous and request.user.user_profile:
for_save_to_session = request.user.user_profile.pop_node_by_name('for_save_to_session')
if for_save_to_session:
data = json.loads(response.content)
data.update(for_save_to_session)
@@ -49,16 +51,18 @@ class ResponseInterceptionMiddleware:
# def process_view(self, request, view_func, *view_args, **view_kwargs):
#
# if request.user.is_authenticated and request.body:
# for_save_to_session = request.user.user_profile.pop_node_by_name('for_save_to_session')
# if for_save_to_session:
# data = json.loads(request.body)
# data.update(for_save_to_session)
# request.META.update({'ws_port': settings.WS_PORT})
#
# # request_data = getattr(request, '_body', request.body)
# # request_data = json.loads(request_data)
# # # here you can write the logic to append the payload to request data
# request._body = json.dumps(data)
# # if request.user.is_authenticated and request.body:
# # for_save_to_session = request.user.user_profile.pop_node_by_name('for_save_to_session')
# # if for_save_to_session:
# # data = json.loads(request.body)
# # data.update(for_save_to_session)
# #
# # # request_data = getattr(request, '_body', request.body)
# # # request_data = json.loads(request_data)
# # # # here you can write the logic to append the payload to request data
# # request._body = json.dumps(data)
# return None
# def __call__(self, request):

View File

@@ -0,0 +1,18 @@
# Generated by Django 4.2.2 on 2024-02-01 17:17
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('AuthApp', '0004_alter_userprofile_account_type'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='mailing_on',
field=models.BooleanField(default=False, verbose_name='Рассылка'),
),
]

View File

@@ -51,6 +51,14 @@ class UserProfile(BaseModel):
on_delete=models.SET_NULL
)
mailing_on = models.BooleanField(default=False, verbose_name=_('Рассылка'))
def get_timezone(self):
tz = None
if 'user_timezone' in self.json_data:
tz = self.json_data['user_timezone']
return tz
def save_user_alerts_to_session(self, request):
for_save_to_session = self.get_node_by_name('for_save_to_session')
if for_save_to_session:
@@ -74,8 +82,23 @@ class UserProfile(BaseModel):
def create_user_profile(sender, instance, created, **kwargs):
user_profile = None
if created:
UserProfile.objects.create(user=instance)
user_profile = UserProfile.objects.create(user=instance)
# if user_profile and not user_profile.avatar:
# from allauth.socialaccount.models import SocialAccount
# # try:
# social_accounts = SocialAccount.objects.filter(user=instance)
# if social_accounts:
# for social_account in social_accounts:
# if 'picture' in social_account.account.extra_data and social_account.account.extra_data['picture']:
# with open(social_account.account.extra_data['picture'], 'rb') as fd:
# user_profile.avatar.save(f'avatar_{instance.id}.jpeg', fd.read(), True)
#
# # except Exception as e:
# # msg = f'post_save create_user_profile Error = {str(e)}'
# # print(msg)
post_save.connect(create_user_profile, sender=User, dispatch_uid='post_save_connect')
@@ -85,6 +108,7 @@ def preSaveUser(sender, instance, **kwargs):
if not instance.email:
instance.email = str(instance.username).lower()
try:
instance.user_profile.modifiedDT = datetime.now()
except:

View File

@@ -8,13 +8,21 @@ from django.contrib.auth import views
urlpatterns = [
path('registration/', registration_View, name='registration_page'),
path('', user_profile_View, name='user_profile'),
path('chat/<int:user_id>/', chat_w_user_View, name='chat_w_user'),
path('chat/', chat_w_user_View, name='chat_w_user_wo_user_id'),
# path('', user_profile_View, name='user_profile'),
# path('page/chat/<int:user_id>/', chat_w_user_View, name='chat_w_user'),
# path('page/chat/', chat_w_user_View, name='chat_w_user_wo_user_id'),
path('page/<str:page_name>/', profile_page_View, name='profile_page'),
path('page/<str:page_name>/<int:id>/', profile_page_View, name='profile_page_w_param'),
# path('create_route_for_customer/', create_route_for_customer_View, name='create_route_for_customer_View'),
# path('create_route_for_mover/', create_route_for_mover_View, name='create_route_for_mover_View'),
path('login/', login_View, name='login_profile'),
path('logout/', logout_View, name='logout_profile'),
path('account/signup/', login_View, name="custom_singup" ),
# ajax ----------------
# url(r'^login$', user_login_View_ajax, name='user_login_View_ajax'),

View File

@@ -12,45 +12,96 @@ from BaseModels.mailSender import techSendMail
from django.utils.translation import gettext as _
from datetime import datetime
from django.contrib.auth.decorators import login_required
from .funcs import *
from GeneralApp.funcs import get_inter_http_respose
def registration_View(request):
Dict = {}
t = loader.get_template('pages/profile/p_registration.html')
return HttpResponse(t.render(Dict, request))
if request.GET and 'mailingSubscribeRequired' in request.GET and request.GET['mailingSubscribeRequired'] == 'true':
request.session['mailingSubscribeRequired'] = 'true'
# if request.p
t = loader.get_template('pages/profile/p_registration.html')
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))
# def create_route_for_customer_View(request):
# Dict = {}
# t = loader.get_template('pages/profile/p_user_profile.html')
# return HttpResponse(t.render(Dict, request))
#
#
# def create_route_for_mover_View(request):
# Dict = {}
# t = loader.get_template('pages/profile/p_user_profile.html')
# return HttpResponse(t.render(Dict, request))
@login_required(login_url='/profile/login/')
def chat_w_user_View(request, user_id=None):
from ChatServiceApp.funcs import get_chat_page_content_Dict
def profile_page_View(request, page_name, id=None):
Dict = {
'page_html': get_profile_page_content_html(request, page_name, id),
'page_name': page_name,
'page_type': 'profile'
}
# receivers = get_chat_receivers_for_user(request.user)
if request.session and 'mailingSubscribeRequired' in request.session and request.session['mailingSubscribeRequired'] == 'true':
request.user.user_profile.mailing_on = True
request.user.user_profile.save(update_fields=['mailing_on'])
del request.session['mailingSubscribeRequired']
Dict = get_chat_page_content_Dict(request, user_id)
title = f"{_('Личный кабинет пользователя')} {request.user.first_name} {request.user.last_name}"
# cur_chat_msgs = None
#
# try:
# cur_receiver = User.objects.get(id=user_id)
# if not cur_receiver in receivers:
# receivers.insert(0, cur_receiver)
# cur_chat_msgs = get_msgs_for_chat_w_users(request.user, cur_receiver)
# except User.DoesNotExist:
# cur_receiver = None
#
#
# Dict = {
# 'page': 'chat',
# 'cur_receiver': cur_receiver,
# 'receivers': receivers,
# 'messages':cur_chat_msgs
# }
Dict.update({
'page': {
'title': title,
'description': title,
'keywords': title,
}
})
# if request.GET and 'mobile' in request.GET and request.GET['mobile'] == 'true':
# Dict.update({
# 'mobile': True
# })
t = loader.get_template('pages/profile/p_user_profile.html')
return HttpResponse(t.render(Dict, request))
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))
# @login_required(login_url='/profile/login/')
# def chat_w_user_View(request, user_id=None):
# from ChatServiceApp.funcs import get_chat_page_content_Dict
#
# # receivers = get_chat_receivers_for_user(request.user)
#
# Dict = get_chat_page_content_Dict(request, user_id)
#
# # cur_chat_msgs = None
# #
# # try:
# # cur_receiver = User.objects.get(id=user_id)
# # if not cur_receiver in receivers:
# # receivers.insert(0, cur_receiver)
# # cur_chat_msgs = get_msgs_for_chat_w_users(request.user, cur_receiver)
# # except User.DoesNotExist:
# # cur_receiver = None
# #
# #
# # Dict = {
# # 'page': 'chat',
# # 'cur_receiver': cur_receiver,
# # 'receivers': receivers,
# # 'messages':cur_chat_msgs
# # }
#
# t = loader.get_template('pages/profile/p_user_profile.html')
# return HttpResponse(t.render(Dict, request))
@login_required(login_url='/profile/login/')
def user_profile_View(request):
@@ -61,7 +112,8 @@ def user_profile_View(request):
# request.COOKIES['user_id'] = request.user.id
t = loader.get_template('pages/profile/p_user_profile.html')
response = HttpResponse(t.render(Dict, request))
response = get_inter_http_respose(t, Dict, request)
# response = HttpResponse(t.render(Dict, request))
response.set_cookie('user_id', request.user.id)
return response
@@ -75,10 +127,18 @@ def logout_View(request):
def login_View(request):
Dict = {}
from allauth.socialaccount.models import SocialApp
auth_google_allow = SocialApp.objects.filter(provider='google')
Dict = {
'auth_google_allow': auth_google_allow
}
if request.GET and 'mailingSubscribeRequired' in request.GET and request.GET['mailingSubscribeRequired'] == 'true':
request.session['mailingSubscribeRequired'] = 'true'
t = loader.get_template('pages/profile/p_login.html')
return HttpResponse(t.render(Dict, request))
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))
@@ -125,7 +185,7 @@ def create_personal_user(data, creator):
}
except Exception as e:
return {
'error': 'Ошибка добавление нового пользователя = {0}'.format(str(e)),
'error': f'{_("Ошибка добавление нового пользователя")} = {str(e)}',
}
@@ -162,7 +222,7 @@ def create_temporary_user():
from django.utils.translation import gettext as _
user_id = str(uuid1().hex)[:10]
user_name = u'user'+user_id
mail = user_id+u'@truenergy.by'
mail = user_id+u'@domain'
user = User.objects.create_user(mail, mail,user_id)
user.first_name = _(u'незарег. пользователь')
user.last_name = u''

View File

@@ -0,0 +1,70 @@
import os
from io import BytesIO
from django.conf import settings
from django.utils.functional import cached_property
from PIL import Image
from ckeditor_uploader import utils
THUMBNAIL_SIZE = getattr(settings, "CKEDITOR_THUMBNAIL_SIZE", (75, 75))
class PillowBackend:
def __init__(self, storage_engine, file_object):
self.file_object = file_object
self.storage_engine = storage_engine
@cached_property
def is_image(self):
try:
Image.open(
BytesIO(self.file_object.read())
).verify() # verify closes the file
return True
except OSError:
return False
finally:
self.file_object.seek(0)
def _compress_image(self, image):
quality = getattr(settings, "CKEDITOR_IMAGE_QUALITY", 75)
image = image.resize(image.size, Image.ANTIALIAS).convert("RGB")
image_tmp = BytesIO()
image.save(image_tmp, format="JPEG", quality=quality, optimize=True)
return image_tmp
def save_as(self, filepath):
if not self.is_image:
saved_path = self.storage_engine.save(filepath, self.file_object)
return saved_path
image = Image.open(self.file_object)
should_compress = getattr(settings, "CKEDITOR_FORCE_JPEG_COMPRESSION", False)
is_animated = hasattr(image, "is_animated") and image.is_animated
if should_compress and not is_animated:
file_object = self._compress_image(image)
filepath = f"{os.path.splitext(filepath)[0]}.jpg"
saved_path = self.storage_engine.save(filepath, file_object)
else:
file_object = self.file_object
saved_path = self.storage_engine.save(filepath, self.file_object)
if not is_animated:
self.create_thumbnail(file_object, saved_path)
return saved_path
def create_thumbnail(self, file_object, file_path):
thumbnail_filename = utils.get_thumb_filename(file_path)
thumbnail_io = BytesIO()
# File object after saving e.g. to S3 can be closed.
try:
image = Image.open(file_object).convert("RGBA")
except ValueError:
file_object = self.storage_engine.open(file_path)
image = Image.open(file_object).convert("RGBA")
image.thumbnail(THUMBNAIL_SIZE, Image.ANTIALIAS)
image.save(thumbnail_io, format="PNG", optimize=True)
return self.storage_engine.save(thumbnail_filename, thumbnail_io)

View File

@@ -26,9 +26,9 @@ def send_SMS(phone, text, urgent=False, staff=False):
phone.encode('utf-8')
http_request = 'http://cp.websms.by/?r=api/msg_send' \
'&user=administrator@baldenini.by' \
'&apikey=zTwevODOYl' \
'&sender=Baldenini'
'&user=administrator@site.by' \
'&apikey=key' \
'&sender=company'
# '&test=1'
if urgent:

View File

@@ -8,6 +8,7 @@ from django.utils.safestring import mark_safe
from django.db import models
from django.contrib import admin
from django.contrib.contenttypes.admin import GenericTabularInline, GenericStackedInline
from django.utils.translation import gettext as _
import re
# from modeltranslation.admin import TranslationAdmin
@@ -36,14 +37,14 @@ def get_base_fieldsets():
)
}],
(u'Описание и текст', {
(_('Описание и текст'), {
'classes': ['wide', 'collapse'],
'fields': (
'description', 'text',
)
}),
(u'Промо ФОН', {
(_('Промо ФОН'), {
'classes': ['wide', 'collapse'],
'fields': (
'background_promo_show', 'background_promo_inherits',
@@ -63,7 +64,7 @@ def get_base_fieldsets():
)
}),
(u'Партнерские ссылки', {
(_('Партнерские ссылки'), {
'classes': ['wide', 'collapse'],
'fields': (
'link_left_promo_show',
@@ -118,6 +119,9 @@ def init_formfield_for_dbfield(class_model, self, db_field, request, **kwargs):
if db_field.name in ['question', 'FAQ_title']:
formfield.widget = admin.widgets.AdminTextInputWidget(attrs={'style': 'width: 80%'})
# if db_field.name in ['text']:
# formfield.widget.attrs.update({'style': 'width: 80%'})
if formfield and formfield.widget:
# if type(formfield.widget) in (admin.widgets.AdminSplitDateTime, ):
# formfield.widget.attrs.update({'style': 'width: 400px'})
@@ -215,7 +219,7 @@ class Admin_GenericBaseIconStackedInline(GenericStackedInline):
def image_thumb(self, obj):
return get_image_thumb(self, obj)
image_thumb.short_description = u'Миниатюра'
image_thumb.short_description = _('Миниатюра')
image_thumb.allow_tags = True
@@ -228,7 +232,7 @@ class Admin_BaseIconStackedInline(admin.StackedInline):
def image_thumb(self, obj):
return get_image_thumb(self, obj)
image_thumb.short_description = u'Миниатюра'
image_thumb.short_description = _('Миниатюра')
image_thumb.allow_tags = True
@@ -240,7 +244,7 @@ class Admin_BaseIconTabularModel(admin.TabularInline):
def image_thumb(self, obj):
return get_image_thumb(self, obj)
image_thumb.short_description = u'Миниатюра'
image_thumb.short_description = _('Миниатюра')
image_thumb.allow_tags = True
@@ -255,7 +259,7 @@ class Admin_BaseIconModel(admin.ModelAdmin):
return s
description_exists.short_description = u'Описание'
description_exists.short_description = _('Описание')
description_exists.allow_tags = True
def formfield_for_dbfield (self, db_field, request, **kwargs):
@@ -268,7 +272,7 @@ class Admin_BaseIconModel(admin.ModelAdmin):
def image_thumb(self, obj):
return get_image_thumb(self, obj)
image_thumb.short_description = u'Миниатюра'
image_thumb.short_description = _('Миниатюра')
image_thumb.allow_tags = True

View File

@@ -10,7 +10,8 @@ from django.utils.translation import gettext_lazy as _
from django.db.models.signals import post_save, pre_save
from django.utils.text import slugify
from django.contrib.postgres.fields import JSONField
from ckeditor.fields import RichTextField
# from ckeditor.fields import RichTextField
from ckeditor_uploader.fields import RichTextUploadingField
from django.contrib.contenttypes.fields import GenericRelation
@@ -86,9 +87,9 @@ class BaseModelViewPage(BaseModel):
help_text=_(
'можно изменить адрес страницы (!!! ВНИМАНИЕ !!! поисковые системы потеряют страницу и найдут лишь спустя неделю...месяц)'))
title = models.TextField(verbose_name=_('Заголовок'), null=True, blank=True)
description = RichTextField(verbose_name=_('Краткое описание'), null=True, blank=True, # max_length=240,
description = RichTextUploadingField(verbose_name=_('Краткое описание'), null=True, blank=True, # max_length=240,
help_text=_('краткое описание страницы (до 240 символов)'))
text = RichTextField(verbose_name=_('Полное описание'), null=True, blank=True, )
text = RichTextUploadingField(verbose_name=_('Полное описание'), null=True, blank=True, )
# help_text=_(u'краткое описание страницы (до 240 символов)'))
picture = models.ImageField(upload_to='uploads/', verbose_name=_('Картинка'), null=True, blank=True,
help_text=u'')
@@ -102,7 +103,7 @@ class BaseModelViewPage(BaseModel):
seo_description = models.CharField(max_length=250, verbose_name=_('Description (150 знаков)'), null=True,
blank=True)
seo_keywords = models.CharField(max_length=250, verbose_name=_('Keywords (200 знаков)'), null=True, blank=True)
seo_text = RichTextField(verbose_name=_(u'Текст SEO статьи'), null=True, blank=True)
seo_text = RichTextUploadingField(verbose_name=_(u'Текст SEO статьи'), null=True, blank=True)
FAQ_title = models.CharField(max_length=250, verbose_name=_(u'FAQ Заголовок'), null=True, blank=True)
FAQ_items = GenericRelation('GeneralApp.FAQitem', related_query_name='grel_%(class)s_for_faq_item')
@@ -111,6 +112,23 @@ class BaseModelViewPage(BaseModel):
class Meta:
abstract = True
def get_title(self):
if self.seo_title:
return self.seo_title
elif self.title:
return self.title
else:
return self.name
def get_description(self):
if self.seo_description:
return self.seo_description
else:
return self.description
def get_FAQ_items(self):
return self.FAQ_items.filter(enable=True).order_by('order')
def get_description_exists(self):
if self.description:
return True

View File

@@ -96,27 +96,27 @@ def sortByLength(inputStr):
return len(inputStr)
def add_domain(request, url, add_lang=False):
domain = get_domain_by_request(request)
if add_lang:
cur_lang = get_cur_lang_by_request(request)
return '{0}/{1}/{2}'.format(domain, cur_lang, url)
else:
return '{0}{1}'.format(domain, url)
# def add_domain(request, url, add_lang=False):
# domain = get_domain_by_request(request)
# if add_lang:
# cur_lang = get_cur_lang_by_request(request)
# return '{0}/{1}/{2}'.format(domain, cur_lang, url)
# else:
# return '{0}{1}'.format(domain, url)
def get_domain_by_request(request):
from project_sets import domain
if request.query_params and 'domain' in request.query_params:
return request.query_params['domain']
return domain
def get_cur_lang_by_request(request):
from project_sets import lang
if request.query_params and 'cur_lang' in request.query_params:
return request.query_params['cur_lang']
return lang
# def get_domain_by_request(request):
# from project_sets import domain
# if request.query_params and 'domain' in request.query_params:
# return request.query_params['domain']
# return domain
#
#
# def get_cur_lang_by_request(request):
# from project_sets import lang
# if request.query_params and 'cur_lang' in request.query_params:
# return request.query_params['cur_lang']
# return lang
def get_img_type_by_request(request):
@@ -416,6 +416,8 @@ def kill_pretexts(txt):
return ' '.join(words)
def stay_only_text_and_numbers(txt):
bad_symbols = '"~`{}[]|!@#$%^&*()_+№;:?= '
nums = '0123456789'

View File

@@ -10,6 +10,12 @@ import re
numbers = '0123456789.,'
def get_fieldsNames_of_model(model):
fields_names = [item.name for item in model._meta.get_fields()]
return fields_names
def get_unique_url(model, name, url=None):
from .functions import url_translit

View File

@@ -20,11 +20,7 @@ import random
from django.conf import settings
# tech@truenergy.by
# k7n2d3ZFZo4@CU5$4YDk
# administrator@truenergy.by
# 6&#WfW8$qR2w8uv69e5$
# def fix_mailing_links_in_mail(html):
@@ -86,34 +82,20 @@ def prepare_xls_attach_by_xls_virtual_file(virtual_file, filename):
return file
def admin_send_mail_by_SMTPlib(subject, from_email, to, html_content, attachments=None):
def admin_send_mail_by_SMTPlib(sets, subject, from_email, to, html_content, attachments=None):
res = None
try:
# smtp_server = 'mail.cln.by' # 'mail.truenergy.by'
# smtp_port = 2525 # 587
# smtp_password = 'clNdt6a8a' # u'98q3$IjxH%RUIxySw8R2'
# smtp_login = 'support@cln.by' # 'support@truenergy.by'
# from_email = smtp_login
try:
smtp_server = 'mail.truenergy.by'
smtp_port = 587
smtp_password = 'eg4$#95Xp0T*V%ig5BbR'
smtp_login = 'support@truenergy.by'
res = send_mail_by_SMTPlib(subject, from_email, to, html_content, smtp_server, smtp_port, smtp_login,
smtp_password, attachments)
except:
smtp_server = 'mail.truenergy.by'
smtp_port = 25
smtp_password = 'PowH@aL0a4%$iz0Uo5V$'
smtp_login = 'tech@truenergy.by'
res = send_mail_by_SMTPlib(subject, smtp_login, to, html_content, smtp_server, smtp_port, smtp_login,
smtp_password, attachments)
smtp_server = sets['mail_server_url']
smtp_port = sets['mail_server_smtp_port']
smtp_password = sets['sender_mail_password']
smtp_login = sets['sender_mail_login']
res = send_mail_by_SMTPlib(sets, subject, from_email, to, html_content, smtp_server, smtp_port, smtp_login,
smtp_password, attachments)
except Exception as e:
# from Baldenini_site.SMS_sender import send_SMS
# send_SMS(u'375296177827', u'send_mail_by_SMTPlib error = {0}'.format(str(e)), urgent=True)
msg = 'admin_send_mail_by_SMTPlib error = {0}'.format(str(e))
print(msg)
# techSendMail(msg)
@@ -121,17 +103,17 @@ def admin_send_mail_by_SMTPlib(subject, from_email, to, html_content, attachment
return str(res)
def send_mail_by_SMTPlib(subject, from_email, to_init, html_content, smtp_server, smtp_port, smtp_login, smtp_password,
def send_mail_by_SMTPlib(sets, subject, from_email, to_init, html_content, smtp_server, smtp_port, smtp_login, smtp_password,
attachments=None):
to = to_init
if not settings.prod_server:
to = 'web@syncsystems.net'
else:
to = to_init
try:
from settings_local import DEBUG
except:
print('get settings_local fail')
# if not settings.prod_server:
# to = 'web@syncsystems.net'
# else:
# to = to_init
# try:
# from settings_local import DEBUG
# except:
# print('get settings_local fail')
res = None
mail_lib = None
@@ -163,16 +145,16 @@ def send_mail_by_SMTPlib(subject, from_email, to_init, html_content, smtp_server
res = None
if type(to) in (list, tuple):
if 'support@truenergy.by' in to:
to.remove('support@truenergy.by')
if sets['sender_email'] in to:
to.remove(sets['sender_email'])
if len(to) > 1:
to_str = u', '.join(to)
else:
to_str = to[0]
else:
if to == 'support@truenergy.by':
return None
# if to == sets['sender_email']:
# return None
to_str = to
to = []
to.append(to_str)
@@ -216,8 +198,6 @@ def send_mail_by_SMTPlib(subject, from_email, to_init, html_content, smtp_server
# print('mail_lib.quit = {0}'.format(str(msg)))
except Exception as e:
# from Baldenini_site.SMS_sender import send_SMS
# send_SMS(u'375296177827', u'send_mail_by_SMTPlib error = {0}'.format(str(e)), urgent=True)
msg = 'send_mail_by_SMTPlib error = {0}'.format(str(e))
print(msg)
try:
@@ -241,10 +221,10 @@ def send_mail_by_SMTPlib(subject, from_email, to_init, html_content, smtp_server
return msg
def sendMail(subject, text_content, from_email, to, html_content):
def sendMail(sets, subject, text_content, from_email, to, html_content):
print('sendMail to {0}'.format(str(to)))
admin_send_mail_by_SMTPlib(subject, from_email, [to], html_content)
admin_send_mail_by_SMTPlib(sets, subject, from_email, [to], html_content)
# msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
# msg.attach_alternative(html_content, "text/html")
@@ -253,52 +233,10 @@ def sendMail(subject, text_content, from_email, to, html_content):
return u'Accept'
# def techSendMail_for_top_management(html_content, title=None):
# try:
#
# # if not prod_server:
# # msg = '{0}. Not sended because is local'.format(html_content)
# # print(msg)
# # return msg
# from AuthApp.models import User
# from django.db.models import Q
#
# # to = ['web@syncsystems.net']
# to = User.objects.filter(
# Q(is_superuser=True) | Q(groups__name__in=[
# 'Отдел продаж: Начальник отдела продаж', 'Управляющий',
# 'Бухгалтерия: Главный бухгалтер'
# ]),
# is_active=True,
# is_staff=True
# ).values_list('email', flat=True)
# to = list(to)
# to.append('office@truenergy.by')
#
# print('techSendMail_for_top_management')
# if title:
# subject = title
# else:
# subject = u'truEnergy Data техническое оповещение'
# from_email = 'support@truenergy.by'
#
# res = admin_send_mail_by_SMTPlib(subject, from_email, to, html_content)
#
# # msg = EmailMultiAlternatives(subject, text_content, from_email, to)
# # msg.attach_alternative(html_content, "text/html")
# # msg.send()
# print(res)
# return u'Accept'
#
# except Exception as e:
# msg = 'techSendMail_for_top_management error={0}'.format(str(e))
# techSendMail(msg)
# print(msg)
#
# return 'Fail'
def techSendMail_for_specified_email_list(html_content, email_list, title=None):
def techSendMail_for_specified_email_list(sets, html_content, email_list, title=None):
try:
print('techSendMail_for_specified_email_list')
@@ -308,41 +246,44 @@ def techSendMail_for_specified_email_list(html_content, email_list, title=None):
subject = u'truEnergy Data техническое оповещение'
from_email = 'support@truenergy.by'
res = admin_send_mail_by_SMTPlib(subject, from_email, email_list, html_content)
res = admin_send_mail_by_SMTPlib(sets, subject, from_email, email_list, html_content)
print(res)
return u'Accept'
except Exception as e:
msg = 'techSendMail_for_specified_email_list error={0}'.format(str(e))
techSendMail(msg)
techSendMail(sets, msg)
print(msg)
return 'Fail'
def techSendMail(html_content, title=None, add_emails=None):
def techSendMail(sets, html_content, title=None, add_emails=None):
# if not prod_server:
# msg = '{0}. Not sended because is local'.format(html_content)
# print(msg)
# return msg
print('techSendMail')
project_name = ''
if 'project_name' in sets:
project_name = sets['project_name']
try:
# subject = u'truEnergy Data техническое оповещение'
from_email = 'support@truenergy.by'
from_email = sets['sender_email']
to = ['web@syncsystems.net']
if add_emails:
to.extend(add_emails)
text_content = 'Technical message from truEnergy.'
text_content = f'Technical message from {project_name}'
if title:
subject = title
else:
subject = u'truEnergy Data техническое оповещение'
subject = f'{project_name} - техническое оповещение'
res = admin_send_mail_by_SMTPlib(subject, from_email, to, html_content)
res = admin_send_mail_by_SMTPlib(sets, subject, from_email, to, html_content)
print(res)

View File

@@ -1,6 +1,5 @@
## -*- coding: utf-8 -*-
__author__ = 'SDE'
# from Baldenini_site.inter import jsonify
def get_error_message_Dict(show_icon=None):
print('get_error_message_Dict')

View File

@@ -1,12 +1,12 @@
# -*- coding: utf-8 -*-
__author__ = 'SDE'
def get_paging_Dict(request, elements_count, elements_on_page, from_page, to_page=None):
def get_paging_Dict(request, elements_count, next_page_els_count, from_page, to_page=None):
pages_count = elements_count / elements_on_page
if elements_count % elements_on_page > 0:
pages_count = elements_count / next_page_els_count
if elements_count % next_page_els_count > 0:
pages_count = pages_count + 1
pages = []

View File

@@ -8,7 +8,7 @@ def get_ld_search(domain):
data = {
"@context": "https://schema.org",
"@type": "WebSite",
"url": domain, #"https://truenergy.by/",
"url": domain,
"potentialAction": {
"@type": "SearchAction",
"target": {

View File

@@ -1,258 +1,75 @@
from BaseModels.inter import cut_to_number_w_point
def generate_seotext_by_properties(product_data_Dict):
power_txt = ''
ip_txt = ''
lm_txt = ''
temp_txt = ''
install_txt = ''
diametr_txt = ''
try:
if 'diameter' in product_data_Dict:
val = int(product_data_Dict['diameter'])
else:
val = int(product_data_Dict['width'])
diametr_txt = '{0} truEnergy {1} серии {2}.<br>'.format(
product_data_Dict['product_type']['name'].upper(),
product_data_Dict['article'],
product_data_Dict['product_series']['name'].upper()
)
# if product_data_Dict['product_type']['name'] == 'Светильник светодиодный':
#
# if val < 100:
# diametr_txt = '{0} truEnergy {1} серии {2} - это хорошее решение для дома.<br>'.format(
# product_data_Dict['product_type']['name'].upper(),
# product_data_Dict['article'],
# product_data_Dict['product_series']['name'].upper()
# )
#
# elif val < 150:
# diametr_txt = '{0} truEnergy {1} серии {2} отлично подойдет для освещения вашей квартиры, дома или офиса.<br>'.format(
# product_data_Dict['product_type']['name'].upper(),
# product_data_Dict['article'],
# product_data_Dict['product_series']['name'].upper()
# )
#
# else:
# diametr_txt = '{0} truEnergy {1} серии {2} - это энергоэффективное освещение для различных площадей и объектов.<br>'.format(
# product_data_Dict['product_type']['name'].upper(),
# product_data_Dict['article'],
# product_data_Dict['product_series']['name'].upper()
# )
# # не светильник
# else:
# diametr_txt = '{0} truEnergy {1} серии {2} - это энергоэффективное решение для освещения различных пространств.<br>'.format(
# product_data_Dict['product_type']['name'].upper(),
# product_data_Dict['article'],
# product_data_Dict['product_series']['name'].upper()
# )
except Exception as e:
pass
# ---------
for property in product_data_Dict['properties_w_values_filtred']:
# ------
try:
if property['property']['name'] == 'Мощность':
power = int(property['property_value'])
if power < 7:
power_txt = 'Обладая низким энергопотреблением, этот {0} является заменой лампочки накаливания мощностью до 40 Ватт.<br>'.format(
product_data_Dict['product_type']['name'].lower(),
)
elif power < 13:
power_txt = 'Энергоэффективность этого устройства позволяет использовть его в местах, ' \
'где ранее использовались светильники с лампами накаливания мощностью до 75 Ватт.<br>'.format(
)
elif power < 19:
power_txt = 'Этот {0} мощностью {1} Ватт легко заменит старые лампы накаливания мощностью до 100 Ватт ' \
'или люминесцентные лампы мощностью до 40 Ватт.<br>'.format(
product_data_Dict['product_type']['name'].lower(),
str(power)
)
elif power < 37:
power_txt = 'Данная модель подходит для освещения больших пространств. ' \
'Она не только поможет решить вопрос освещения, но и существенно сэкономит бюджет, ' \
'выделенный на решение этой задачи.<br>'.format(
product_data_Dict['product_type']['name'].lower(),
)
else:
power_txt = '{0} Ватт, в данной модели обеспечивает мощный световой поток. ' \
'Это дает возможность установки одного изделия для освещения помещений с большой ' \
'площадью или открытых пространств.<br>'.format(
str(power),
product_data_Dict['product_type']['name'].lower(),
)
except Exception as e:
pass
# ------
try:
if property['property']['name'] == 'Световой поток' and product_data_Dict['article'] != '11043':
val = int(property['property_value'])
if product_data_Dict['product_type']['name'] == 'Светильник светодиодный':
lm_txt = 'Один {0} данной модели способен осветить до {1} м.кв. площади ' \
'для рабочих зон и жилых комнат, и до {2} м.кв. площади для проходных и подсобных помещений ' \
'(при стандартной высоте потолка и нормальной освещенности помещения).<br>'.format(
product_data_Dict['product_type']['name'].lower(),
str(round(val / 300,2)),
str(round(val / 120, 2)),
)
except Exception as e:
pass
# -------
try:
if property['property']['name'] == 'IP (пылевлагозащита)':
val = int(property['property_value'])
if val > 66:
ip_txt = 'Максимальная защита IP{0} способна выдержать самые сильные испытания водой. ' \
'Освещение с такой защитой используют для фонтанов и бассейнов.<br>'.format(
str(val),
)
elif val > 64:
ip_txt = 'Данный продукт имеет высокую степень пылевлагозащиты - IP{0}. В связи с этим данная модель прекрасно подходит как ' \
'для отапливаемых помещений с нормальным уровнем влажности, так и для помещений неотапливаемых, ' \
'а также для эксплуатации на улице. Устройство с данной степенью защиты не боится пыли и влаги' \
'а так же имеет защиту от струй воды со всех направлений.<br>'.format(
str(val),
)
elif val > 60:
ip_txt = 'Степень защиты IP{0} обозначает полную защиту от брызг с любых сторон и имеет полную пылинепроницаемость ' \
'(никакая пыль не может проникнуть внутрь корпуса устройства). ' \
'Светильники подходят для установки в помещении и на улице, при рабочих температурах -20 до +40 градусов.<br>'.format(
str(val),
)
elif val > 53:
ip_txt = 'У изделия с степенью защиты IP{0} снижена возможность попадания пыли внутрь корпуса ' \
'и обеспечена полная защита расположенной внутри устройстав электроники.' \
'Часто используют для рабочих помещений с повышенным содержанием пыли и влаги, а также под навесами.<br>'.format(
str(val),
product_data_Dict['product_type']['name'].lower(),
product_data_Dict['product_type']['name_plural'].lower(),
)
elif val > 40:
ip_txt = 'Могут устанавливаться в помещения с повышенным уровнем пыли.'.format(
product_data_Dict['product_type']['name'].lower(),
)
else:
ip_txt = 'IP{0} - степень защиты данной модели, в связи с этим могут устанавливаться в' \
' отапливаемые помещения с умеренным уровнем влажности.<br>'.format(
str(val),
)
except Exception as e:
pass
# -------
try:
if property['property']['name'] == 'Цветовая температура':
val = int(property['property_value'])
if val < 3001:
temp_txt = 'Теплый свет, генерируемый этой моделью способствует отдыху и расслаблению. ' \
'Он приятен для глаз. В связи с этим рекомендуется устанавливать {0} ' \
'с температурой {1}К в зоны отдыха, жилые комнаты и спальни, кафе, лаундж зоны. ' \
'Очень удачное решение для обеденных и гостинных комнат.<br>'.format(
product_data_Dict['product_type']['name_plural'].lower(),
str(val),
)
elif val < 4601:
temp_txt = 'Модель обладает нейтральным цветом свечения, который прекрасно подходит и как для жилых помещений и комнат, ' \
'так и для рабочих зон (офисов, кабинетов, производств) . ' \
'Данный свет стимулирует к работе не вызывая перенапряжения глаз и не искажая цветопередачу. ' \
'Универсальное и наиболее распространенное решение.<br>'.format(
str(val),
)
elif val < 7001:
temp_txt = 'Цветовая температура {0}К - наиболее оптимально использование в помещениях промышленного назначения, ' \
'административных зданиях, на производствах, складах, гаражах, паркингах. ' \
'Однако могут применяться и в интерьере для создания акцентов в дизайне, ' \
'либо если предпочтения потребителя отданы в пользу белого света. <br>'.format(
str(val),
)
else:
temp_txt = 'От показателя цветовой температуры зависит то, как Вы будут воспринимать предметы и другие объекты освещенные устройством. ' \
'С помощью цветовой температуры можно сделать более приятным отдых и улучшить эффективность работы. ' \
'Отниситесь внимательно к выбору устройства по этому параметру.<br>'.format(
str(val),
)
except Exception as e:
pass
# -------
try:
if property['property']['name'] == 'Тип монтажа':
val = property['property_value']
if val == 'встраиваемый':
install_txt = 'Устройство устанавливается в предварительно вырезанное в поверхности отверстие. ' \
'Этот вариант монтажа используется для подвесных и натяжных потолков, а так же для фальш-стен и ниш.'.format(
str(val),
)
elif val == 'накладной':
install_txt = 'Способ крепления - накладной. Значит эта модель может быть закреплена на любую ровную поверхность.'.format(
str(val),
)
elif val == 'встраиваемый/накладной':
install_txt = '{0} обладает возможностью монтажа как в отверстия на поверхности плоскостей, так и на любую ровную поверхность.'.format(
product_data_Dict['article'],
)
else:
pass
if 'height_visible_part' in product_data_Dict and product_data_Dict['height_visible_part']:
install_txt = install_txt + ' Высота видимой части устройства после монтажа составит {0}мм.<br>'.format(
str(round(product_data_Dict['height_visible_part']))
)
else:
install_txt = install_txt + '<br>'
except Exception as e:
pass
product_data_Dict['seo_text'] = '{0}{1}{2}{3}{4}{5}'.format(
diametr_txt,
power_txt,
lm_txt,
ip_txt,
temp_txt,
install_txt
)
return product_data_Dict
# def generate_seotext_by_properties(product_data_Dict):
#
# power_txt = ''
# ip_txt = ''
# lm_txt = ''
# temp_txt = ''
# install_txt = ''
# diametr_txt = ''
#
# try:
#
#
#
# except Exception as e:
# pass
#
# # ---------
# for property in product_data_Dict['properties_w_values_filtred']:
#
# # ------
#
# try:
#
#
# except Exception as e:
# pass
#
# # ------
#
# try:
#
#
#
# except Exception as e:
# pass
#
# # -------
#
# try:
#
#
#
# except Exception as e:
# pass
#
# # -------
#
# try:
#
#
# except Exception as e:
# pass
#
# # -------
#
# try:
#
#
#
# except Exception as e:
# pass
#
# product_data_Dict['seo_text'] = '{0}{1}{2}{3}{4}{5}'.format(
# diametr_txt,
# power_txt,
# lm_txt,
# ip_txt,
# temp_txt,
# install_txt
# )
#
# return product_data_Dict

View File

@@ -0,0 +1,24 @@
from django.utils.translation import gettext as _
from django.utils.safestring import mark_safe
def get_phone_valid_error(val):
allow_chars = '01234567890()+ -'
error_msg = mark_safe(_('Некорректные символы в номере, введите номер в международном формате с кодом страны'))
if not val:
return None
if len(val) < 10:
return error_msg
if '+' in val and val[0] != '+':
return error_msg
i = 0
while i < len(val):
if val[i] not in allow_chars:
return error_msg
i += 1
return None

View File

@@ -1,6 +1,11 @@
from sets.admin import *
from .models import *
from django.contrib import admin
from django import forms
from django.forms import widgets
class Admin_MsgGroup(Admin_BaseModel):
list_display = [
@@ -11,8 +16,33 @@ class Admin_MsgGroup(Admin_BaseModel):
admin.site.register(MsgGroup,Admin_MsgGroup)
class Admin_Message(Admin_BaseModel):
def cut_group_text(self, obj):
if obj.group:
return obj.group.name[:10]
return '-'
cut_group_text.allow_tags = True
cut_group_text.short_description = 'ticket'
def cut_text(self, obj):
if obj.text:
if len(obj.text) > 20:
return f'{obj.text[:20]}...'
else:
return obj.text
return '-'
cut_text.allow_tags = True
cut_text.short_description = 'сообщение'
search_fields = ['group__name', 'text', 'name', 'id']
list_filter = ['status']
list_display = [
'id', 'msg_type', 'group', 'status', 'sender', 'receiver', 'text',
'id',
# 'msg_type',
'cut_group_text', 'status', 'sender', 'receiver', 'cut_text',
'name',
'order', 'modifiedDT', 'createDT'
]

View File

@@ -1,6 +1,318 @@
from .models import *
from django.db.models import Q
import copy
from .models import *
from django.db.models import Q, Value as V, Count, OuterRef, Subquery
from django.http import HttpResponse, Http404, JsonResponse
from django.template import loader, RequestContext
from django.contrib.auth.decorators import login_required
from BaseModels.mailSender import techSendMail
from django.utils.translation import gettext as _
from datetime import datetime
from django.template.loader import render_to_string
from django.urls import reverse
import json
from datetime import datetime, time
from django.conf import settings
from AuthApp.funcs import get_user_timezone_Dict
def get_unanswered_msgs_count_for_user(user):
msgs = Message.objects.filter(receiver=user, status='sended', group=None)
return msgs.count()
def get_update_chat_Dict(data):
from AuthApp.models import User
res_Dict = {}
msgs = []
Dict = {}
context_Dict = {}
last_message_modifiedDT = None
required_beep = False
required_full_support_chat_html = False
try:
if type(data) == str:
data = json.loads(data)
ticket = None
tpl_name = 'blocks/profile/b_messages_container.html'
if 'ticket_id' in data and data['ticket_id'] and data['ticket_id'] != 'null':
ticket = MsgGroup.objects.get(id=data['ticket_id'])
res_Dict.update({'ticket': ticket.id})
sender = User.objects.get(id=data['sender'])
receiver = None
if 'receiver' in data and data['receiver']:
receiver = User.objects.get(id=data['receiver'])
if data['sender'] == data['cur_user']:
user = copy.copy(sender)
cur_receiver = copy.copy(receiver)
else:
user = copy.copy(receiver)
cur_receiver = copy.copy(sender)
# context_Dict.update({'user': receiver})
# context_Dict.update({'cur_receiver': receiver})
context_Dict.update({
'cur_receiver': cur_receiver,
'user': user,
})
context_Dict.update(get_user_timezone_Dict(user))
if sender == receiver:
print('!')
required_beep = data['required_beep']
if not ticket:
if data['receiver'] == data['cur_user']:
# получаем правую панель с получателями
# получатели
receivers, unread_msgs_count = get_chat_receivers_for_user(receiver)
# формируем правую панель
context_Dict.update({
'receivers': receivers,
'MEDIA_URL': settings.MEDIA_URL
})
users_list_html = render_to_string(
'blocks/profile/b_list_of_users_messenger.html', context_Dict)
res_Dict.update({
'users_list_html': users_list_html,
'unread_msgs_count': unread_msgs_count
})
msgs = get_msgs_for_chat_w_users(sender, receiver)
if ticket:
msgs = get_messages_for_ticket(ticket)
# если меньше 3 сообщений значит выведена не полная форма - требуется другой шаблон
if len(msgs) < 3 and data['receiver'] == data['cur_user']:
tpl_name = 'blocks/profile/b_support_chat.html'
required_full_support_chat_html = True
elif 'bad_manager' in data:
tpl_name = 'blocks/profile/b_support_chat.html'
required_full_support_chat_html = True
context_Dict.update({'ticket': ticket})
if ticket.manager:
context_Dict.update({'new_msg_allow': True})
context_Dict.update({
'messages': msgs,
'MEDIA_URL': settings.MEDIA_URL,
})
html = render_to_string(tpl_name, context_Dict)
if required_full_support_chat_html:
res_Dict.update({'support_chat_html': html})
else:
res_Dict.update({'chat_html': html})
res_Dict.update({
'required_beep': required_beep,
})
return res_Dict
except Exception as e:
msg = f'update_chat_ajax2 Error = {str(e)}'
return {'error': msg}
def send_msg(data):
from AuthApp.models import User
import base64
import os
res_Dict = {}
msg = None
required_update_tickets_list_wo_managers = False
try:
if type(data) == str:
data = json.loads(data)
ticket = None
tpl_name = 'blocks/profile/b_messages_container.html'
if 'text' in data or 'files' in data:
if 'ticket_id' in data:
ticket = MsgGroup.objects.get(id=data['ticket_id'])
sender = User.objects.get(id=data['sender'])
receiver = User.objects.get(id=data['receiver'])
msg_create_kwargs = {}
# если только что манагер присоединился к тикету
if ticket:
required_update_tickets_list_wo_managers = True
if not ticket.manager:
if sender.is_staff:
ticket.manager = sender
else:
ticket.manager = receiver
ticket.save()
# создаем сообщение на базе темы и сообщения введенных пользователем при создании тикета
kwargs = {
'sender': ticket.owner,
'receiver': ticket.manager,
'text': ticket.text,
'group': ticket,
}
msg = Message.objects.create(**kwargs)
Message.objects.filter(id=msg.id).update(modifiedDT=ticket.createDT, createDT=ticket.createDT)
# ------------------------
else:
if not sender in (ticket.owner, ticket.manager) or not receiver in (ticket.owner, ticket.manager):
return {
'msg': None,
'required_update_tickets_list_wo_managers': required_update_tickets_list_wo_managers
}
res_Dict.update({
'ticket': ticket,
})
msg_create_kwargs.update({
'group': ticket,
})
res_Dict.update(get_ticketsDict_for_staff(sender))
# if not msg:
msg_create_kwargs.update({
'sender': sender,
'receiver': receiver,
})
if 'text' in data:
msg_create_kwargs.update({'text': data['text']})
msg = Message.objects.create(**msg_create_kwargs)
if 'files' in data:
files_list = []
for file in data['files']:
file_data =json.loads(file)
if not os.path.exists(f'chat_file_storage/{msg.id}'):
os.makedirs(f'chat_file_storage/{msg.id}')
f = open(f'chat_file_storage/{msg.id}/{file_data["file_name"]}', 'wb+')
head, content = file_data['file'].split(',')
content = base64.b64decode(content)
f.write(content)
f.close()
del file_data['file']
files_list.append(file_data)
msg.files = files_list
msg.save(update_fields=['files'])
# if ticket:
# msgs = get_messages_for_ticket(ticket)
# else:
# msgs = get_msgs_for_chat_w_users(sender, receiver)
#
# receivers, unread_msgs_count = get_chat_receivers_for_user(sender)
#
# res_Dict.update({
# 'messages': msgs,
# 'cur_receiver': receiver,
# 'receivers': receivers,
# 'text': data['text'],
# 'modifiedDT': msgs[0].modifiedDT
# })
#
# html = render_to_string(tpl_name, res_Dict)
# return {'html': html, 'sender': data['sender']}
return {
'msg': msg,
'required_update_tickets_list_wo_managers': required_update_tickets_list_wo_managers
}
except Exception as e:
msg = f'send_msg_ajax Error = {str(e)}'
print(msg)
# return {'error': msg}
return {
'msg': msg,
'required_update_tickets_list_wo_managers': required_update_tickets_list_wo_managers
}
# def get_create_route_for_customer_page_content_Dict(request):
# from AuthApp.models import User
#
# msgs = []
# try:
# cur_receiver = User.objects.get(id=receiver_id)
#
# msgs = get_msgs_for_chat_w_users(request.user, cur_receiver)
# msgs.filter(receiver=request.user).update(status='seen')
#
# except User.DoesNotExist:
# cur_receiver = None
#
# receivers, unread_msgs_count = get_chat_receivers_for_user(request.user)
#
# Dict = {
# 'cur_receiver': cur_receiver,
# 'messages': msgs,
# 'receivers': receivers,
# 'page': 'chat',
# }
# return Dict
def get_chat_page_content_html(request, receiver_id=None):
from AuthApp.models import User
from AuthApp.funcs import get_user_timezone_Dict
msgs = []
try:
cur_receiver = User.objects.get(id=receiver_id)
msgs = get_msgs_for_chat_w_users(request.user, cur_receiver)
msgs.filter(receiver=request.user).update(status='seen')
except User.DoesNotExist:
cur_receiver = None
receivers, unread_msgs_count = get_chat_receivers_for_user(request.user)
Dict = {
'cur_receiver': cur_receiver,
'messages': msgs,
'receivers': receivers,
'page': 'chat',
}
Dict.update(get_user_timezone_Dict(request.user, request=request))
tpl_name = 'blocks/profile/b_chats.html'
html = render_to_string(tpl_name, Dict, request=request)
return html
def get_chat_page_content_Dict(request, receiver_id=None):
from AuthApp.models import User
@@ -48,19 +360,38 @@ def get_chat_receivers_for_user(user, cur_receiver=None):
for msg in msgs:
if msg.receiver not in receivers_list and msg.receiver != user:
msg.receiver.unread_msgs_count = 0
msg.receiver.last_msg = msg.text
if msg.text:
msg.receiver.last_msg = msg.text
elif msg.files:
msg.receiver.last_msg = msg.files[len(msg.files)-1]['file_name']
receivers_list.append(msg.receiver)
if msg.sender not in receivers_list and msg.sender != user:
msg.sender.unread_msgs_count = 0
receivers_list.append(msg.sender)
msg.sender.last_msg = msg.text
if msg.text:
msg.sender.last_msg = msg.text
elif msg.files:
msg.sender.last_msg = msg.files[len(msg.files)-1]['file_name']
if msg.receiver == user and (not cur_receiver or msg.sender != cur_receiver):
if msg.status == 'sended':
i = receivers_list.index(msg.sender)
receivers_list[i].unread_msgs_count += 1
unread_msgs_count += 1
# if msg.status == 'sended':
# i = receivers_list.index(msg.sender)
# receivers_list[i].unread_msgs_count += 1
# unread_msgs_count += 1
# if msg.receiver == user:# and (not cur_receiver or msg.sender != cur_receiver):
# if msg.status == 'sended':
# i = receivers_list.index(msg.sender)
# receivers_list[i].unread_msgs_count += 1
# unread_msgs_count += 1
return receivers_list, unread_msgs_count
# receivers_list.extend((item.sender for item in receivers if item.sender != user))
@@ -90,12 +421,38 @@ def get_ticketsDict_for_staff(user):
def get_tickets_wo_manager():
tickets = MsgGroup.objects.filter(enable=True, manager=None).order_by('-modifiedDT')
tickets = MsgGroup.objects.filter(
enable=True, manager=None
).annotate(
unread_msgs_count=V(1)
).order_by('-modifiedDT')
return tickets
def get_tickets_for_manager(user):
unread_msgs = Message.objects.filter(
group=31, status='sended', receiver=user
)
unread_msgs = Message.objects.filter(
group=OuterRef('pk'), status='sended', receiver=user
).values('id')[:1]
msgs = Message.objects.filter(
group=OuterRef('pk')
).order_by('-modifiedDT').values('modifiedDT')[:1]
tickets = MsgGroup.objects.filter(
enable=True, manager=user
).annotate(
unread_msgs_count = Count(Subquery(unread_msgs)),
last_msg_modifiedDT = Subquery(msgs)
# unread_msgs_count=Count(
# 'rel_messages_for_group',
# filter=Q(rel_messages_for_group__status='sended', rel_messages_for_group__receiver=user)
# )
).order_by('-unread_msgs_count', '-last_msg_modifiedDT')
tickets = MsgGroup.objects.filter(enable=True, manager=user).order_by('-modifiedDT')
return tickets

View File

@@ -10,7 +10,9 @@ urlpatterns = [
path('support_create_ticket_form/', support_create_ticket_form_ajax, name='support_create_ticket_form_ajax'),
path('create_ticket/', create_ticket_ajax, name='create_ticket_ajax'),
path('support_show_chat_by_ticket/', support_show_chat_by_ticket_ajax, name='support_show_chat_by_ticket_ajax'),
path('send_msg/', send_msg_ajax, name='send_msg_ajax'),
path('update_chat/', update_chat_ajax, name='update_chat_ajax'),
# path('send_msg/', send_msg_ajax, name='send_msg_ajax'),
# path('update_chat/', update_chat_ajax2, name='update_chat_ajax'),
path('show_chat_w_user/', show_chat_w_user_ajax, name='show_chat_w_user_ajax'),
path('get_file_from_msg/', get_file_from_msg_ajax, name='get_file_from_msg_ajax'),
]

View File

@@ -14,6 +14,33 @@ from django.urls import reverse
from .funcs import *
import json
from datetime import datetime, time
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
@login_required(login_url='/profile/login/')
def get_file_from_msg_ajax(request):
if request.method != 'POST':
raise Http404
try:
data = json.loads(request.body)
msg = Message.objects.get(id=data['message_id'])
res_Dict = {}
for file in msg.files:
if file['file_name'] == data['file_name']:
res_Dict = file
break
return JsonResponse(res_Dict, status=200)
except Exception as e:
msg = f'get_file_from_msg_ajax Error = {str(e)}'
return JsonResponse({'error': msg}, status=400)
@login_required(login_url='/profile/login/')
@@ -26,10 +53,12 @@ def show_chat_w_user_ajax(request):
data = json.loads(request.body)
Dict = get_chat_page_content_Dict(request, data['user_id'])
Dict.update(get_user_timezone_Dict(request.user, request=request))
tpl_name = 'blocks/profile/b_chats.html'
html = render_to_string(tpl_name, Dict, request=request)
return JsonResponse({'html': html}, status=200)
@@ -38,6 +67,96 @@ def show_chat_w_user_ajax(request):
return JsonResponse({'error': msg}, status=400)
def update_chat_ajax2(request):
from AuthApp.models import User
if request.method != 'POST':
raise Http404
res_Dict = {}
msgs = []
Dict = {}
context_Dict = {}
last_message_modifiedDT = None
required_beep = False
try:
data = json.loads(request.body)
ticket = None
tpl_name = 'blocks/profile/b_messages_container.html'
if 'ticket_id' in data and data['ticket_id'] and data['ticket_id'] != 'null':
ticket = MsgGroup.objects.get(id=data['ticket_id'])
sender = User.objects.get(id=data['sender'])
receiver = None
if 'receiver' in data and data['receiver']:
receiver = User.objects.get(id=data['receiver'])
context_Dict.update({'cur_receiver': receiver})
context_Dict.update(get_user_timezone_Dict(request.user, request=request))
if not ticket:
# получаем правую панель с получателями
# получатели
receivers, unread_msgs_count = get_chat_receivers_for_user(request.user, receiver)
# собираем для сохранения в профиле
receivers_unread_msgs_count = sorted(
[{'id': item.id, 'unread_msgs_count': item.unread_msgs_count} for item in receivers],
key=lambda d: d['id'])
# забираем данные из профиля
saved_receivers_unread_msgs_count = request.user.user_profile.get_node_by_name(
'receivers_unread_msgs_count')
# если данных нет или данные поменялись
if saved_receivers_unread_msgs_count == None or receivers_unread_msgs_count != saved_receivers_unread_msgs_count:
# записываем данные в профиль
request.user.user_profile.add_node_to_json_data(
{'receivers_unread_msgs_count': receivers_unread_msgs_count}, save=True)
# разрешаем сигнал
required_beep = True
# формируем правую панель
context_Dict.update({'receivers': receivers})
users_list_html = render_to_string(
'blocks/profile/b_list_of_users_messenger.html', context_Dict, request=request)
res_Dict.update({
'users_list_html': users_list_html,
'unread_msgs_count': unread_msgs_count
})
if sender and receiver:
msgs = get_msgs_for_chat_w_users(sender, receiver)
unreaded_msgs = msgs.filter(status='sended')
if msgs and unreaded_msgs:
context_Dict.update({'messages': msgs})
chat_html = render_to_string(tpl_name, context_Dict, request=request)
res_Dict.update({'chat_html': chat_html})
required_beep = True
unreaded_msgs.update(status='seen')
res_Dict.update({
'required_beep': required_beep,
})
return JsonResponse(res_Dict, status=200)
except Exception as e:
msg = f'update_chat_ajax2 Error = {str(e)}'
return JsonResponse({'error': msg}, status=400)
def update_chat_ajax(request):
from AuthApp.models import User
@@ -73,6 +192,7 @@ def update_chat_ajax(request):
receiver = User.objects.get(id=data['receiver'])
context_Dict.update({'cur_receiver': receiver})
context_Dict.update(get_user_timezone_Dict(request.user, request=request))
if ticket:
@@ -84,6 +204,8 @@ def update_chat_ajax(request):
context_Dict.update({'messages': msgs})
context_Dict = get_ticketsDict_for_staff(request.user)
context_Dict.update(get_user_timezone_Dict(request.user))
tickets_list_html = render_to_string(
'blocks/profile/b_list_of_tickets_support_chat.html', context_Dict, request=request)
res_Dict.update({
@@ -94,13 +216,17 @@ def update_chat_ajax(request):
# получаем правую панель с получателями
receivers, unread_msgs_count = get_chat_receivers_for_user(request.user, receiver)
saved_unread_msgs_count = request.user.user_profile.get_node_by_name('receivers_unread_msgs_count')
if saved_unread_msgs_count == None or saved_unread_msgs_count != unread_msgs_count:
request.user.user_profile.add_node_to_json_data({'receivers_unread_msgs_count': unread_msgs_count}, save=True)
receivers_unread_msgs_count = sorted([{'id':item.id, 'unread_msgs_count': item.unread_msgs_count} for item in receivers], key=lambda d: d['id'])
saved_receivers_unread_msgs_count = request.user.user_profile.get_node_by_name('receivers_unread_msgs_count')
if saved_receivers_unread_msgs_count == None or receivers_unread_msgs_count != saved_receivers_unread_msgs_count:
request.user.user_profile.add_node_to_json_data(
{'receivers_unread_msgs_count': receivers_unread_msgs_count}, save=True)
# if unread_msgs_count:
required_beep = True
context_Dict.update({'receivers': receivers})
context_Dict.update(get_user_timezone_Dict(request.user))
users_list_html = render_to_string(
'blocks/profile/b_list_of_users_messenger.html', context_Dict, request=request)
res_Dict.update({
@@ -133,116 +259,118 @@ def update_chat_ajax(request):
return JsonResponse(res_Dict, status=200)
except Exception as e:
msg = f'send_msg_ajax Error = {str(e)}'
msg = f'update_chat_ajax Error = {str(e)}'
return JsonResponse({'error': msg}, status=400)
@login_required(login_url='/profile/login/')
def send_msg_ajax(request):
from AuthApp.models import User
if request.method != 'POST':
raise Http404
res_Dict = {}
msg = None
try:
data = json.loads(request.body)
ticket = None
tpl_name = 'blocks/profile/b_messages_container.html'
if data['text']:
if 'ticket_id' in data:
ticket = MsgGroup.objects.get(id=data['ticket_id'])
sender = User.objects.get(id=data['sender'])
receiver = User.objects.get(id=data['receiver'])
msg_create_kwargs = {}
# если только что манагер присоединился к тикету
if ticket:
if not ticket.manager:
if sender.is_staff:
ticket.manager = sender
else:
ticket.manager = receiver
ticket.save()
kwargs = {
'sender': ticket.owner,
'receiver': ticket.manager,
'text': ticket.text,
}
kwargs.update({'group': ticket})
msg = Message.objects.create(**kwargs)
Message.objects.filter(id=msg.id).update(modifiedDT=ticket.createDT, createDT=ticket.createDT)
res_Dict.update({
'ticket': ticket,
# 'cur_receiver': receiver,
# 'messages': get_messages_for_ticket(ticket),
# 'text': data['text'],
# 'modifiedDT': msg.modifiedDT
})
msg_create_kwargs.update({
'group': ticket,
})
res_Dict.update(get_ticketsDict_for_staff(request.user))
# if not msg:
msg_create_kwargs.update({
'sender': sender,
'receiver': receiver,
'text': data['text']
})
session_data = {
'for_save_to_session':{
'user_alerts':{
'new_message': True
}
}
}
receiver.user_profile.add_node_to_json_data(session_data, save=True)
msg = Message.objects.create(**msg_create_kwargs)
if ticket:
msgs = get_messages_for_ticket(ticket)
else:
msgs = get_msgs_for_chat_w_users(sender, receiver)
receivers, unread_msgs_count = get_chat_receivers_for_user(request.user)
res_Dict.update({
'messages': msgs,
'cur_receiver': receiver,
'receivers': receivers,
'text': data['text'],
'modifiedDT': msgs[0].modifiedDT
})
html = render_to_string(tpl_name, res_Dict, request=request)
return JsonResponse({'html': html}, status=200)
except Exception as e:
msg = f'send_msg_ajax Error = {str(e)}'
return JsonResponse({'error': msg}, status=400)
# @login_required(login_url='/profile/login/')
# def send_msg_ajax(request):
# from AuthApp.models import User
#
# if request.method != 'POST':
# raise Http404
#
# res_Dict = {}
# msg = None
#
# try:
#
# data = json.loads(request.body)
# ticket = None
#
# tpl_name = 'blocks/profile/b_messages_container.html'
#
# if data['text']:
#
#
# if 'ticket_id' in data:
# ticket = MsgGroup.objects.get(id=data['ticket_id'])
#
# sender = User.objects.get(id=data['sender'])
# receiver = User.objects.get(id=data['receiver'])
#
# msg_create_kwargs = {}
#
# # если только что манагер присоединился к тикету
# if ticket:
# if not ticket.manager:
# if sender.is_staff:
# ticket.manager = sender
# else:
# ticket.manager = receiver
#
# ticket.save()
#
# kwargs = {
# 'sender': ticket.owner,
# 'receiver': ticket.manager,
# 'text': ticket.text,
# }
#
# kwargs.update({'group': ticket})
#
# msg = Message.objects.create(**kwargs)
# Message.objects.filter(id=msg.id).update(modifiedDT=ticket.createDT, createDT=ticket.createDT)
#
# # if
#
# res_Dict.update({
# 'ticket': ticket,
# # 'cur_receiver': receiver,
# # 'messages': get_messages_for_ticket(ticket),
# # 'text': data['text'],
# # 'modifiedDT': msg.modifiedDT
# })
#
# msg_create_kwargs.update({
# 'group': ticket,
# })
#
# res_Dict.update(get_ticketsDict_for_staff(request.user))
#
#
# # if not msg:
# msg_create_kwargs.update({
# 'sender': sender,
# 'receiver': receiver,
# 'text': data['text']
# })
#
# session_data = {
# 'for_save_to_session':{
# 'user_alerts':{
# 'new_message': True
# }
# }
# }
#
# receiver.user_profile.add_node_to_json_data(session_data, save=True)
#
# msg = Message.objects.create(**msg_create_kwargs)
#
# if ticket:
# msgs = get_messages_for_ticket(ticket)
# else:
# msgs = get_msgs_for_chat_w_users(sender, receiver)
#
# receivers, unread_msgs_count = get_chat_receivers_for_user(request.user)
#
#
# res_Dict.update({
# 'messages': msgs,
# 'cur_receiver': receiver,
# 'receivers': receivers,
# 'text': data['text'],
# 'modifiedDT': msgs[0].modifiedDT
# })
#
#
# html = render_to_string(tpl_name, res_Dict, request=request)
# return JsonResponse({'html': html}, status=200)
#
# except Exception as e:
# msg = f'send_msg_ajax Error = {str(e)}'
# return JsonResponse({'error': msg}, status=400)
@@ -272,16 +400,25 @@ def support_show_chat_by_ticket_ajax(request):
# if len(msgs) > 1:
new_msg_allow = True
seen_msgs = msgs.filter(receiver=request.user, status='sended')
if seen_msgs:
seen_msgs.update(status='seen')
msgs = get_messages_for_ticket(ticket)
Dict = {
'ticket': ticket,
'messages': msgs,
'cur_receiver': cur_receiver,
'new_msg_allow': new_msg_allow
'new_msg_allow': new_msg_allow,
'staff': request.user.is_staff,
# 'mobile': data['mobile']
}
Dict.update(get_ticketsDict_for_staff(request.user))
tpl_name = 'blocks/profile/b_support_chat.html'
Dict.update(get_user_timezone_Dict(request.user))
tpl_name = 'blocks/profile/b_support_chat.html'
Dict.update(get_user_timezone_Dict(request.user, request=request))
html = render_to_string(tpl_name, Dict, request=request)
return JsonResponse({'html': html}, status=200)
@@ -301,6 +438,9 @@ def support_create_ticket_form_ajax(request):
Dict = {
'form': TicketForm()
}
Dict.update(get_user_timezone_Dict(request.user))
tpl_name = 'blocks/profile/b_create_ticket.html'
html = render_to_string(tpl_name, Dict, request=request)
@@ -322,6 +462,8 @@ def create_ticket_ajax(request):
if not form.is_valid():
form.initial = form.cleaned_data
Dict = {'form': form}
Dict.update(get_user_timezone_Dict(request.user))
html = render_to_string('blocks/profile/b_create_ticket.html', Dict, request=request)
return JsonResponse({'html': html}, status=400)
@@ -330,11 +472,39 @@ def create_ticket_ajax(request):
ticket.enable = True
ticket.save()
from .websocket_views import send_to_support_managers_list_tickets_wo_manager
send_to_support_managers_list_tickets_wo_manager(ticket, required_beep=True)
# # рассылаем всем менеджерам сообщение
#
# Dict = {
# 'ticket': ticket,
# 'tickets_wo_manager': get_tickets_wo_manager()
# }
# tickets_wo_manager_html = render_to_string('widgets/w_tickets_wo_manager.html', Dict, request=request)
#
# group_name = 'support_managers'
# Dict = {
# 'type': 'update_chat',
# 'tickets_wo_manager_html': tickets_wo_manager_html,
# 'required_beep': True,
# 'group_name': group_name,
# }
# channel_layer = get_channel_layer()
# async_to_sync(channel_layer.group_send)(
# group_name,
# Dict
# )
#
#
# # ---------------------
msgs_for_ticket = get_messages_for_ticket(ticket)
Dict = {
'ticket': ticket,
'messages': get_messages_for_ticket(ticket)
'messages': msgs_for_ticket
}
Dict.update(get_user_timezone_Dict(request.user))
html = render_to_string('blocks/profile/b_support_chat.html', Dict, request=request)
@@ -346,12 +516,24 @@ def create_ticket_ajax(request):
except Exception as e:
msg = f'{_("ошибка в запросе")} = {str(e)}'
errors_Dict = {
'errors': {
'all__': f'ошибка в запросе = {str(e)}'
'all__': msg
}
}
Dict = {'form': errors_Dict}
html = render_to_string('blocks/profile/b_create_ticket.html', Dict, request=request)
return JsonResponse({'html': html}, status=400)
def sendDeployments(owner, armies):
type = "renderDeployments"
message = owner + " has " + str(armies) + " to deploy"
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
'render_updates_group',
{'type': 'render', 'message': message}
)

View File

@@ -0,0 +1,21 @@
# Generated by Django 4.2.2 on 2023-08-11 23:21
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('ChatServiceApp', '0003_msggroup_text'),
]
operations = [
migrations.AlterModelOptions(
name='message',
options={'verbose_name': 'Сообщение', 'verbose_name_plural': 'Сообщения'},
),
migrations.AlterModelOptions(
name='msggroup',
options={'verbose_name': 'Тикет', 'verbose_name_plural': 'Тикеты'},
),
]

View File

@@ -3,41 +3,53 @@ from BaseModels.base_models import BaseModel
from django.utils.translation import gettext_lazy as _
msg_type_choices = (
('support', 'техподдержка'),
('private', 'личное')
('support', _('техподдержка')),
('private', _('личное'))
)
msg_status_choices = (
('sended', 'Отправлено'),
('seen', 'Просмотрено')
('sended', _('Отправлено')),
('seen', _('Просмотрено'))
)
grp_msg_status = (
('open', 'Открыт'),
('answered', 'Отвечен'),
('closed', 'Закрыт')
('open', _('Открыт')),
('answered', _('Отвечен')),
('closed', _('Закрыт'))
)
grp_msg_department = (
('support', 'Отдел: Техническая поддержка'),
('finance', 'Отдел: Финансовый департамент'),
('support', _('Отдел: Техническая поддержка')),
('finance', _('Отдел: Финансовый департамент')),
)
class MsgGroup(BaseModel):
from AuthApp.models import User
department = models.CharField(verbose_name='Отдел', default='support', choices=grp_msg_department)
status = models.CharField(verbose_name='Статус', default='open', choices=grp_msg_status)
department = models.CharField(verbose_name=_('Отдел'), default='support', choices=grp_msg_department)
status = models.CharField(verbose_name=_('Статус'), default='open', choices=grp_msg_status)
text = models.TextField(verbose_name='Сообщение')
text = models.TextField(verbose_name=_('Сообщение'))
owner = models.ForeignKey(User, verbose_name=_('Владелец'), related_name='rel_msgGroups_for_owner',
on_delete=models.SET_NULL, null=True)
manager = models.ForeignKey(User, verbose_name=_('Менеджер'), related_name='rel_msgGroups_for_manager',
on_delete=models.SET_NULL, null=True)
def get_last_msg_txt(self):
msg = self.rel_messages_for_group.all().order_by('-createDT').first()
if not msg:
return self.text
if msg.text:
return msg.text
elif msg.files:
return msg.files[0]['file_name']
return self.name
class Meta:
verbose_name = _('Тикет')
verbose_name_plural = _('Тикеты')
@@ -46,23 +58,23 @@ class MsgGroup(BaseModel):
class Message(BaseModel):
from AuthApp.models import User
msg_type = models.CharField(max_length=50, verbose_name='Тип сообщения', default='private', choices=msg_type_choices)
msg_type = models.CharField(max_length=50, verbose_name=_('Тип сообщения'), default='private', choices=msg_type_choices)
group = models.ForeignKey(
MsgGroup, verbose_name='Группа сообщений', related_name='rel_messages_for_group',
MsgGroup, verbose_name=_('Группа сообщений'), related_name='rel_messages_for_group',
on_delete=models.SET_NULL, null=True)
sender = models.ForeignKey(
User, verbose_name='Отправитель', on_delete=models.CASCADE, related_name='rel_messages_for_sender'
User, verbose_name=_('Отправитель'), on_delete=models.CASCADE, related_name='rel_messages_for_sender'
)
receiver = models.ForeignKey(
User, verbose_name='Получатель', on_delete=models.CASCADE, related_name='rel_messages_for_receiver'
User, verbose_name=_('Получатель'), on_delete=models.CASCADE, related_name='rel_messages_for_receiver'
)
text = models.TextField(verbose_name='Сообщение')
text = models.TextField(verbose_name=_('Сообщение'))
status = models.CharField(verbose_name='Статус', default='sended', choices=msg_status_choices)
status = models.CharField(verbose_name=_('Статус'), default='sended', choices=msg_status_choices)
files = models.JSONField(verbose_name='Прикрепленные файлы', default=dict)
files = models.JSONField(verbose_name=_('Прикрепленные файлы'), default=dict)
class Meta:
verbose_name = _('Сообщение')

View File

@@ -1,5 +1,6 @@
from django import template
from django.template.defaultfilters import stringfilter
from django.conf import settings
register = template.Library()
@@ -10,9 +11,40 @@ register = template.Library()
# from django.utils.html import mark_safe
# @register.filter('get_msg_side')
@register.simple_tag
def get_ws_address():
return settings.WS_ADDRESS
@register.simple_tag
def get_filesize(size):
if size:
unit = 'B'
if size / 1024 > 1:
unit = 'KB'
size = size / 1024
if size / 1024 > 1:
unit = 'MB'
size = size / 1024
if size / 1024 > 1:
unit = 'GB'
size = size / 1024
if size / 1024 > 1:
unit = 'TB'
size = size / 1024
size = round(size, 2)
return f'{str(size)}{unit}'
else:
return ''
@register.simple_tag
def get_msg_side(cur_user, ticket, msg):
if msg:
# if msg.sender == cur_user:
# return 'left'
# else:
# return 'right'
if msg.sender == cur_user:
return 'right'
else:

11
ChatServiceApp/urls.py Normal file
View File

@@ -0,0 +1,11 @@
# coding=utf-8
from django.urls import path
# from AuthApp.js_views import *
# from AuthApp.import_funcs import *
from .views import *
from django.contrib.auth import views
from RoutesApp.js_views import new_route_view_ajax
urlpatterns = [
path('get_file/<str:msg_id>/<str:file_name>', get_file_from_message, name='get_file_from_message'),
]

View File

@@ -1,3 +1,26 @@
from django.shortcuts import render
import json
# Create your views here.
from django.http import HttpResponse, Http404, FileResponse
from django.template import loader, RequestContext
from django.contrib.auth.decorators import login_required
from .models import *
from django.conf import settings
def get_file_from_message(request, msg_id, file_name):
from django.http import FileResponse
try:
msg = Message.objects.get(id=msg_id)
if request.user not in (msg.sender, msg.receiver):
raise Http404
for file in msg.files:
if file['file_name'] == file_name:
f = open(f'chat_file_storage/{msg.id}/{file["file_name"]}', 'rb')
return FileResponse(f)
except Exception as e:
raise Http404

View File

@@ -0,0 +1,8 @@
from django.urls import re_path
from .websocket_views import *
websocket_urlpatterns = [
re_path(r'ws/socket-server/', ChatConsumer.as_asgi()),
# re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
]

View File

@@ -0,0 +1,260 @@
import json
from channels.generic.websocket import WebsocketConsumer, AsyncWebsocketConsumer, JsonWebsocketConsumer
from asgiref.sync import async_to_sync, sync_to_async
# from channels.auth import channel_session_user, channel_session_user_from_http
from channels.layers import get_channel_layer
from django.template.loader import render_to_string
def send_to_support_managers_list_tickets_wo_manager(ticket, required_beep=False):
from.funcs import get_tickets_wo_manager
# рассылаем всем менеджерам сообщение
Dict = {
'ticket': ticket,
'tickets_wo_manager': get_tickets_wo_manager()
}
tickets_wo_manager_html = render_to_string('widgets/w_tickets_wo_manager.html', Dict)
group_name = 'support_managers'
Dict = {
'type': 'update_chat',
'tickets_wo_manager_html': tickets_wo_manager_html,
'required_beep': required_beep,
'group_name': group_name,
}
if ticket.manager:
Dict.update({'ticket_manager': ticket.manager.id})
channel_layer = get_channel_layer()
async_to_sync(channel_layer.group_send)(
group_name,
Dict
)
# ---------------------
return True
def get_tickets_wo_manager_html(ticket, user, data):
from .funcs import get_tickets_wo_manager
# если не менеджер - возвращаем None
if not ticket or ticket.manager.id != user.id:
return None
Dict = {
'ticket': ticket,
'tickets_wo_manager': get_tickets_wo_manager()
}
tickets_wo_manager_html = render_to_string('widgets/w_tickets_wo_manager.html', Dict)
return tickets_wo_manager_html
def get_tickets_w_manager_html(ticket, user, data):
from .funcs import get_tickets_for_manager
# если не менеджер - возвращаем None
if not ticket or ticket.manager.id != user.id:
return None
Dict = {
'ticket': ticket,
'tickets_for_manager': get_tickets_for_manager(user)
}
tickets_w_manager_html = render_to_string('widgets/w_tickets_w_manager.html', Dict)
return tickets_w_manager_html
class ChatConsumer(WebsocketConsumer):
# def __init__(self, *args, **kwargs):
# super().__init__(args, kwargs)
# self.room_name = None
def disconnect(self, close_code):
print("Closed websocket with code: ", close_code)
for gr in self.groups:
async_to_sync(self.channel_layer.group_discard)(
gr,
self.channel_name
)
self.close()
def connect(self):
print('ws connect')
user_group = f'user_{self.scope["user"].id}'
add_to_group = False
if not self.groups or not user_group in self.groups or not self.channel_name in self.groups[user_group]:
add_to_group = True
if add_to_group:
if not self.groups:
self.groups = {user_group: [self.channel_name]}
elif not user_group in self.groups:
self.groups[user_group].append(self.channel_name)
else:
self.groups.update({user_group: [self.channel_name]})
async_to_sync(self.channel_layer.group_add)(
user_group,
self.channel_name
)
print(f'created group user_{self.scope["user"].id}')
if self.scope['user'].is_staff:
async_to_sync(self.channel_layer.group_add)(
f'support_managers',
self.channel_name
)
print(f'add user {self.scope["user"].id} to group support_managers')
self.accept()
def receive(self, text_data=None, bytes_data=None):
if len(text_data) < 500:
print(f'ws receive text_data = {text_data}')
else:
print(f'ws receive text_data size = {len(text_data)}')
import base64
from AuthApp.models import User
from .models import Message, MsgGroup
data = json.loads(text_data)
# if 'file' in data:
# file_Dict = json.loads(data['file'])
#
# f = open(f'chat_file_storage/{file_Dict["file_name"]}', 'wb+')
# head, content = file_Dict['file'].split(',')
# content = base64.b64decode(content)
# f.write(content)
# f.close()
sender = data['sender']
receiver = data['receiver']
sender_obj = User.objects.get(id=sender)
receiver_obj = User.objects.get(id=receiver)
from .funcs import send_msg, get_update_chat_Dict
data.update({
'cur_user': sender,
'required_beep': False,
})
res = send_msg(data)
if not res['msg'] or type(res['msg']) == str:
data.update({'bad_manager': True})
ticket = None
if 'ticket_id' in data and data['ticket_id']:
ticket = MsgGroup.objects.get(id=data['ticket_id'])
# receiver_obj = User.objects.get(id=receiver)
msgs = Message.objects.filter(
receiver__id=sender, group__id=data['ticket_id']
)
msgs.update(status='seen')
if 'required_update_tickets_list_wo_managers' in res and res['required_update_tickets_list_wo_managers']:
send_to_support_managers_list_tickets_wo_manager(ticket, required_beep=False)
Dict = get_update_chat_Dict(data)
group_name = f'user_{sender}'
resDict = {
'type': 'update_chat',
'sender': sender,
'receiver': receiver,
'group_name': group_name,
}
if ticket:
tickets_wo_manager_html = get_tickets_wo_manager_html(ticket, sender_obj, data)
if tickets_wo_manager_html:
resDict.update({'tickets_wo_manager_html': tickets_wo_manager_html})
tickets_w_manager_html = get_tickets_w_manager_html(ticket, sender_obj, data)
if tickets_w_manager_html:
resDict.update({'tickets_w_manager_html': tickets_w_manager_html})
resDict.update(Dict)
async_to_sync(self.channel_layer.group_send)(
group_name,
resDict
)
group_name = f'user_{receiver}'
# if group_name in self.channel_layer.groups.keys():
data.update({
'cur_user': receiver,
'required_beep': True,
})
Dict = get_update_chat_Dict(data)
if 'support_chat_html' in Dict:
msg_type = 'update_support_chat'
else:
msg_type = 'update_chat'
resDict = {
'type': msg_type,
'sender': receiver,
'receiver': sender,
'group_name': group_name,
}
if ticket:
tickets_wo_manager_html = get_tickets_wo_manager_html(ticket, receiver_obj, data)
if tickets_wo_manager_html:
resDict.update({'tickets_wo_manager_html': tickets_wo_manager_html})
tickets_w_manager_html = get_tickets_w_manager_html(ticket, receiver_obj, data)
if tickets_w_manager_html:
resDict.update({'tickets_w_manager_html': tickets_w_manager_html})
resDict.update(Dict)
async_to_sync(self.channel_layer.group_send)(
group_name,
resDict
)
def echo(self, data):
print('ws echo')
self.send(text_data=json.dumps(data))
def update_support_chat(self, data):
print(f'ws update_support_chat {data["group_name"]}')
self.send(text_data=json.dumps(data))
def update_chat(self, data):
print(f'ws update_chat {data["group_name"]}')
self.send(text_data=json.dumps(data))
def ws_send_msg(self, data):
print('ws ws_send_msg')
self.send(text_data=json.dumps(data))

View File

@@ -1,25 +1,47 @@
from sets.admin import *
from .models import *
from django.contrib import admin
from django.utils.translation import gettext as _
class Admin_StaticPage(Admin_Trans_BaseModelViewPage):
def get_fieldsets(self, request, obj=None):
fieldsets = super(type(self), self).get_fieldsets(request, obj)
if not request.user.is_superuser and obj.url and obj.url in ('main', 'spec_technics', 'works'):
fieldsets[0][1]['fields'].pop(2)
# fieldsets.insert(
# 1, ('Промо-хэдер', {
# 'classes': ['wide'],
# 'fields': (
# 'promo_header',
# 'title', 'description', 'text',
# 'picture',
# )
#
# })
# )
return fieldsets
fieldsets = [
(None, {
'classes': ['wide'],
'fields': ('name',
'url',
'title', 'description', 'text',
'picture',
'order',
)
}),
(_('Настройки'), {
'classes': ['wide', 'collapse'],
'fields': (
'FAQ_title',
)
}),
('SEO', {
'classes': ['wide', 'collapse'],
'fields': (
'seo_title', 'seo_description', 'seo_keywords', 'seo_text',
)
}),
]
list_display = [
'id',
'name', 'url', 'title',
'order', 'modifiedDT', 'createDT'
]
list_display_links = ['id']
list_editable = ['order']
list_filter = ['modifiedDT', 'createDT']
search_fields = ['name', 'title']
# filter_horizontal = ['options']
def has_delete_permission(self, request, obj=None):
if request.user.is_superuser:
@@ -30,46 +52,61 @@ class Admin_StaticPage(Admin_Trans_BaseModelViewPage):
admin.site.register(StaticPage,Admin_StaticPage)
class Admin_Block(Admin_BaseBlock):
# class Admin_Block(Admin_Trans_BaseModel):
# pass
#
# # def get_fieldsets(self, request, obj=None):
# # fieldsets = super(type(self), self).get_fieldsets(request, obj)
# # if not request.user.is_superuser and obj.name and obj.name in ('About US', 'machines', 'works'):
# # fieldsets[0][1]['fields'].pop(0)
# # fieldsets.insert(
# # 1, (_('Контент'), {
# # 'classes': ['wide'],
# # 'fields': (
# # 'title', 'description', 'text',
# # 'picture',
# # )
# #
# # })
# # )
# # return fieldsets
# #
# # def has_delete_permission(self, request, obj=None):
# # if request.user.is_superuser:
# # return True
# #
# # if obj.name in ('About US', 'machines', 'works'):
# # return False
#
# admin.site.register(Block,Admin_Block)
def get_fieldsets(self, request, obj=None):
fieldsets = super(type(self), self).get_fieldsets(request, obj)
if not request.user.is_superuser and obj.name and obj.name in ('About US', 'machines', 'works'):
fieldsets[0][1]['fields'].pop(0)
fieldsets.insert(
1, ('Контент', {
'classes': ['wide'],
'fields': (
'title', 'description', 'text',
'picture',
)
class Admin_Option(Admin_Trans_BaseModel):
})
)
return fieldsets
# def get_fieldsets(self, request, obj=None):
# fieldsets = super(type(self), self).get_fieldsets(request, obj)
# fieldsets.insert(
# 1, ('Контент', {
# 'classes': ['wide'],
# 'fields': (
# 'opt_type', 'prefix', 'value', 'picture'
# )
#
# })
# )
# return fieldsets
def has_delete_permission(self, request, obj=None):
if request.user.is_superuser:
return True
if obj.name in ('About US', 'machines', 'works'):
return False
fieldsets = [
(_('Контент'), {
'classes': ['wide'],
'fields': (
'name', 'opt_type', 'prefix', 'value', 'picture'
)
}),
]
admin.site.register(Block,Admin_Block)
class Admin_Option(Admin_BaseModel):
def get_fieldsets(self, request, obj=None):
fieldsets = super(type(self), self).get_fieldsets(request, obj)
fieldsets.insert(
1, ('Контент', {
'classes': ['wide'],
'fields': (
'opt_type', 'prefix', 'value', 'picture'
)
})
)
return fieldsets
list_display = ['image_thumb', 'opt_type', 'name', 'value', 'prefix']
list_editable = ['value', 'prefix']
list_filter = ['opt_type']
admin.site.register(Option,Admin_Option)

View File

@@ -0,0 +1,51 @@
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
from allauth.account.utils import user_field
from django.conf import settings
from allauth.account.adapter import DefaultAccountAdapter
import requests
from django.core.files import File
from django.core.files.temp import NamedTemporaryFile
class MyAccountAdapter(DefaultAccountAdapter):
def get_login_redirect_url(self, request):
path = super(MyAccountAdapter, self).get_login_redirect_url(request)
try:
user = request.user
user_profile = user.user_profile
if user_profile and not user_profile.avatar:
social_accounts = user.socialaccount_set.all()
if social_accounts:
for social_account in social_accounts:
if 'picture' in social_account.extra_data and social_account.extra_data['picture']:
r = requests.get(social_account.extra_data['picture'])
img_temp = NamedTemporaryFile()
img_temp.write(r.content)
img_temp.flush()
user_profile.avatar.save(f'avatar_{user.id}.jpeg', File(img_temp), True)
break
except Exception as e:
msg = f'post_save create_user_profile Error = {str(e)}'
print(msg)
return path
# class CustomSocialAccountAdapter(DefaultSocialAccountAdapter):
# def populate_user(self, request, sociallogin, data):
# from AuthApp.models import UserProfile
#
# user = super().populate_user(request, sociallogin, data)
# try:
# picture = sociallogin.account.extra_data['picture']
# user_profile = UserProfile.objects.get_or_create(user=user)
# with open(picture, 'rb') as fd:
# user_profile.avatar.save(f'user_{user.id}_avatar.jpeg', fd.read(), True)
# # user_field(user, "profile_photo", picture)
# except Exception as e:
# msg = f'CustomSocialAccountAdapter populate_user Error = {str(e)}'
# print(msg)
#
# return user

45
GeneralApp/funcs.py Normal file
View File

@@ -0,0 +1,45 @@
from django.http import HttpResponse, Http404, FileResponse
from django.conf import settings
def get_inter_Dict(user):
from SubscribesApp.funcs import get_cur_user_subscribe
user_subscribe = get_cur_user_subscribe(user)
Dict = {
'user_subscribe': user_subscribe,
'prod': True
}
if settings.WS_ADDRESS == 'localhost:8000':
Dict.update({'prod': False})
from PushMessages.views import get_key_Dict
Dict.update(get_key_Dict())
return Dict
def get_inter_http_respose(template_obj, context_Dict, request):
context_Dict.update(get_inter_Dict(request.user))
from PushMessages.views import send_push
if request and 'page' in context_Dict:
text = None
title = None
if context_Dict['page'] == dict:
if 'title' in context_Dict['page']:
title = context_Dict['page']['title']
if 'description' in context_Dict['page']:
text = context_Dict['page']['description']
else:
title = getattr(context_Dict['page'], 'title', None)
text = getattr(context_Dict['page'], 'description', None)
# if text and title and not request.user.is_anonymous:
# send_push(user=request.user, title=title, text=text)
return HttpResponse(template_obj.render(context_Dict, request))

View File

@@ -0,0 +1,44 @@
from .models import *
def get_options_by_opt_types(opt_types, only_vals=False):
if type(opt_types) == str:
kwargs = {'opt_type': opt_types}
elif type(opt_types) == dict:
kwargs = opt_types
else:
kwargs = {'opt_type__in': opt_types}
opts = Option.objects.filter(**kwargs)
if opts and only_vals:
res = {}
opts = opts.values('opt_type', 'value', 'prefix')
for item in opts:
if item['opt_type'] == 'domain':
try:
from django.contrib.sites.models import Site
current_site = Site.objects.get_current()
res.update({item['opt_type']: current_site.domain})
continue
except Exception as e:
print(str(e))
if item['prefix']:
res.update({item['opt_type']: f"{item['prefix']}{item['value']}"})
else:
res.update({item['opt_type']: f"{item['value']}"})
return res
return opts
def get_first_option_value_by_opt_type(opt_type):
opts = get_options_by_opt_types(opt_type)
if opts:
return opts[0].value
else:
return None
def get_mail_send_options():
opt_types = ['mail_server_url', 'mail_server_smtp_port', 'sender_mail_login', 'sender_mail_password', 'sender_email', 'project_name']
return get_options_by_opt_types(opt_types, only_vals=True)

View File

@@ -0,0 +1,66 @@
from .models import *
from django.utils.translation import gettext as _
required_options_Dict = {
'mail_server_url': {
'name_ru': 'Адрес почтового сервера',
'opt_type': 'mail_server_url',
'value': '213.142.147.40',
},
'mail_server_smtp_port': {
'name_ru': 'SMTP порт почтового сервера',
'opt_type': 'mail_server_smtp_port',
'value': 587,
},
'sender_mail_login': {
'name_ru': 'email для отправки писем с сайта',
'opt_type': 'sender_mail_login',
'value': 'admin@tripwb.com',
},
'sender_email': {
'name_ru': 'email для отправки',
'opt_type': 'sender_email',
'value': 'admin@tripwb.com',
},
'sender_mail_password': {
'name_ru': 'пароль для отправки писем с сайта',
'opt_type': 'sender_mail_password',
'value': 't5Fdcah^gdajY',
},
'project_name': {
'name_ru': 'Название проекта',
'opt_type': 'project_name',
'value': 'TWB',
},
'domain': {
'name_ru': 'Адрес сайта',
'opt_type': 'domain',
'value': 'tripwb.com',
},
'support_email': {
'name_ru': 'email техподдержки',
'opt_type': 'support_email',
'value': 'admin@tripwb.com',
},
'corp_email': {
'name_ru': 'корпоративный email',
'opt_type': 'corp_email',
'value': 'admin@tripwb.com',
},
}
def init_options():
options = Option.objects.all()
option_types_list = options.values_list('opt_type', flat=True)
opts_for_create = []
for opt_type, data_Dict in required_options_Dict.items():
if not opt_type in option_types_list:
opt = Option(**data_Dict)
opts_for_create.append(opt)
Option.objects.bulk_create(opts_for_create)
return True

View File

@@ -0,0 +1,44 @@
# Generated by Django 4.2.2 on 2023-08-31 13:21
import ckeditor.fields
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('GeneralApp', '0002_block_faq_title_en_block_faq_title_ru_and_more'),
]
operations = [
migrations.AddField(
model_name='staticpage',
name='seo_description_en',
field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)'),
),
migrations.AddField(
model_name='staticpage',
name='seo_description_ru',
field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)'),
),
migrations.AddField(
model_name='staticpage',
name='seo_text_en',
field=ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AddField(
model_name='staticpage',
name='seo_text_ru',
field=ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AddField(
model_name='staticpage',
name='seo_title_en',
field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)'),
),
migrations.AddField(
model_name='staticpage',
name='seo_title_ru',
field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)'),
),
]

View File

@@ -0,0 +1,109 @@
# Generated by Django 4.2.2 on 2023-09-22 13:29
import ckeditor_uploader.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('GeneralApp', '0003_staticpage_seo_description_en_and_more'),
]
operations = [
migrations.AlterField(
model_name='block',
name='description',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='block',
name='description_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='block',
name='description_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='block',
name='seo_text',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='block',
name='text',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Полное описание'),
),
migrations.AlterField(
model_name='block',
name='text_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Полное описание'),
),
migrations.AlterField(
model_name='block',
name='text_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Полное описание'),
),
migrations.AlterField(
model_name='faqitem',
name='answer',
field=ckeditor_uploader.fields.RichTextUploadingField(verbose_name='Ответ'),
),
migrations.AlterField(
model_name='faqitem',
name='answer_en',
field=ckeditor_uploader.fields.RichTextUploadingField(null=True, verbose_name='Ответ'),
),
migrations.AlterField(
model_name='faqitem',
name='answer_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(null=True, verbose_name='Ответ'),
),
migrations.AlterField(
model_name='staticpage',
name='description',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='staticpage',
name='description_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='staticpage',
name='description_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание'),
),
migrations.AlterField(
model_name='staticpage',
name='seo_text',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='staticpage',
name='seo_text_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='staticpage',
name='seo_text_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Текст SEO статьи'),
),
migrations.AlterField(
model_name='staticpage',
name='text',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Полное описание'),
),
migrations.AlterField(
model_name='staticpage',
name='text_en',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Полное описание'),
),
migrations.AlterField(
model_name='staticpage',
name='text_ru',
field=ckeditor_uploader.fields.RichTextUploadingField(blank=True, null=True, verbose_name='Полное описание'),
),
]

View File

@@ -0,0 +1,43 @@
# Generated by Django 4.2.2 on 2023-11-30 13:42
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('GeneralApp', '0004_alter_block_description_alter_block_description_en_and_more'),
]
operations = [
migrations.AddField(
model_name='option',
name='name_en',
field=models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название'),
),
migrations.AddField(
model_name='option',
name='name_ru',
field=models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название'),
),
migrations.AddField(
model_name='option',
name='prefix_en',
field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Префикс'),
),
migrations.AddField(
model_name='option',
name='prefix_ru',
field=models.CharField(blank=True, max_length=250, null=True, verbose_name='Префикс'),
),
migrations.AddField(
model_name='option',
name='value_en',
field=models.CharField(max_length=250, null=True, verbose_name='Значение'),
),
migrations.AddField(
model_name='option',
name='value_ru',
field=models.CharField(max_length=250, null=True, verbose_name='Значение'),
),
]

View File

@@ -1,10 +1,11 @@
from django.db import models
from BaseModels.base_models import BaseModelViewPage, BaseModel
from django.utils.translation import gettext_lazy as _
from ckeditor.fields import RichTextField
# from ckeditor.fields import RichTextField
from ckeditor_uploader.fields import RichTextUploadingField
class StaticPage(BaseModelViewPage):
promo_header = models.BooleanField(verbose_name='Промо-хэдер', default=False)
promo_header = models.BooleanField(verbose_name=_('Промо-хэдер'), default=False)
class Meta:
verbose_name = _('Статическая страница')
@@ -16,9 +17,9 @@ class Block(BaseModelViewPage):
verbose_name_plural = _('Блоки на страницах')
class Option(BaseModel):
opt_type = models.CharField(max_length=250, verbose_name='Тип', blank=True, null=True)
prefix = models.CharField(max_length=250, verbose_name='Префикс', blank=True, null=True)
value = models.CharField(max_length=250, verbose_name='Значение')
opt_type = models.CharField(max_length=250, verbose_name=_('Тип'), blank=True, null=True)
prefix = models.CharField(max_length=250, verbose_name=_('Префикс'), blank=True, null=True)
value = models.CharField(max_length=250, verbose_name=_('Значение'))
picture = models.ImageField(upload_to='uploads/', verbose_name=_('Миниатюра'), null=True, blank=True,
help_text=u'')
@@ -35,8 +36,8 @@ class FAQitem(BaseModel):
object_id = models.PositiveIntegerField()
content_object = GenericForeignKey('content_type', 'object_id')
question = models.TextField(verbose_name='Вопрос')
answer = RichTextField(verbose_name='Ответ')
question = models.TextField(verbose_name=_('Вопрос'))
answer = RichTextUploadingField(verbose_name=_('Ответ'))
def __str__(self):
if self.question:

View File

@@ -0,0 +1 @@
__author__ = 'SDE'

View File

@@ -0,0 +1,160 @@
__author__ = 'SDE'
from django import template
from django.template.defaultfilters import stringfilter
register = template.Library()
from django.core.serializers import serialize
from django.db.models.query import QuerySet
# import simplejson
from django.template import Library
from django.utils.html import mark_safe
@register.filter('get_value_from_dict')
def get_value_from_dict(dict_data, key):
"""
usage example {{ your_dict|get_value_from_dict:your_key }}
"""
if key in dict_data:
res = dict_data[key]
return res
return False
@register.filter()
def get_rows_count_by_cols_count(data, cols_count):
rows_count = len(data) // cols_count
if len(data) % cols_count:
rows_count += 1
return rows_count
@register.filter()
def get_numbers_list(from_el, to_el):
res = range(from_el, to_el+1)
return res
def val_type(value):
res = type(value)
return res.__name__
register.filter('val_type', val_type)
@register.filter()
def get_cols_table_data_for_row_when_cols3(value, row):
el_count = 3
from_el = (row-1) * el_count
to_el = row * el_count
part = list(value)[from_el:to_el]
return part
# register.filter('val_type', val_type)
@register.filter
@stringfilter
def correct_for_tables(value):
if value in ['None', '0.0']:
return '-'
return value
@register.filter
@stringfilter
def del_bad_symbols(value):
from BaseModels.functions import del_bad_symbols
return del_bad_symbols(value)
@register.filter
@stringfilter
def del_amp_symbols(value):
from BaseModels.functions import del_nbsp
return del_nbsp(value)
@register.filter
@stringfilter
def del_lang_from_path(value):
path_list = value.split('/')
path = '/' + '/'.join(path_list[2:])
# for i in path_list[1:]:
# path.join(i + '/')
return path
@register.filter
@stringfilter
def get_color_by_number(value, arg=None):
color = None
try:
val = float(value)
if not color and arg == u'%':
color = u'black'
if val > 50:
color = u'green'
elif val <= 50 and val >= 25:
color = u'#6c8107'
elif val <= 25 and val >= 10:
color = u'#a89803'
elif val <= 10 and val >= 5:
color = u'#e6a707'
elif val <= 5 and val >= 0:
color = u'#e67307'
elif val <= 0:
color = u'red'
# val_range = val_max - val_min
# # val_percent = (val_range * 100 / val) - 100
# offset = -(val_min + -(val))
# if val <0:
# val = offset
# if val > val_max:
# val = val_max
# elif val < 0:
# val = 0
#
# color_range = 16711680 - 1211136
# val_1unit = float(color_range) / float(val_range)
# dec_color = 16711680 - int(val_1unit * val)
if not color:
color = u'black'
if val > 1000:
color = u'green'
elif val <= 1000 and val >= 500:
color = u'#6c8107'
elif val <= 500 and val >= 250:
color = u'#a89803'
elif val <= 250 and val >= 125:
color = u'#e6a707'
elif val <= 125 and val >= 50:
color = u'#e67307'
elif val <= 50:
color = u'red'
# s = u'style="color: #{0}12;"'.format(str(hex(dec_color))[2:6])
s = u'style="color: {0};"'.format(color)
return s
except:
return u''
# @register.filter
# @stringfilter
# def check_aprox_compare_strings(search_phrase, txt):
# from ProductApp.search import get_highlight_string
#
# s = get_highlight_string(search_phrase, txt)
#
# return s

View File

@@ -4,7 +4,7 @@ from .models import *
class StaticPage_TranslationOptions(TranslationOptions):
fields = (
'name', 'description', 'text', 'title', 'FAQ_title'
'name', 'description', 'text', 'title', 'FAQ_title', 'seo_title', 'seo_description', 'seo_text'
)
translator.register(StaticPage, StaticPage_TranslationOptions)
@@ -14,6 +14,12 @@ class Block_TranslationOptions(TranslationOptions):
)
translator.register(Block, Block_TranslationOptions)
class Option_TranslationOptions(TranslationOptions):
fields = (
'name', 'value', 'prefix'
)
translator.register(Option, Option_TranslationOptions)
class FAQitem_TranslationOptions(TranslationOptions):
fields = (

View File

@@ -6,4 +6,6 @@ from .views import *
urlpatterns = [
path('', MainPage, name='main'),
path('page/<str:url>/', StaticPageView, name='static_page'),
path('test_code', test_code, name='test_code'),
]

View File

@@ -5,28 +5,151 @@ from django.template import loader, RequestContext
from django.contrib.auth.decorators import login_required
from .models import *
from django.conf import settings
from .funcs import get_inter_http_respose
from django.http.response import JsonResponse, HttpResponse
from django.views.decorators.http import require_GET, require_POST
from django.shortcuts import get_object_or_404
from django.contrib.auth.models import User
from django.views.decorators.csrf import csrf_exempt
from webpush import send_user_notification
import json
def test_code(request):
from RoutesApp.funcs import get_city_by_type_transport_and_address_point
from RoutesApp.models import Route
from ReferenceDataApp.models import Airport, City
# import allauth
# from allauth.socialaccount.models import SocialApp
# apps = SocialApp.objects.all()
# apps.delete()
from RoutesApp.search_matches import search_matches
search_matches()
# try:
# # body = request.body
# # data = json.loads(body)
# # if 'head' not in data or 'body' not in data or 'id' not in data:
# # return JsonResponse(status=400, data={"message": "Invalid data format"})
# # user_id = data['id']
# user = request.user
# payload = {'head': '123', 'body': 'qwerty'}
# send_user_notification(user=user, payload=payload, ttl=1000)
# return JsonResponse(status=200, data={"message": "Web push successful"})
# except TypeError:
# return JsonResponse(status=500, data={"message": "An error occurred"})
# routes = Route.objects.all()
#
# for route in routes:
# print(route.id)
# required_save = False
# if not route.from_city:
# route.from_city = get_city_by_type_transport_and_address_point(route.type_transport, route.from_address_point)
# required_save = True
#
# if not route.to_city:
# route.to_city = get_city_by_type_transport_and_address_point(route.type_transport,
# route.to_address_point)
# required_save = True
#
# if required_save:
# route.save()
return HttpResponse('finished')
def Page404(request, exeption=None):
Dict = {}
t = loader.get_template('404.html')
try:
res = get_inter_http_respose(t, Dict, request)
return HttpResponse(res, status=404)
except Exception as e:
return HttpResponse(str(e))
def MainPage(request):
from RoutesApp.forms import RouteForm
# from ReferenceDataApp.funcs import parse_data, search_cities_in_db, search_airports_in_db
# res = search_airports_in_db('ang')
# res = parse_data()
from .init_options import init_options
init_options()
print(f'LOCALE_PATHS = {str(settings.LOCALE_PATHS)}')
page = StaticPage.objects.get(url='main')
from ArticlesApp.models import ArticleModel
arts = ArticleModel.objects.filter(enable=True).order_by('-createDT')[:3]
Dict = {
'page': page,
'FAQ': page.FAQ_items.filter(enable=True)
'FAQ': page.FAQ_items.filter(enable=True),
'route_form': RouteForm(),
'articles': arts,
'owner_type': 'mover'
}
breadcrumbs_Dict = {
}
Dict.update({'breadcrumbs': breadcrumbs_Dict})
t = loader.get_template('pages/p_main.html')
return HttpResponse(t.render(Dict, request))
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))
def StaticPageView(request, url):
from RoutesApp.forms import RouteForm
from SubscribesApp.funcs import get_subsribes_w_options
Dict = {}
if url == '':
return MainPage(request)
elif url == 'for_movers':
Dict.update({
'route_form': RouteForm(),
'owner_type': 'customer',
})
elif url == 'for_customers':
Dict.update({
'route_form': RouteForm(),
'owner_type': 'mover'
})
# elif url == 'works':
# return WorksPage(request)
elif url in ['main']:
raise Http404
if url in ['for_movers', 'for_customers']:
subscribes, all_options = get_subsribes_w_options()
Dict.update({
'subscribes': subscribes,
})
try:
page = StaticPage.objects.get(url=url)
except StaticPage.DoesNotExist:
raise Http404
Dict.update({
'page': page,
})
t = loader.get_template('pages/p_static_page.html')
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))

0
PushMessages/__init__.py Normal file
View File

3
PushMessages/admin.py Normal file
View File

@@ -0,0 +1,3 @@
from django.contrib import admin
# Register your models here.

6
PushMessages/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class PushmessagesConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'PushMessages'

View File

3
PushMessages/models.py Normal file
View File

@@ -0,0 +1,3 @@
from django.db import models
# Create your models here.

3
PushMessages/tests.py Normal file
View File

@@ -0,0 +1,3 @@
from django.test import TestCase
# Create your tests here.

16
PushMessages/urls.py Normal file
View File

@@ -0,0 +1,16 @@
# coding=utf-8
from django.urls import path, include
# from AuthApp.js_views import *
# from AuthApp.import_funcs import *
from .views import *
from django.contrib.auth import views
from RoutesApp.js_views import new_route_view_ajax
from django.views.generic import TemplateView
urlpatterns = [
path('send_push', send_push),
path('webpush/', include('webpush.urls')),
path('sw.js', TemplateView.as_view(template_name='sw.js', content_type='application/x-javascript')),
]

49
PushMessages/views.py Normal file
View File

@@ -0,0 +1,49 @@
from django.http.response import JsonResponse, HttpResponse
from django.views.decorators.http import require_GET, require_POST
from django.contrib.auth.models import User
from django.views.decorators.csrf import csrf_exempt
from webpush import send_user_notification
import json
from django.shortcuts import render, get_object_or_404
from django.conf import settings
from django.utils.translation import gettext as _
def get_key_Dict():
webpush_settings = getattr(settings, 'WEBPUSH_SETTINGS', {})
vapid_key = webpush_settings.get('VAPID_PUBLIC_KEY')
Dict = {
'vapid_key': vapid_key
}
return Dict
def send_push(user, title, text, url=None, button_name=None, img=None):
try:
# body = request.body
# data = json.loads(body)
#
# if 'head' not in data or 'body' not in data or 'id' not in data:
# return JsonResponse(status=400, data={"message": "Invalid data format"})
#
# user_id = data['id']
# user = get_object_or_404(User, pk=user_id)
Dict = {
'head': title,
'body': text,
}
if url:
Dict['url'] = url
if button_name:
Dict['button_name'] = button_name
else:
Dict['button_name'] = _('Перейти'),
if img:
Dict['img'] = img
# payload = {'head': data['head'], 'body': data['body']}
send_user_notification(user=user, payload=Dict, ttl=1000)
return JsonResponse(status=200, data={"message": "Web push successful"})
except TypeError:
return JsonResponse(status=500, data={"message": "An error occurred"})

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
celery -A TWB.celery:app worker -l info
celery -A TWB.celery:app beat -l info

View File

@@ -4,6 +4,15 @@ from .models import *
from modeltranslation.admin import TranslationAdmin
class Admin_Country(Admin_Trans_BaseModel):
fieldsets = [
[None, {
'classes': ['wide'],
'fields': [
'name', 'enable', 'short_code', 'code',
]
}]
]
list_display = [
'id', 'name', 'name_en', 'name_ru',
'short_code', 'code',
@@ -13,6 +22,15 @@ class Admin_Country(Admin_Trans_BaseModel):
admin.site.register(Country, Admin_Country)
class Admin_City(Admin_Trans_BaseModel):
fieldsets = [
[None, {
'classes': ['wide'],
'fields': [
'name', 'enable', 'country',
]
}]
]
list_display = [
'id', 'name', 'name_en', 'name_ru',
'country',
@@ -23,6 +41,18 @@ class Admin_City(Admin_Trans_BaseModel):
admin.site.register(City, Admin_City)
class Admin_Airport(Admin_Trans_BaseModel):
fieldsets = [
[None, {
'classes': ['wide'],
'fields': [
'name', 'enable',
'city', 'iata_code', 'icao_code',
'international_name',
# 'area_id'
]
}]
]
list_display = [
'id', 'name', 'name_en', 'name_ru',
'city', 'iata_code', 'icao_code',
@@ -31,4 +61,8 @@ class Admin_Airport(Admin_Trans_BaseModel):
'order', 'modifiedDT', 'createDT']
search_fields = ['id', 'name_en', 'name_ru', 'city__name', 'city__country__name', 'iata_code', 'icao_code', 'international_name']
list_filter = ['city__country']
raw_id_fields = [
'city'
]
admin.site.register(Airport, Admin_Airport)

View File

@@ -13,6 +13,7 @@ from django.template.loader import render_to_string
from django.urls import reverse
from django.db.models import Q
import json
from GeneralApp.funcs import get_inter_http_respose
def get_address_point_ajax(request):
from .funcs import search_cities_in_db, search_airports_in_db
@@ -54,7 +55,7 @@ def get_address_point_ajax(request):
else:
item['city_name'] = item['name']
item['country_name'] = item['country__name']
item['fullname'] = f'{item["country_name"]} / {item["city_name"]}'
item['fullname'] = f'{item["city_name"]} / {item["country_name"]}'
html = f"{html}{render_to_string('widgets/w_ac_input_address_point.html', item)}"
i += 1

View File

@@ -3,21 +3,21 @@ from BaseModels.base_models import BaseModel
from django.utils.translation import gettext_lazy as _
class Country(BaseModel):
international_name = models.CharField(max_length=250, verbose_name='Международное название', blank=True, null=True)
official_name = models.CharField(max_length=250, verbose_name='Официальное название', blank=True, null=True)
international_name = models.CharField(max_length=250, verbose_name=_('Международное название'), blank=True, null=True)
official_name = models.CharField(max_length=250, verbose_name=_('Официальное название'), blank=True, null=True)
short_code = models.CharField(max_length=2, verbose_name='Код страны по ISO3166-1:alpha2')
code = models.CharField(max_length=3, verbose_name='Код страны по ISO3166-1:alpha3')
num_code = models.CharField(max_length=3, verbose_name='Код страны по ISO3166-1:numeric', blank=True, null=True)
short_code = models.CharField(max_length=2, verbose_name=_('Код страны по ISO3166-1:alpha2'))
code = models.CharField(max_length=3, verbose_name=_('Код страны по ISO3166-1:alpha3'))
num_code = models.CharField(max_length=3, verbose_name=_('Код страны по ISO3166-1:numeric'), blank=True, null=True)
flag_img_url = models.URLField(verbose_name='Ссылка на изображение флага', blank=True, null=True)
flag_img_url = models.URLField(verbose_name=_('Ссылка на изображение флага'), blank=True, null=True)
geo_lat = models.CharField(max_length=20, verbose_name='GPS широта', blank=True, null=True)
geo_lon = models.CharField(max_length=20, verbose_name='GPS долгота', blank=True, null=True)
geo_lat = models.CharField(max_length=20, verbose_name=_('GPS широта'), blank=True, null=True)
geo_lon = models.CharField(max_length=20, verbose_name=_('GPS долгота'), blank=True, null=True)
area_id = models.BigIntegerField(blank=True, null=True)
parsing_finished_DT = models.DateTimeField(verbose_name='Дата и время завершения парсинга', blank=True, null=True)
parsing_finished_DT = models.DateTimeField(verbose_name=_('Дата и время завершения парсинга'), blank=True, null=True)
def __str__(self):
if self.name:
@@ -39,14 +39,14 @@ class Country(BaseModel):
class City(BaseModel):
country = models.ForeignKey(
Country, verbose_name='Страна', related_name='rel_cities_for_country', on_delete=models.CASCADE)
Country, verbose_name=_('Страна'), related_name='rel_cities_for_country', on_delete=models.CASCADE)
geo_lat = models.CharField(max_length=20, verbose_name='GPS широта', blank=True, null=True)
geo_lon = models.CharField(max_length=20, verbose_name='GPS долгота', blank=True, null=True)
geo_lat = models.CharField(max_length=20, verbose_name=_('GPS широта'), blank=True, null=True)
geo_lon = models.CharField(max_length=20, verbose_name=_('GPS долгота'), blank=True, null=True)
area_id = models.BigIntegerField(blank=True, null=True)
parsing_finished_DT = models.DateTimeField(verbose_name='Дата и время завершения парсинга', blank=True, null=True)
parsing_finished_DT = models.DateTimeField(verbose_name=_('Дата и время завершения парсинга'), blank=True, null=True)
def __str__(self):
if self.name:
@@ -54,6 +54,14 @@ class City(BaseModel):
else:
return f'{self.id}'
def get_country_n_city_str(self):
country = _('Неизвестно')
city = self.name
if self.country:
country = self.country
return f'{city} / {country}'
class Meta:
verbose_name = _('Город')
@@ -64,20 +72,20 @@ class City(BaseModel):
class Airport(BaseModel):
city = models.ForeignKey(
City, verbose_name='Город', related_name='rel_airports_for_city', on_delete=models.CASCADE,
City, verbose_name=_('Город'), related_name='rel_airports_for_city', on_delete=models.CASCADE,
blank=True, null=True)
international_name = models.CharField(max_length=250, verbose_name='Международное название', blank=True, null=True)
international_name = models.CharField(max_length=250, verbose_name=_('Международное название'), blank=True, null=True)
iata_code = models.CharField(max_length=3, verbose_name='IATA')
icao_code = models.CharField(max_length=4, verbose_name='ICAO')
geo_lat = models.CharField(max_length=20, verbose_name='GPS широта', blank=True, null=True)
geo_lon = models.CharField(max_length=20, verbose_name='GPS долгота', blank=True, null=True)
geo_lat = models.CharField(max_length=20, verbose_name=_('GPS широта'), blank=True, null=True)
geo_lon = models.CharField(max_length=20, verbose_name=_('GPS долгота'), blank=True, null=True)
area_id = models.BigIntegerField(blank=True, null=True)
parsing_finished_DT = models.DateTimeField(verbose_name='Дата и время завершения парсинга', blank=True, null=True)
parsing_finished_DT = models.DateTimeField(verbose_name=_('Дата и время завершения парсинга'), blank=True, null=True)
def __str__(self):
if self.name:

View File

@@ -4,12 +4,16 @@ from django.contrib import admin
class Admin_Route(Admin_Trans_BaseModel):
list_display = [
'id', 'type_transport', 'cargo_type',
'departure_DT', 'from_address_point', 'from_place',
'arrival_DT', 'to_place', 'owner',
'id', 'owner_type', 'receive_msg_by_email', 'type_transport', 'cargo_type',
'departure_DT', 'from_city', 'from_place',
'arrival_DT', 'to_city', 'to_place', 'owner',
'order', 'modifiedDT', 'createDT'
]
list_display_links = ['id']
list_filter = ['owner_type', 'type_transport', 'cargo_type', 'from_place', 'arrival_DT', 'modifiedDT', 'createDT']
search_fields = ['owner__first_name', 'owner__last_name']
raw_id_fields = ['from_city', 'to_city']
admin.site.register(Route,Admin_Route)

View File

@@ -6,6 +6,47 @@ from django.core.exceptions import ValidationError
from .models import *
def routeForm_assign_choices_by_type_transport(form, type_transport):
if type_transport == 'avia':
transfer_location_choices = (
('airport', _('В аэропорту')),
('city', _('По городу')),
('other', _('По договоренности'))
)
cargo_type_choices = (
('cargo', _('Груз')),
('parcel', _('Бандероль')),
('package', _('Посылка')),
('letter', _('Письмо\Документ'))
)
else:
transfer_location_choices = (
('city', _('По городу')),
('other', _('По договоренности'))
)
cargo_type_choices = (
('passenger', _('Пассажир')),
('cargo', _('Груз')),
('parcel', _('Бандероль')),
('package', _('Посылка')),
('letter', _('Письмо\Документ'))
)
form.fields['from_place'].choices = transfer_location_choices
form.fields['to_place'].choices = transfer_location_choices
form.fields['cargo_type'].choices = cargo_type_choices
form.base_fields['from_place'].choices = transfer_location_choices
form.base_fields['to_place'].choices = transfer_location_choices
form.base_fields['cargo_type'].choices = cargo_type_choices
return form
class RouteForm(forms.ModelForm):
from_address_point_txt = forms.CharField(required=True)
to_address_point_txt = forms.CharField(required=True)
@@ -24,6 +65,22 @@ class RouteForm(forms.ModelForm):
try:
if 'phone' in cleaned_data and 'phone' in cleaned_data:
from BaseModels.validators.form_field_validators import get_phone_valid_error
error = get_phone_valid_error(cleaned_data["phone"])
if error:
self.add_error('phone', error)
if 'extra_phone' in cleaned_data and 'extra_phone' in cleaned_data:
from BaseModels.validators.form_field_validators import get_phone_valid_error
error = get_phone_valid_error(cleaned_data["extra_phone"])
if error:
self.add_error('extra_phone', error)
if 'departure_DT' in cleaned_data and 'arrival_DT' in cleaned_data and cleaned_data['arrival_DT'] < cleaned_data['departure_DT']:
self.add_error('arrival_DT', _('Указана неверная дата прибытия'))
if 'from_place' in self.data and self.data['from_place'] not in [item[0] for item in self.fields['from_place'].choices]:
cleaned_data['from_place'] = self.fields['from_place'].choices[0][0]
if 'from_place' in self.errors and self.errors['from_place'].data[0].code == 'invalid_choice':

View File

@@ -1,5 +1,144 @@
from .models import *
def get_routes_Dict(user=None):
from .forms import *
from django.utils.translation import gettext as _
from django.template.loader import render_to_string
from datetime import datetime
elements_on_page = 25
def get_profile_new_route_page_html(request, data):
form = RouteForm()
Dict = {
'form': form
}
try:
errors_off = True
if 'from_address_point' in data:
del data['from_address_point']
if 'from_address_point_txt' in data:
del data['from_address_point_txt']
if 'to_address_point' in data:
del data['to_address_point']
if 'to_address_point_txt' in data:
del data['to_address_point_txt']
# if data['type_transport'] == 'avia':
# transfer_location_choices = (
# ('airport', _('В аэропорту')),
# ('city', _('По городу')),
# ('other', _('По договоренности'))
# )
#
# cargo_type_choices = (
# ('cargo', _('Груз')),
# ('parcel', _('Бандероль')),
# ('package', _('Посылка')),
# ('letter', _('Письмо\Документ'))
# )
#
#
# else:
# transfer_location_choices = (
# ('city', _('По городу')),
# ('other', _('По договоренности'))
# )
#
# cargo_type_choices = (
# ('passenger', _('Пассажир')),
# ('cargo', _('Груз')),
# ('parcel', _('Бандероль')),
# ('package', _('Посылка')),
# ('letter', _('Письмо\Документ'))
# )
form = RouteForm(data)
if not form.is_valid():
pass
form = RouteForm(initial=form.cleaned_data)
if 'type_transport' in data:
form = routeForm_assign_choices_by_type_transport(form, data['type_transport'])
# form.fields['from_place'].choices = transfer_location_choices
# form.fields['to_place'].choices = transfer_location_choices
# form.fields['cargo_type'].choices = cargo_type_choices
#
# form.base_fields['from_place'].choices = transfer_location_choices
# form.base_fields['to_place'].choices = transfer_location_choices
# form.base_fields['cargo_type'].choices = cargo_type_choices
# form = CreateRouteForm(initial=data)
# if not form.is_valid():
# pass
if 'owner_type' in data:
form.initial['owner_type'] = data['owner_type']
if request.user and request.user.is_authenticated and request.user.user_profile and request.user.user_profile.phone:
form.initial.update({'phone': request.user.user_profile.phone})
Dict = {
'form': form,
'errors_off': errors_off
}
if 'owner_type' in data:
Dict.update({'owner_type': data['owner_type']})
# print(form)
except Exception as e:
# form.errors.append({'__all__': f'Ошибка: {str(e)}'})
print(str(e))
html = render_to_string('blocks/profile/b_new_route.html', Dict, request=request)
return html
def get_country_n_city_str_by_type_transport_and_address_point(type_transport, address_point):
city = get_city_by_type_transport_and_address_point(type_transport, address_point)
return city.get_country_n_city_str()
def get_city_by_type_transport_and_address_point(type_transport, address_point):
from ReferenceDataApp.models import Airport, City
try:
if type_transport == 'avia':
return Airport.objects.get(id=address_point).city
else:
return City.objects.get(id=address_point)
except Exception as e:
msg = f'get_city_by_type_transport_and_address_point Error = {str(e)}, type_transport = {type_transport}, address_point = {address_point}'
print(msg)
return None
def get_profile_my_routes_page_content_html(request):
routes_Dict = get_routes_Dict(request.user)
if 'errors' in routes_Dict:
msg = f'get_my_routes_page_content_html errors = {str(routes_Dict)}'
print(msg)
return msg
html = render_to_string('blocks/profile/b_my_routes.html', routes_Dict, request=request)
return html
def get_routes_Dict(user=None, data=None):
from ReferenceDataApp.models import Airport, Country, City
# if not user and user.is_authenticated:
@@ -15,10 +154,97 @@ def get_routes_Dict(user=None):
'owner': user
})
routes = Route.objects.filter(**kwargs).order_by('-modifiedDT')
from_el = None
to_el = None
res_Dict = {}
if data:
type_transport = None
if 'type_transport' in data and data['type_transport']:
items_list = data['type_transport'].split(',')
kwargs.update({f'type_transport__in': items_list})
if len(items_list) == 1:
type_transport = items_list[0]
for key, val in data.items():
if val:
if key == 'weight':
weight_list = val.split(';')
if len(weight_list) > 1:
if weight_list[0]:
kwargs.update({f'{key}__gte': int(weight_list[0])})
if weight_list[1]:
kwargs.update({f'{key}__lte': int(weight_list[1])})
else:
kwargs.update({f'{key}__lte': int(weight_list[0])})
# if key == 'type_transport':
# items_list = val.split(',')
# kwargs.update({f'{key}__in': items_list})
if key in (
'departure_DT', 'arrival_DT'
):
dates = val.split(' - ')
kwargs.update({
f'{key}__date__gte': datetime.strptime(dates[0], '%d.%m.%Y'),
f'{key}__date__lte': datetime.strptime(dates[1], '%d.%m.%Y').replace(hour=23, minute=59, second=59)
})
if key not in (
'from_address_point_txt', 'to_address_point_txt', 'csrfmiddlewaretoken', 'sort', 'weight',
'from_el', 'to_el', 'from_address_point', 'to_address_point', 'type_transport',
'departure_DT', 'arrival_DT'
):
kwargs.update({key: val})
if key == 'from_address_point':
city = get_city_by_type_transport_and_address_point(type_transport, val)
kwargs.update({f'from_city': city})
res_Dict.update({
'from_address_point_txt': city.get_country_n_city_str()
})
if key == 'to_address_point':
city = get_city_by_type_transport_and_address_point(type_transport, val)
kwargs.update({f'to_city': city})
res_Dict.update({
'to_address_point_txt': city.get_country_n_city_str()
})
if key == 'from_el':
from_el = int(val)
if key == 'to_el':
to_el = int(val)
routes = Route.objects.filter(**kwargs).order_by('-departure_DT', '-arrival_DT', '-modifiedDT')
routes_count = routes.count()
if from_el and to_el:
routes = routes[from_el:to_el]
elif from_el:
routes = routes[from_el:]
elif to_el:
routes = routes[:to_el]
else:
to_el = elements_on_page
routes = routes[:to_el]
last_block = False
if not to_el or to_el >= routes_count:
last_block = True
if routes_count - to_el > elements_on_page:
next_page_els_count = elements_on_page
else:
next_page_els_count = routes_count - to_el
try:
for route in routes:
@@ -40,9 +266,12 @@ def get_routes_Dict(user=None):
msg = f'get_routes_for_user get route Error = {str(e)}'
print(msg)
res_Dict = {
'routes': routes
}
res_Dict.update({
'routes': routes,
'last_block': last_block,
'last_el': to_el,
'next_page_els_count': next_page_els_count
})
return res_Dict

View File

@@ -9,6 +9,9 @@ urlpatterns = [
path('create_or_change_route/', create_or_change_route_ajax, name='create_or_change_route_ajax'),
path('edit_route/', edit_route_ajax, name='edit_route_ajax'),
path('del_route/', del_route_ajax, name='del_route_ajax'),
path('get_routes/', get_my_routes_ajax, name='get_my_routes_ajax'),
path('find_routes/', find_routes_ajax, name='find_routes_ajax'),
path('get_routes/', get_routes_ajax, name='get_routes_ajax'),
]

View File

@@ -14,7 +14,40 @@ from datetime import datetime
from django.template.loader import render_to_string
from django.urls import reverse
from .forms import *
from .funcs import get_routes_Dict
from .funcs import *
def del_route_ajax(request):
if request.method != 'POST':
raise Http404
try:
data = json.loads(request.body)
if not 'route_id' in data:
msg = f'Недостаточно данных'
return JsonResponse({'errors': msg})
route = Route.objects.filter(id=data['route_id']).delete()
routes_Dict = get_routes_Dict(request.user)
if 'errors' in routes_Dict:
return JsonResponse(routes_Dict, status=400)
html = render_to_string('blocks/profile/b_my_routes.html', routes_Dict, request=request)
res_Dict = {
'html': html
}
return JsonResponse(res_Dict)
except Exception as e:
# form.errors.append({'__all__': f'Ошибка: {str(e)}'})
msg = f'Ошибка del_route_ajax = {str(e)}'
print(msg)
return JsonResponse({'errors': msg})
def edit_route_ajax(request):
@@ -34,6 +67,8 @@ def edit_route_ajax(request):
route = Route.objects.get(id=data['route_id'])
form = RouteForm(instance=route)
form = routeForm_assign_choices_by_type_transport(form, route.type_transport)
route_address_points_Dict = route.get_address_points()
form.initial.update({
'from_address_point_txt': route_address_points_Dict['from_address_point_txt'],
@@ -57,14 +92,17 @@ def edit_route_ajax(request):
def new_route_view_ajax(request):
if request.method != 'POST':
raise Http404
form = RouteForm()
Dict = {
'form': form
}
# form = RouteForm()
# Dict = {
# 'form': form
# }
try:
errors_off = True
@@ -74,79 +112,64 @@ def new_route_view_ajax(request):
data = json.loads(request.body)
# show_errors = False
if 'from_address_point' in data:
del data['from_address_point']
if 'from_address_point_txt' in data:
del data['from_address_point_txt']
if 'to_address_point' in data:
del data['to_address_point']
if 'to_address_point_txt' in data:
del data['to_address_point_txt']
if 'type_transport' in data:
if data['type_transport'] == 'avia':
transfer_location_choices = (
('airport', _('В аэропорту')),
('city', _('По городу')),
('other', _('По договоренности'))
)
cargo_type_choices = (
('passenger', _('Пассажир')),
('cargo', _('Груз')),
('parcel', _('Бандероль')),
('package', _('Посылка')),
('letter', _('Письмо\Документ'))
)
html = get_profile_new_route_page_html(request, data)
else:
transfer_location_choices = (
('city', _('По городу')),
('other', _('По договоренности'))
)
cargo_type_choices = (
('cargo', _('Груз')),
('parcel', _('Бандероль')),
('package', _('Посылка')),
('letter', _('Письмо\Документ'))
)
form = RouteForm(data)
if not form.is_valid():
pass
form = RouteForm(initial=form.cleaned_data)
form.fields['from_place'].choices = transfer_location_choices
form.fields['to_place'].choices = transfer_location_choices
form.fields['cargo_type'].choices = cargo_type_choices
form.base_fields['from_place'].choices = transfer_location_choices
form.base_fields['to_place'].choices = transfer_location_choices
form.base_fields['cargo_type'].choices = cargo_type_choices
# form = CreateRouteForm(initial=data)
# if not form.is_valid():
# pass
Dict = {
'form': form,
'errors_off': errors_off
}
# print(form)
except Exception as e:
# form.errors.append({'__all__': f'Ошибка: {str(e)}'})
print(str(e))
msg = f'new_route_view_ajax Error = {str(e)}'
print(msg)
html = msg
html = render_to_string('blocks/profile/b_new_route.html', Dict, request=request)
# html = render_to_string('blocks/profile/b_new_route.html', Dict, request=request)
return JsonResponse({'html': html}, status=200)
def get_routes_ajax(request):
def find_routes_ajax(request):
if request.method != 'POST':
raise Http404
try:
data = request.POST.dict()
if not data and request.body:
data = json.loads(request.body)
if not 'owner_type' in data:
data['owner_type'] = 'mover'
routes_Dict = get_routes_Dict(data=data)
if 'errors' in routes_Dict:
return JsonResponse(routes_Dict, status=400)
if routes_Dict['routes']:
html = render_to_string('blocks/b_search_routes.html', routes_Dict, request=request)
else:
html = render_to_string('templates_js_translate/not_found_find_routes.html', routes_Dict, request=request)
res_Dict = {
'html': html,
'last_block': routes_Dict['last_block'],
'next_page_els_count': routes_Dict['next_page_els_count'],
# 'form': RouteForm(initial=data)
}
return JsonResponse(res_Dict)
except Exception as e:
errors_Dict = {
'errors': {
'all__': f'ошибка в запросе = {str(e)}'
}
}
return JsonResponse(errors_Dict, status=400)
def get_my_routes_ajax(request):
if request.method != 'POST':
@@ -178,6 +201,7 @@ def get_routes_ajax(request):
def create_or_change_route_ajax(request, route_id=None):
from ReferenceDataApp.models import Airport, City
if request.method != 'POST':
raise Http404
@@ -187,6 +211,8 @@ def create_or_change_route_ajax(request, route_id=None):
try:
data = request.POST
if not data:
data = json.loads(request.body)
route = None
if route_id:
@@ -206,9 +232,20 @@ def create_or_change_route_ajax(request, route_id=None):
return JsonResponse({'html': html}, status=400)
obj = form.save(commit=False)
if 'owner_type' in data and data['owner_type']:
obj.owner_type = data['owner_type']
if obj.from_address_point:
obj.from_city = get_city_by_type_transport_and_address_point(obj.type_transport, obj.from_address_point)
if obj.to_address_point:
obj.to_city = get_city_by_type_transport_and_address_point(obj.type_transport, obj.to_address_point)
obj.owner = request.user
obj.save()
route_id = obj.id
routes_Dict = get_routes_Dict(request.user)
if 'errors' in routes_Dict:
@@ -220,7 +257,8 @@ def create_or_change_route_ajax(request, route_id=None):
html = render_to_string('blocks/profile/b_my_routes.html', routes_Dict, request=request)
res_Dict = {
'html': html
'html': html,
'route_id': route_id
}
return JsonResponse(res_Dict)

View File

View File

@@ -0,0 +1,36 @@
from django.core.management.base import BaseCommand
from datetime import datetime
from BaseModels.mailSender import techSendMail
from GeneralApp.funcs_options import get_options_by_opt_types, get_mail_send_options
class Command(BaseCommand):
def handle(self, *args, **options):
mail_sets = get_mail_send_options()
log = ''
log_begin_DT = datetime.now()
msg = str(log_begin_DT)
print('-------------')
print(msg)
try:
from ...search_matches import search_matches
msg = search_matches()
if msg:
print(msg)
except Exception as e:
msg = f'every_1hour_start search_matches fail = {str(e)}'
print(msg)
techSendMail(mail_sets, msg, title='every_1hour_start search_matches')
if msg:
techSendMail(mail_sets, str(msg), title='every_1hour_start get_competitors_prices')
print(f'- processing time = {str(datetime.now() - log_begin_DT)} -')

View File

@@ -0,0 +1,13 @@
from django.core.management.base import BaseCommand
from datetime import datetime
class Command(BaseCommand):
def handle(self, *args, **options):
# from B2BApp.funcs import assign_contracts_for_orders_by_tmp_data
# log = assign_contracts_for_orders_by_tmp_data()
from GeneralApp.views import test_code
test_code(None)
self.stdout.write(u'fix_code end')

View File

@@ -0,0 +1,25 @@
# Generated by Django 4.2.2 on 2023-08-29 18:02
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('ReferenceDataApp', '0005_remove_airport_parsing_finished_and_more'),
('RoutesApp', '0004_alter_route_type_transport'),
]
operations = [
migrations.AddField(
model_name='route',
name='from_city',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rel_routes_for_cityFrom', to='ReferenceDataApp.city', verbose_name='Город отправки'),
),
migrations.AddField(
model_name='route',
name='to_city',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rel_routes_for_cityTo', to='ReferenceDataApp.city', verbose_name='Город получения'),
),
]

View File

@@ -24,13 +24,14 @@ cargo_type_choices = (
)
owner_type_choices = (
('customer', 'Заказчик'),
('mover', 'Перевозчик')
('customer', _('Заказчик')),
('mover', _('Перевозчик'))
)
class Route(BaseModel):
from django.contrib.auth.models import User
from ReferenceDataApp.models import City
owner_type = models.CharField(
choices=owner_type_choices, default='customer', verbose_name=_('Тип опреации'))
@@ -41,8 +42,14 @@ class Route(BaseModel):
arrival_DT = models.DateTimeField(default=True, verbose_name=_('Дата и время прибытия'))
from_address_point = models.IntegerField(verbose_name=_('Пункт выезда'))
to_address_point = models.IntegerField(verbose_name=_('Пункт приезда'))
# from_city = forms.CharField(required=True)
# to_city = forms.CharField(required=True)
from_city = models.ForeignKey(
City, verbose_name=_('Город отправки'), related_name='rel_routes_for_cityFrom', on_delete=models.SET_NULL,
null=True, blank=True
)
to_city = models.ForeignKey(
City, verbose_name=_('Город получения'), related_name='rel_routes_for_cityTo', on_delete=models.SET_NULL,
null=True, blank=True
)
from_place = models.CharField(choices=transfer_location_choices, default='other',
verbose_name=_('Откуда можете забрать?'))
to_place = models.CharField(choices=transfer_location_choices, default='other',
@@ -67,6 +74,21 @@ class Route(BaseModel):
verbose_name_plural = _(u'Маршруты')
ordering = ('name',)
def from_country_n_city_str(self):
res = _('Неизвестно')
if self.from_city:
res = self.from_city.get_country_n_city_str()
return res
def to_country_n_city_str(self):
res = _('Неизвестно')
if self.to_city:
res = self.to_city.get_country_n_city_str()
return res
def get_address_points(self):
from ReferenceDataApp.models import Airport, City
@@ -89,8 +111,8 @@ class Route(BaseModel):
to_address_point_Dict = to_address_point_objs.values(
'id', 'name', 'country__name')[0]
from_address_point_txt = f'{from_address_point_Dict["country__name"]} / {from_address_point_Dict["name"]}'
to_address_point_txt = f'{to_address_point_Dict["country__name"]} / {to_address_point_Dict["name"]}'
from_address_point_txt = f'{from_address_point_Dict["name"]} / {from_address_point_Dict["country__name"]}'
to_address_point_txt = f'{to_address_point_Dict["name"]} / {to_address_point_Dict["country__name"]}'
return {
'from_address_point_obj': from_address_point_objs[0],

145
RoutesApp/search_matches.py Normal file
View File

@@ -0,0 +1,145 @@
from .models import *
from datetime import datetime, timedelta
from django.utils.translation import gettext as _
from django.template.loader import render_to_string
from GeneralApp.funcs_options import get_options_by_opt_types, get_mail_send_options
from BaseModels.mailSender import admin_send_mail_by_SMTPlib, techSendMail
def get_Dict_for_send_msgs(kwargs, search_owner_type):
print('get_Dict_for_send_msgs')
Dict = {
'search_owner_type': search_owner_type
}
sets = get_options_by_opt_types(['domain', 'project_name'], only_vals=True)
Dict.update(sets)
Dict.update({'logo': f'{sets["domain"]}/static/img/svg/LogoMobile.svg', })
find_routes_page_url = f'{sets["domain"]}/routes/route_search_results/?'
kwargs_list = [f'{key}={value}' for key, value in kwargs.items()]
kwargs_list.append(f'owner_type={search_owner_type}')
find_routes_page_url += f'{"&".join(kwargs_list)}'
Dict.update({'find_routes_page_url': find_routes_page_url})
return Dict
def send_push_message_for_found_matches_routes(route, data_Dict):
print(f'send_push_message_for_found_matches_routes to route id = {route.id}')
if not route.owner.is_authenticated:
return None
from PushMessages.views import send_push
title = 'Мы нашли исполнителя по Вашему объявлению!'
text = 'Для просмотра результата нажмите на кнопку ниже'
send_push(route.owner, title, text, url=data_Dict['find_routes_page_url'], button_name=_('Перейти к найденному'))
return None
def send_mail_found_matches_routes(route, data_Dict):
print(f'send_mail_found_matches_routes to route id = {route.id}')
Dict = {
'route': route,
}
Dict.update(data_Dict)
html = render_to_string('mail/m_found_matched_routes.html', Dict)
mail_sets = get_mail_send_options()
to = [route.owner.email, 'web@syncsystems.net']
subject = _('Мы нашли исполнителя по Вашему объявлению!')
res = admin_send_mail_by_SMTPlib(
mail_sets,
subject=subject,
from_email=mail_sets['sender_email'], to=to,
html_content=html
)
return res
def search_matches(for_routes=None):
print('search_matches')
log = ''
try:
if not for_routes:
for_routes = Route.objects.filter(
createDT__gte=datetime.now() - timedelta(hours=1),
receive_msg_by_email=True
)
check_fields = [
'type_transport', 'departure_DT', 'arrival_DT', 'from_address_point', 'to_address_point',
'from_place', 'to_place', 'cargo_type', 'weight'
]
if for_routes:
msg = f'last hour create routes count = {for_routes.count()}'
else:
msg = f'last hour not create routes'
print(msg)
for route in for_routes:
kwargs = {}
params = {}
for field_name in check_fields:
field_val = getattr(route, field_name, None)
if field_val:
if type(field_val) == datetime:
params.update({f"{field_name}": f'{field_val.strftime("%d.%m.%Y")} - {field_val.strftime("%d.%m.%Y")}'})
kwargs.update({f"{field_name}__date": field_val.date()})
elif field_name == 'weight':
# print(field_name)
params.update({f"{field_name}": field_val})
if route.owner_type == 'mover':
kwargs.update({f"{field_name}__lte": field_val})
else:
kwargs.update({f"{field_name}__gte": field_val})
else:
kwargs.update({field_name: field_val})
params.update({field_name: field_val})
found_routes = Route.objects.exclude(
id=route.id,
).exclude(
owner=route.owner
).exclude(
owner_type=route.owner_type
).filter(
**kwargs
# ).count(
)
if found_routes:
msg = f'found routes for send messages = {found_routes.count()}'
data_Dict = get_Dict_for_send_msgs(params, found_routes[0].owner_type)
msg = send_push_message_for_found_matches_routes(route, data_Dict)
if msg:
log += msg
msg = send_mail_found_matches_routes(route, data_Dict)
if msg:
log += msg
except Exception as e:
msg = f'<br>\n! search_matches Error = {str(e)}'
print(msg)
log += msg
mail_sets = get_mail_send_options()
techSendMail(mail_sets, log, title='search_matches fail')
return log

View File

@@ -10,16 +10,51 @@ from BaseModels.mailSender import techSendMail
from django.utils.translation import gettext as _
from datetime import datetime
from .funcs import *
from .forms import *
from GeneralApp.funcs import get_inter_http_respose
def route_search_results_View(request):
Dict = {}
data = None
routes = get_routes_Dict()
if routes:
if request.GET:
data = request.GET.dict()
routes_Dict = get_routes_Dict(data=data)
if routes_Dict:
Dict = {
'routes': routes['routes']
'routes': routes_Dict['routes'],
'last_block': routes_Dict['last_block'],
'show_filter_and_results': True,
'owner_type': data['owner_type'],
'last_el': routes_Dict['last_el'],
'page_type': 'routes',
'next_page_els_count': routes_Dict['next_page_els_count'],
}
if 'from_address_point_txt' in routes_Dict:
data.update({'from_address_point_txt': routes_Dict['from_address_point_txt']})
if 'to_address_point_txt' in routes_Dict:
data.update({'to_address_point_txt': routes_Dict['to_address_point_txt']})
Dict.update({'route_form': RouteForm(initial=data)})
title = _('Результат поиска маршрутов')
if 'from_address_point_txt' in data:
title = f'{title} из {data["from_address_point_txt"]}'
if 'to_address_point_txt' in data:
title = f'{title} в {data["to_address_point_txt"]}'
Dict.update({
'page': {
'title': title,
'description': title,
'keywords': title,
}
})
t = loader.get_template('pages/p_results_find_route.html')
return HttpResponse(t.render(Dict, request))
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))

View File

93
SubscribesApp/admin.py Normal file
View File

@@ -0,0 +1,93 @@
from sets.admin import *
from .models import *
from django.contrib import admin
class Admin_Subscribe(Admin_Trans_BaseModel):
fieldsets = (
(None, {
'classes': ['wide'],
'fields': ('name', 'enable',
('price'),
'options',
'period_name', 'period',
'order',
'bg_color', 'text_color'
)
}),
)
list_display = [
'id',
'name', 'enable', 'price',
'period_name', 'period',
'order', 'modifiedDT', 'createDT'
]
list_display_links = ['id']
list_editable = ['enable']
list_filter = ['modifiedDT', 'createDT']
search_fields = ['name', 'period_name']
filter_horizontal = ['options']
admin.site.register(Subscribe,Admin_Subscribe)
class Admin_SubscribeOption(Admin_Trans_BaseModel):
fieldsets = (
(None, {
'classes': ['wide'],
'fields': ('name',
'order',
'enable'
)
}),
)
list_display = [
'id', 'enable',
'name',
'order', 'modifiedDT', 'createDT'
]
list_editable = ['enable']
list_display_links = ['id']
list_filter = ['modifiedDT', 'createDT']
search_fields = ['name']
admin.site.register(SubscribeOption,Admin_SubscribeOption)
class Admin_SubscribeForUser(Admin_Trans_BaseModel):
fieldsets = (
(None, {
'classes': ['wide'],
'fields': ('name',
'user', 'subscribe',
'last_paid_DT',
'paid_period_from_DT', 'paid_period_to_DT',
'auto_continue', 'receive_finish_subscribe_msg',
'order'
)
}),
)
list_display = [
'id',
'name', 'user', 'subscribe',
'last_paid_DT', 'paid_period_from_DT', 'paid_period_to_DT',
'auto_continue', 'receive_finish_subscribe_msg',
'order', 'modifiedDT', 'createDT'
]
list_display_links = ['id']
list_filter = [
'subscribe', 'last_paid_DT', 'paid_period_from_DT', 'paid_period_to_DT',
'auto_continue', 'receive_finish_subscribe_msg',
'modifiedDT', 'createDT'
]
search_fields = ['name']
admin.site.register(SubscribeForUser,Admin_SubscribeForUser)

6
SubscribesApp/apps.py Normal file
View File

@@ -0,0 +1,6 @@
from django.apps import AppConfig
class SubscribesappConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'SubscribesApp'

57
SubscribesApp/funcs.py Normal file
View File

@@ -0,0 +1,57 @@
from .models import *
from django.template.loader import render_to_string
def get_cur_user_subscribe(user):
user_subscribe = None
try:
user_subscribe = SubscribeForUser.objects.get(user=user)
except Exception as e:
pass
return user_subscribe
def get_subsribes_w_options():
all_options = SubscribeOption.objects.filter(enable=True)
subscribes = Subscribe.objects.filter(enable=True)
for subscribe in subscribes:
subscribe_options_ids = subscribe.options.values_list('id', flat=True)
subscribe.disabled_options = all_options.exclude(id__in=subscribe_options_ids)
return subscribes, all_options
def get_profile_subscribe_page_content_html(request):
try:
# data = json.loads(request.body)
# all_options = SubscribeOption.objects.filter(enable=True)
subscribes, all_options = get_subsribes_w_options()
subscribe_for_user = SubscribeForUser.objects.filter(user=request.user)
if not subscribe_for_user:
tpl_name = 'blocks/profile/b_subscribe_variants.html'
else:
tpl_name = 'blocks/profile/b_subscribe_current.html'
subscribe_for_user = subscribe_for_user[0]
subscribe_options_ids = subscribe_for_user.subscribe.options.values_list('id', flat=True)
subscribe_for_user.subscribe.disabled_options = all_options.exclude(id__in=subscribe_options_ids)
# subscribes = Subscribe.objects.filter(enable=True)
# for subscribe in subscribes:
# subscribe_options_ids = subscribe.options.values_list('id', flat=True)
# subscribe.disabled_options = all_options.exclude(id__in=subscribe_options_ids)
Dict = {
'subscribe_for_user': subscribe_for_user,
'subscribes': subscribes
}
html = render_to_string(tpl_name, Dict, request=request)
return html
except Exception as e:
msg = f'show_cur_subscribe_ajax Error = {str(e)}'
return msg

17
SubscribesApp/js_urls.py Normal file
View File

@@ -0,0 +1,17 @@
# coding=utf-8
from django.urls import path
# from AuthApp.js_views import *
# from AuthApp.import_funcs import *
from .js_views import *
from django.contrib.auth import views
from RoutesApp.js_views import new_route_view_ajax
urlpatterns = [
path('show_cur_subscribe/', show_cur_subscribe_ajax, name='show_cur_subscribe_ajax'),
path('subscribe_now/', subscribe_now_ajax, name='subscribe_now_ajax'),
# path('create_ticket/', create_ticket_ajax, name='create_ticket_ajax'),
# path('support_show_chat_by_ticket/', support_show_chat_by_ticket_ajax, name='support_show_chat_by_ticket_ajax'),
# # path('send_msg/', send_msg_ajax, name='send_msg_ajax'),
# # path('update_chat/', update_chat_ajax2, name='update_chat_ajax'),
# path('show_chat_w_user/', show_chat_w_user_ajax, name='show_chat_w_user_ajax'),
]

112
SubscribesApp/js_views.py Normal file
View File

@@ -0,0 +1,112 @@
from django.shortcuts import render
from uuid import uuid1
from .models import *
from django.contrib import auth
from django.http import HttpResponse, Http404, JsonResponse
from django.template import loader, RequestContext
from django.contrib.auth.decorators import login_required
from BaseModels.mailSender import techSendMail
from django.utils.translation import gettext as _
from datetime import datetime
from django.template.loader import render_to_string
from django.urls import reverse
from .funcs import *
import json
from datetime import datetime, time, timedelta
from channels.layers import get_channel_layer
from asgiref.sync import async_to_sync
@login_required(login_url='/profile/login/')
def subscribe_now_ajax(request):
if request.method != 'POST':
raise Http404
try:
data = json.loads(request.body)
subscribe = Subscribe.objects.get(id=data['subscribe_id'])
kwargs = {
'user': request.user,
'subscribe': subscribe,
'last_paid_DT': datetime.now(),
'paid_period_from_DT': datetime.now(),
'paid_period_to_DT': datetime.now() + timedelta(hours=subscribe.period),
'receive_finish_subscribe_msg': True,
}
subscribe_for_user = SubscribeForUser.objects.filter(user=request.user)
if subscribe_for_user:
subscribe_for_user.update(**kwargs)
subscribe_for_user = subscribe_for_user[0]
else:
subscribe_for_user = SubscribeForUser.objects.create(**kwargs)
if not subscribe_for_user:
tpl_name = 'blocks/profile/b_subscribe_variants.html'
else:
tpl_name = 'blocks/profile/b_subscribe_current.html'
all_options = SubscribeOption.objects.filter(enable=True)
subscribes = Subscribe.objects.filter(enable=True)
for subscribe in subscribes:
subscribe_options_ids = subscribe.options.values_list('id', flat=True)
subscribe.disabled_options = all_options.exclude(id__in=subscribe_options_ids)
Dict = {
'subscribe_for_user': subscribe_for_user,
'subscribes': subscribes
}
html = render_to_string(tpl_name, Dict, request=request)
return JsonResponse({'html': html}, status=200)
except Exception as e:
msg = f'show_cur_subscribe_ajax Error = {str(e)}'
return JsonResponse({'error': msg}, status=400)
@login_required(login_url='/profile/login/')
def show_cur_subscribe_ajax(request):
if request.method != 'POST':
raise Http404
html = get_profile_subscribe_page_content_html(request)
return JsonResponse({'html': html}, status=200)
# try:
#
# # data = json.loads(request.body)
# all_options = SubscribeOption.objects.filter(enable=True)
#
# subscribe_for_user = SubscribeForUser.objects.filter(user=request.user)
# if not subscribe_for_user:
# tpl_name = 'blocks/profile/b_subscribe_variants.html'
# else:
# tpl_name = 'blocks/profile/b_subscribe_current.html'
# subscribe_for_user = subscribe_for_user[0]
# subscribe_options_ids = subscribe_for_user.subscribe.options.values_list('id', flat=True)
# subscribe_for_user.subscribe.disabled_options = all_options.exclude(id__in=subscribe_options_ids)
#
# subscribes = Subscribe.objects.filter(enable=True)
# for subscribe in subscribes:
# subscribe_options_ids = subscribe.options.values_list('id', flat=True)
# subscribe.disabled_options = all_options.exclude(id__in=subscribe_options_ids)
#
# Dict = {
# 'subscribe_for_user': subscribe_for_user,
# 'subscribes': subscribes
# }
#
# html = render_to_string(tpl_name, Dict, request=request)
# return JsonResponse({'html': html}, status=200)
#
# except Exception as e:
# msg = f'show_cur_subscribe_ajax Error = {str(e)}'
# return JsonResponse({'error': msg}, status=400)

View File

@@ -0,0 +1,89 @@
# Generated by Django 4.2.2 on 2023-08-29 16:04
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Subscribe',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_ru', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_en', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
('price', models.FloatField(default=0, verbose_name='Стоимость')),
('period_name', models.CharField(max_length=250, verbose_name='Название периода')),
('period_name_ru', models.CharField(max_length=250, null=True, verbose_name='Название периода')),
('period_name_en', models.CharField(max_length=250, null=True, verbose_name='Название периода')),
('period', models.IntegerField(default=0, verbose_name='Длительность подписки в часах')),
],
options={
'verbose_name': 'Подписка',
'verbose_name_plural': 'Подписки',
},
),
migrations.CreateModel(
name='SubscribeOption',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_ru', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_en', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
],
options={
'verbose_name': 'Опция подписки',
'verbose_name_plural': 'Опции подписки',
},
),
migrations.CreateModel(
name='SubscribeForUser',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
('last_paid_DT', models.DateTimeField(blank=True, null=True, verbose_name='Последняя дата оплаты')),
('paid_period_from_DT', models.DateTimeField(blank=True, null=True, verbose_name='Оплаченный период с')),
('paid_period_to_DT', models.DateTimeField(blank=True, null=True, verbose_name='Оплаченный период до')),
('auto_continue', models.BooleanField(default=False, verbose_name='Автопродление')),
('receive_finish_subscribe_msg', models.BooleanField(default=False, verbose_name='Получать сообщения о окончании периода')),
('subscribe', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rel_userSubscribes_for_subscribe', to='SubscribesApp.subscribe', verbose_name='Подписка')),
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rel_userSubscribes_for_user', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь')),
],
options={
'verbose_name': 'Пользовательская подписка',
'verbose_name_plural': 'Пользовательские подписки',
},
),
migrations.AddField(
model_name='subscribe',
name='options',
field=models.ManyToManyField(blank=True, related_name='rel_subscribes_for_option', to='SubscribesApp.subscribeoption', verbose_name='Подключенные опции'),
),
]

View File

@@ -0,0 +1,28 @@
# Generated by Django 4.2.2 on 2023-08-30 16:11
import colorfield.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('SubscribesApp', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='subscribeoption',
options={'ordering': ['order'], 'verbose_name': 'Опция подписки', 'verbose_name_plural': 'Опции подписки'},
),
migrations.AddField(
model_name='subscribe',
name='bg_color',
field=colorfield.fields.ColorField(default='#FFFFFF', image_field=None, max_length=18, samples=None, verbose_name='Цвет фона'),
),
migrations.AddField(
model_name='subscribe',
name='text_color',
field=colorfield.fields.ColorField(default='#000000', image_field=None, max_length=18, samples=None, verbose_name='Цвет текста'),
),
]

View File

@@ -0,0 +1,24 @@
# Generated by Django 4.2.2 on 2023-11-30 13:42
import colorfield.fields
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('SubscribesApp', '0002_alter_subscribeoption_options_subscribe_bg_color_and_more'),
]
operations = [
migrations.AlterField(
model_name='subscribe',
name='bg_color',
field=colorfield.fields.ColorField(default='#FFFFFF', image_field=None, max_length=25, samples=None, verbose_name='Цвет фона'),
),
migrations.AlterField(
model_name='subscribe',
name='text_color',
field=colorfield.fields.ColorField(default='#000000', image_field=None, max_length=25, samples=None, verbose_name='Цвет текста'),
),
]

View File

52
SubscribesApp/models.py Normal file
View File

@@ -0,0 +1,52 @@
from django.db import models
from BaseModels.base_models import BaseModel
from django.utils.translation import gettext_lazy as _
from colorfield.fields import ColorField
class SubscribeOption(BaseModel):
class Meta:
verbose_name = _('Опция подписки')
verbose_name_plural = _('Опции подписки')
ordering = ['order']
class Subscribe(BaseModel):
price = models.FloatField(verbose_name='Стоимость', default=0)
options = models.ManyToManyField(
SubscribeOption, verbose_name=_('Подключенные опции'), blank=True, related_name='rel_subscribes_for_option'
)
period_name = models.CharField(max_length=250, verbose_name=_('Название периода'))
period = models.IntegerField(default=0, verbose_name=_('Длительность подписки в часах'))
bg_color = ColorField(default='#FFFFFF', verbose_name=_('Цвет фона'))
text_color = ColorField(default='#000000', verbose_name=_('Цвет текста'))
class Meta:
verbose_name = _('Подписка')
verbose_name_plural = _('Подписки')
class SubscribeForUser(BaseModel):
from AuthApp.models import User
user = models.ForeignKey(
User, verbose_name=_('Пользователь'), related_name='rel_userSubscribes_for_user', on_delete=models.CASCADE)
subscribe = models.ForeignKey(
Subscribe, verbose_name=_('Подписка'), null=True, blank=True, related_name='rel_userSubscribes_for_subscribe',
on_delete=models.SET_NULL
)
last_paid_DT = models.DateTimeField(verbose_name=_('Последняя дата оплаты'), blank=True, null=True)
paid_period_from_DT = models.DateTimeField(verbose_name=_('Оплаченный период с'), blank=True, null=True)
paid_period_to_DT = models.DateTimeField(verbose_name=_('Оплаченный период до'), blank=True, null=True)
auto_continue = models.BooleanField(default=False, verbose_name=_('Автопродление'))
receive_finish_subscribe_msg = models.BooleanField(
default=False, verbose_name=_('Получать сообщения о окончании периода'))
class Meta:
verbose_name = _('Пользовательская подписка')
verbose_name_plural = _('Пользовательские подписки')

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