1036 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
246 changed files with 39452 additions and 3084 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

View File

@@ -1,8 +1,6 @@
## -*- coding: utf8 -*-
__author__ = 'SDE'
# -*- 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 *
@@ -18,7 +16,7 @@ from sets.admin import *
#
#
# }),
# (u'Управление отображением страницы', {
# ('Управление отображением страницы', {
# 'classes': ['wide'],
# 'fields': (
# # ('serviceBlockStateShow'),
@@ -70,7 +68,7 @@ class Admin_Article(Admin_Trans_BaseModelViewPage):
# ('devices'),
)
}),
(u'Статья', {
(_('Статья'), {
'classes': ['wide'],
'fields': (
'description', 'text',
@@ -116,7 +114,7 @@ class Admin_UserPage(Admin_Trans_BaseModelViewPage):
# ('devices'),
)
}),
(u'Статья', {
(_('Статья'), {
'classes': ['wide'],
'fields': ('picture', 'description', 'text')
}),
@@ -151,7 +149,7 @@ class Admin_UserPage(Admin_Trans_BaseModelViewPage):
if request.user.is_superuser:
return True
if obj and obj.url in ('for-partners', 'dealers', 'about-truenergy', 'contacts'):
if obj and obj.url in ('for-partners', 'dealers', 'contacts'):
return False
return True

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,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

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

View File

@@ -5,5 +5,5 @@ 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('page/<str:page_url>/', UserPageView, name=u'page_one'),
path('info_page/<str:page_url>/', UserPageView, name=u'user_page'),
]

View File

@@ -1,10 +1,13 @@
# -*- coding: utf8 -*-
# -*- 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
@@ -15,7 +18,7 @@ from django.template import loader
def get_flat_pages_links_Dict(site):
flat_pages_links = UserPageModel.objects.filter(
url__in=('about-truenergy', 'for-partners', 'contacts'),
url__in=('for-partners', 'contacts'),
sites=site
).values_list('url', flat=True)
@@ -25,21 +28,25 @@ def get_flat_pages_links_Dict(site):
def get_article_breadcrumbs(request, art):
# print('get_article_breadcrumbs')
half_count = art.articlesCountInBlock / 2
articles_add_count = 3
# half_count = articlesCountInBlock / 2
art_List = ArticleModel.objects.filter(enable=True, article_DT__gte=art.article_DT).order_by(
'article_DT')[:half_count]
artListDown = ArticleModel.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 = ArticleModel.objects.filter(enable=True, article_DT__gte=art.article_DT).order_by(
'article_DT')[:art.articlesCountInBlock-len(artListDown)]
# 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)]
art_List = list(art_List)
art_List.reverse()
artlist = art_List + list(artListDown)
# print('artlist',artlist)
return artlist
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):
@@ -75,14 +82,18 @@ def ArticlesPageView(request, year=None):
raise Http404
arts = ArticleModel.objects.filter(**kwargs)
Dict = {
'articles': arts
}
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 HttpResponse(t.render(Dict, request))
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))
@@ -105,7 +116,7 @@ def UserPageView(request, page_url):
# print('article_breadcrumbs',article_breadcrumbs)
Dict = {
'art' : art,
'page' : art,
'user_page' : True,
# 'service' : service,
# 'article_breadcrumbs' : article_breadcrumbs
@@ -113,8 +124,9 @@ def UserPageView(request, page_url):
t = loader.get_template('pages/p_article.html')
return HttpResponse(t.render(Dict, request))
t = loader.get_template('pages/p_user_page.html')
return get_inter_http_respose(t, Dict, request)
# return HttpResponse(t.render(Dict, request))
@@ -131,15 +143,17 @@ def ArticlesOnePageView(request, art_url):
raise Http404
# article_breadcrumbs = get_article_breadcrumbs(request, art)
# print('article_breadcrumbs',article_breadcrumbs)
Dict = {
'art' : art,
# 'article_breadcrumbs' : article_breadcrumbs
'page' : art,
}
Dict.update(get_article_breadcrumbs(request, art))
t = loader.get_template('pages/p_user_page.html')
return HttpResponse(t.render(Dict, request))
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)

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,3 +1,5 @@
import copy
from .models import *
from django.db.models import Q, Value as V, Count, OuterRef, Subquery
from django.http import HttpResponse, Http404, JsonResponse
@@ -10,6 +12,13 @@ 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()
@@ -35,7 +44,6 @@ def get_update_chat_Dict(data):
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})
@@ -46,11 +54,22 @@ def get_update_chat_Dict(data):
receiver = User.objects.get(id=data['receiver'])
if data['sender'] == data['cur_user']:
context_Dict.update({'user': sender})
user = copy.copy(sender)
cur_receiver = copy.copy(receiver)
else:
context_Dict.update({'user': receiver})
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': 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']
@@ -64,7 +83,10 @@ def get_update_chat_Dict(data):
# формируем правую панель
context_Dict.update({'receivers': receivers})
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({
@@ -88,7 +110,10 @@ def get_update_chat_Dict(data):
if ticket.manager:
context_Dict.update({'new_msg_allow': True})
context_Dict.update({'messages': msgs})
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})
@@ -108,6 +133,9 @@ def get_update_chat_Dict(data):
def send_msg(data):
from AuthApp.models import User
import base64
import os
res_Dict = {}
msg = None
required_update_tickets_list_wo_managers = False
@@ -115,11 +143,12 @@ def send_msg(data):
try:
if type(data) == str:
data = json.loads(data)
ticket = None
tpl_name = 'blocks/profile/b_messages_container.html'
if data['text']:
if 'text' in data or 'files' in data:
if 'ticket_id' in data:
ticket = MsgGroup.objects.get(id=data['ticket_id'])
@@ -173,11 +202,33 @@ def send_msg(data):
msg_create_kwargs.update({
'sender': sender,
'receiver': receiver,
'text': data['text']
})
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:
@@ -211,6 +262,57 @@ def send_msg(data):
# 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
@@ -258,7 +360,10 @@ 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)
@@ -266,7 +371,10 @@ def get_chat_receivers_for_user(user, cur_receiver=None):
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.status == 'sended':
i = receivers_list.index(msg.sender)

View File

@@ -13,4 +13,6 @@ urlpatterns = [
# 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

@@ -18,6 +18,31 @@ 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/')
def show_chat_w_user_ajax(request):
@@ -28,6 +53,7 @@ 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'
@@ -74,7 +100,7 @@ def update_chat_ajax2(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 not ticket:
@@ -98,6 +124,7 @@ def update_chat_ajax2(request):
# формируем правую панель
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({
@@ -165,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:
@@ -176,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({
@@ -195,6 +225,8 @@ def update_chat_ajax(request):
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({
@@ -377,12 +409,16 @@ def support_show_chat_by_ticket_ajax(request):
'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)
@@ -402,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)
@@ -423,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)
@@ -463,6 +504,8 @@ def create_ticket_ajax(request):
'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)
res_Dict = {
@@ -473,9 +516,11 @@ 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}

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

@@ -16,10 +16,35 @@ register = template.Library()
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

@@ -121,8 +121,13 @@ class ChatConsumer(WebsocketConsumer):
def receive(self, text_data):
print(f'ws receive text_data = {text_data}')
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
@@ -130,6 +135,15 @@ class ChatConsumer(WebsocketConsumer):
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)

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,9 +4,9 @@ from django.contrib import admin
class Admin_Route(Admin_Trans_BaseModel):
list_display = [
'id', 'owner_type', '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'
]
@@ -14,5 +14,6 @@ class Admin_Route(Admin_Trans_BaseModel):
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,4 +1,143 @@
from .models import *
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
@@ -18,27 +157,73 @@ def get_routes_Dict(user=None, data=None):
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 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])})
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_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('-modifiedDT')
routes = Route.objects.filter(**kwargs).order_by('-departure_DT', '-arrival_DT', '-modifiedDT')
routes_count = routes.count()
if from_el and to_el:
@@ -48,13 +233,17 @@ def get_routes_Dict(user=None, data=None):
elif to_el:
routes = routes[:to_el]
else:
routes = routes[:25]
to_el = elements_on_page
routes = routes[:to_el]
last_block_routes = False
if to_el and to_el >= routes_count:
last_block_routes = True
last_block = False
if not to_el or to_el >= routes_count:
last_block = True
res_Dict = {}
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:
@@ -77,10 +266,12 @@ def get_routes_Dict(user=None, data=None):
msg = f'get_routes_for_user get route Error = {str(e)}'
print(msg)
res_Dict = {
res_Dict.update({
'routes': routes,
'last_block_routes': last_block_routes
}
'last_block': last_block,
'last_el': to_el,
'next_page_els_count': next_page_els_count
})
return res_Dict

View File

@@ -9,8 +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_routes_ajax, name='get_routes_ajax'),
path('get_routes/', get_my_routes_ajax, name='get_my_routes_ajax'),
path('find_routes/', find_routes_ajax, name='find_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,75 +112,17 @@ 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)
@@ -159,17 +139,21 @@ def find_routes_ajax(request):
if not data and request.body:
data = json.loads(request.body)
data['owner_type'] = 'mover'
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)
html = render_to_string('blocks/b_search_routes.html', routes_Dict, request=request)
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': routes_Dict['last_block_routes']
'last_block': routes_Dict['last_block'],
'next_page_els_count': routes_Dict['next_page_els_count'],
# 'form': RouteForm(initial=data)
}
@@ -185,7 +169,7 @@ def find_routes_ajax(request):
return JsonResponse(errors_Dict, status=400)
def get_routes_ajax(request):
def get_my_routes_ajax(request):
if request.method != 'POST':
@@ -217,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
@@ -247,11 +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:
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:
@@ -263,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

@@ -11,17 +11,50 @@ 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'],
'form': RouteForm()
'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

@@ -6,23 +6,25 @@ class Admin_Subscribe(Admin_Trans_BaseModel):
fieldsets = (
(None, {
'classes': ['wide'],
'fields': ('name',
'fields': ('name', 'enable',
('price'),
'options',
'period_name', 'period',
'order'
'order',
'bg_color', 'text_color'
)
}),
)
list_display = [
'id',
'name', 'price',
'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']
@@ -36,16 +38,18 @@ class Admin_SubscribeOption(Admin_Trans_BaseModel):
(None, {
'classes': ['wide'],
'fields': ('name',
'order'
'order',
'enable'
)
}),
)
list_display = [
'id',
'id', 'enable',
'name',
'order', 'modifiedDT', 'createDT'
]
list_editable = ['enable']
list_display_links = ['id']

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,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

@@ -1,12 +1,14 @@
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']
@@ -19,6 +21,9 @@ class Subscribe(BaseModel):
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 = _('Подписки')

View File

@@ -0,0 +1,7 @@
from rest_framework import serializers
class SubscribersSerializer(serializers.Serializer):
email = serializers.EmailField(error_messages={'invalid': 'Invalid email'})
email_notification = serializers.BooleanField()
auto_subscribe = serializers.BooleanField()

9
SubscribesApp/urls.py Normal file
View File

@@ -0,0 +1,9 @@
from django.urls import path
from SubscribesApp.views import SubscribersView
urlpatterns = [
path('auto-subscribe/', SubscribersView.as_view(), name='auto_subscribe'),
]

View File

@@ -1,3 +1,43 @@
from django.shortcuts import render
from django.contrib.auth.models import User
from django.http import JsonResponse
from rest_framework import status
from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView
# Create your views here.
from SubscribesApp.models import SubscribeForUser
from SubscribesApp.serializers import SubscribersSerializer
class SubscribersView(APIView):
# permission_classes = [IsAuthenticated]
def post(self, request):
serializer = SubscribersSerializer(data=request.data)
if serializer.is_valid():
validated_data = serializer.validated_data
email = validated_data['email']
email_notification = validated_data['email_notification']
auto_subscribe = validated_data['auto_subscribe']
user = User.objects.filter(email=email)
if user:
subscribe_for_user = SubscribeForUser.objects.filter(user_id=user[0].id)
if email_notification:
subscribe_for_user.update(receive_finish_subscribe_msg=True)
else:
subscribe_for_user.update(receive_finish_subscribe_msg=False)
if auto_subscribe:
subscribe_for_user.update(auto_continue=True)
else:
subscribe_for_user.update(auto_continue=False)
return JsonResponse({"message": "Subscriptions updated successfully"}, status=status.HTTP_200_OK)
else:
return JsonResponse({"message": "User not found"}, status=status.HTTP_404_NOT_FOUND)
else:
return JsonResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

View File

@@ -22,11 +22,14 @@ os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'TWB.settings')
application = ProtocolTypeRouter({
'http': get_asgi_application(),
"websocket": QueryAuthMiddleware(
URLRouter(
websocket_urlpatterns
)
),
"websocket":
# AllowedHostsOriginValidator(
QueryAuthMiddleware(
URLRouter(
websocket_urlpatterns
)
# )
),
# 'websocket': AuthMiddlewareStack(
# URLRouter(
# websocket_urlpatterns

27
TWB/celery.py Executable file
View File

@@ -0,0 +1,27 @@
from __future__ import absolute_import
import os
from celery import Celery
from celery.schedules import crontab
from django.conf import settings
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "TWB.settings")
app = Celery('bo', include=['TWB.tasks'])
app.config_from_object('django.conf:settings', namespace='CELERY')
app.autodiscover_tasks(lambda: settings.INSTALLED_APPS)
app.conf.beat_schedule = {
'update-currency-rates': {
'task': 'TWB.tasks.check_auto_subscribe',
'schedule': crontab(minute=0, hour='*/1'),
},
'subscription_expiration_check': {
'task': 'TWB.tasks.subscription_expiration_check',
'schedule': crontab(hour=0, minute=0),
# 'schedule': crontab(minute='*', hour='*'),
},
}
app.conf.broker_url = settings.CELERY_BROKER_URL

View File

@@ -11,10 +11,13 @@ https://docs.djangoproject.com/en/4.2/ref/settings/
"""
from pathlib import Path
from decouple import config
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
CSRF_TRUSTED_ORIGINS = ['https://tripwb.com']
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
@@ -28,6 +31,50 @@ DEBUG = True
ALLOWED_HOSTS = ["*"]
WEBPUSH_SETTINGS = {
"VAPID_PUBLIC_KEY": "BKS8byh3MucwCF2h06JY9oey1s1RYII09j-j3ehI3qTYhs965UHv0qNPl-jFjQBbIJCvjVXm9RW6t_oJJK8yMOk",
"VAPID_PRIVATE_KEY": "f5NMgOntBtRqsyeKwEzloK-051ggMnZGF_GFimERY0w",
"VAPID_ADMIN_EMAIL": "admin@tripwb.com"
}
SOCIALACCOUNT_LOGIN_ON_GET=True
ACCOUNT_DEFAULT_HTTP_PROTOCOL='https'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_EMAIL_VERIFICATION = 'optional'
LOGIN_REDIRECT_URL = '/profile/page/dashboard/'
LOGIN_URL = '/profile/login/'
LOGOUT_REDIRECT_URL = '/profile/login/'
ACCOUNT_SIGNUP_REDIRECT_URL = '/profile/page/dashboard/'
ACCOUNT_LOGOUT_ON_GET = True
# SOCIALACCOUNT_ADAPTER = 'GeneralApp.allauth_funcs.MyAccountAdapter'
ACCOUNT_ADAPTER = 'GeneralApp.allauth_funcs.MyAccountAdapter'
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend',
'allauth.account.auth_backends.AuthenticationBackend',
]
SOCIALACCOUNT_PROVIDERS = {
'google': {
'SCOPE': [
'profile',
'email',
],
'AUTH_PARAMS': {
'access_type': 'online',
},
}
}
# NOTIFICATION_KEY = 'BJLyGzmo8sLI3Qkc6pN2cz11frCXiJdewvgve7Yps-_fM1lY1LSnTQfQxYtAgQ_26nAji_rgeYC1DkLiTwxw0Mo'
# SESSION_COOKIE_HTTPONLY = False
# Application definition
@@ -46,15 +93,27 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'django.contrib.humanize',
'django.contrib.sites',
'colorfield',
'ckeditor',
'ckeditor_uploader',
'webpush',
'allauth',
'allauth.account',
'allauth.socialaccount',
'allauth.socialaccount.providers.google',
'GeneralApp',
'AuthApp',
'RoutesApp',
'ReferenceDataApp',
'ArticlesApp',
'SubscribesApp',
'PushMessages',
]
MIDDLEWARE = [
@@ -67,8 +126,14 @@ MIDDLEWARE = [
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
'AuthApp.middleware.ResponseInterceptionMiddleware',
'TWB.tz_middelware.TimezoneMiddleware',
# 'tz_detect.middleware.TimezoneMiddleware',
"allauth.account.middleware.AccountMiddleware",
]
SITE_ID = 1
ROOT_URLCONF = 'TWB.urls'
TEMPLATES = [
@@ -107,11 +172,8 @@ CHANNEL_LAYERS = {
}
POSTGRES_DB = config('POSTGRES_DB')
POSTGRES_USER = config('POSTGRES_USER')
# Database
@@ -120,8 +182,8 @@ CHANNEL_LAYERS = {
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'twbDB',
'USER': 'test_user',
'NAME': POSTGRES_DB,
'USER': POSTGRES_USER,
'PASSWORD': 'test_db_pass',
'HOST': '127.0.0.1',
'PORT': '5432',
@@ -183,6 +245,7 @@ LOCALE_PATHS = (
)
gettext = lambda s: s
LANGUAGES = (
(u'ru', gettext(u'Russian')),
@@ -206,11 +269,17 @@ django.conf.locale.LANG_INFO = LANG_INFO
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
DATA_UPLOAD_MAX_MEMORY_SIZE = 4145728
CKEDITOR_BASEPATH = "/static/ckeditor/ckeditor/"
CKEDITOR_UPLOAD_PATH = "uploads/"
CKEDITOR_RESTRICT_BY_DATE = False
CKEDITOR_RESTRICT_BY_USER = True
CKEDITOR_BROWSE_SHOW_DIRS = True
CKEDITOR_IMAGE_BACKEND = "pillow"
# CKEDITOR_IMAGE_BACKEND = 'ckeditor_uploader.backends.PillowBackend'
# CKEDITOR_IMAGE_BACKEND = 'pillow'
CKEDITOR_IMAGE_BACKEND = "BaseModels.PIL.pillow_backend.PillowBackend"
# CKEDITOR_BROWSE_SHOW_DIRS = True
@@ -218,85 +287,131 @@ CKEDITOR_JQUERY_URL = 'https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery
CKEDITOR_CONFIGS = {
'default': {
'height': 291,
'width': '70vw',
'filebrowserWindowHeight': 600,
'filebrowserWindowWidth': "90%",
'toolbar': 'Custom',
'forcePasteAsPlainText': True,
'allowedContent': True,
'filebrowserImageThumbWidth': 300,
'filebrowserImageThumbHeight': 300,
'filebrowserUploadUrl': '/ckeditor/upload/',
# 'disallowedContent': 'img{width,height};img[width,height]',
# 'extraPlugins': 'image2',
'enterMode': 2,
'basicEntities' : False,
'entities_additional': '',
'entities' : False,
'htmlEncodeOutput' : False,
# 'toolbar_Custom': [
# ['Bold', 'Italic', 'Underline'],
# ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
# ['Link', 'Unlink'],
# ['RemoveFormat', 'Source']
# ],
'toolbar_Custom': [
['Bold', 'Italic', 'Underline'],
['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock'],
['Link', 'Unlink'],
['RemoveFormat', 'Source']
]
{'name': 'document', 'items': ['Source', '-', 'Save', '-', 'Find', 'Replace', '-', 'SelectAll']},
{'name': 'clipboard', 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo', '-', 'Maximize', 'ShowBlocks']},
# {'name': 'forms',
# 'items': ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton',
# 'HiddenField']},
'/',
{'name': 'basicstyles',
'items': ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'TextColor', 'BGColor', '-', 'RemoveFormat', '-']},
{'name': 'paragraph',
'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-',
'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl'
]},
'/',
{'name': 'links', 'items': ['Link', 'Unlink', 'Anchor', '-', 'Blockquote' ]},
{'name': 'insert',
'items': ['Image', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']},
],
}
}
CELERY_BROKER_URL = config('CELERY_BROKER_URL')
CELERY_RESULT_BACKEND = config('CELERY_RESULT_BACKEND')
CKEDITOR_OPTIONS = {
'height': 291,
'width': '95%',
'filebrowserWindowHeight': 600,
'filebrowserWindowWidth': "100%",
'toolbar': 'YourCustomToolbarConfig',
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_USE_TLS = True
EMAIL_PORT = 587
EMAIL_HOST_USER = config('EMAIL_HOST_USER')
EMAIL_HOST_PASSWORD = config('EMAIL_HOST_PASSWORD')
'allowedContent': True,
'enterMode': 2,
'basicEntities' : False,
'entities_additional': '',
'entities' : False,
'htmlEncodeOutput' : False,
'toolbar_Basic': [
['Source', '-', 'Bold', 'Italic']
],
'toolbar_YourCustomToolbarConfig': [
{'name': 'document', 'items': ['Source', '-', 'Save', 'NewPage', 'Preview', 'Print', '-', 'Templates']},
{'name': 'clipboard', 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
{'name': 'editing', 'items': ['Find', 'Replace', '-', 'SelectAll']},
{'name': 'forms',
'items': ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton',
'HiddenField']},
'/',
{'name': 'basicstyles',
'items': ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']},
{'name': 'paragraph',
'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-',
'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl',
'Language']},
{'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
{'name': 'insert',
'items': ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']},
'/',
{'name': 'styles', 'items': ['FontSize']},
{'name': 'colors', 'items': ['TextColor', 'BGColor']},
{'name': 'tools', 'items': ['Maximize', 'ShowBlocks']},
# {'name': 'about', 'items': ['About']},
'/', # put this to force next toolbar on new line
# {'name': 'yourcustomtools', 'items': [
# # put the name of your editor.ui.addButton here
# 'Preview',
# 'Maximize',
#
# ]},
],
'tabSpaces': 4,
'removePlugins': 'stylesheetparser',
# 'extraPlugins': ','.join([
# 'uploadimage', # the upload image feature
# # your extra plugins here
# 'div',
# 'autolink',
# 'autoembed',
# 'embedsemantic',
# 'autogrow',
# # 'devtools',
# 'widget',
# 'lineutils',
# 'clipboard',
# 'dialog',
# 'dialogui',
# 'elementspath'
# ]),
}
# CKEDITOR_OPTIONS = {
# 'height': 291,
# 'width': '95%',
# 'filebrowserWindowHeight': 600,
# 'filebrowserWindowWidth': "100%",
# 'toolbar': 'YourCustomToolbarConfig',
#
# 'allowedContent': True,
#
# 'enterMode': 2,
# 'basicEntities' : False,
# 'entities_additional': '',
# 'entities' : False,
# 'htmlEncodeOutput' : False,
# 'toolbar_Basic': [
# ['Source', '-', 'Bold', 'Italic']
# ],
# 'toolbar_YourCustomToolbarConfig': [
# {'name': 'document', 'items': ['Source', '-', 'Save', 'NewPage', 'Preview', 'Print', '-', 'Templates']},
# {'name': 'clipboard', 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
# {'name': 'editing', 'items': ['Find', 'Replace', '-', 'SelectAll']},
# {'name': 'forms',
# 'items': ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton',
# 'HiddenField']},
# '/',
# {'name': 'basicstyles',
# 'items': ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']},
# {'name': 'paragraph',
# 'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-',
# 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl',
# 'Language']},
# {'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
# {'name': 'insert',
# 'items': ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']},
# '/',
# {'name': 'styles', 'items': ['FontSize']},
# {'name': 'colors', 'items': ['TextColor', 'BGColor']},
# {'name': 'tools', 'items': ['Maximize', 'ShowBlocks']},
# # {'name': 'about', 'items': ['About']},
# '/', # put this to force next toolbar on new line
# # {'name': 'yourcustomtools', 'items': [
# # # put the name of your editor.ui.addButton here
# # 'Preview',
# # 'Maximize',
# #
# # ]},
# ],
#
# 'tabSpaces': 4,
# 'removePlugins': 'stylesheetparser',
# # 'extraPlugins': ','.join([
# # 'uploadimage', # the upload image feature
# # # your extra plugins here
# # 'div',
# # 'autolink',
# # 'autoembed',
# # 'embedsemantic',
# # 'autogrow',
# # # 'devtools',
# # 'widget',
# # 'lineutils',
# # 'clipboard',
# # 'dialog',
# # 'dialogui',
# # 'elementspath'
# # ]),
#
# }
try:

40
TWB/tasks.py Normal file
View File

@@ -0,0 +1,40 @@
from datetime import datetime, timedelta, timezone
from SubscribesApp.models import SubscribeForUser
from TWB import settings
from TWB.celery import app
from django.core.mail import send_mail
@app.task
def check_auto_subscribe():
current_time = datetime.now()
subscribes = SubscribeForUser.objects.filter(auto_continue=True)
if subscribes:
for subscribe in subscribes:
if subscribe.paid_period_to_DT and subscribe.paid_period_to_DT <= current_time + timedelta(hours=1):
user_email = subscribe.user.email
subject = 'Подписка продлена!'
message = 'Ваша подписка успешно продлена!'
send_mail(subject, message, settings.EMAIL_HOST_USER, [user_email], fail_silently=False)
else:
print('Нету подписок')
@app.task
def subscription_expiration_check():
current_time = datetime.now()
subscribes = SubscribeForUser.objects.filter(paid_period_to_DT__gte=current_time)
if subscribes:
for subscribe in subscribes:
expiration_date = subscribe.paid_period_to_DT
remaining_days = (expiration_date - current_time).days
if remaining_days <= 7:
message = f'Ваша подписка заканчивается через {remaining_days} дня. Пожалуйста, продлите её.'
user_email = subscribe.user.email
subject = 'Подписка истекает!'
send_mail(subject, message, settings.EMAIL_HOST_USER, [user_email], fail_silently=False)
else:
print('Нету подписок')

22
TWB/tz_middelware.py Normal file
View File

@@ -0,0 +1,22 @@
import zoneinfo
from django.utils import timezone
from django.shortcuts import render
class TimezoneMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
tz = request.COOKIES.get("user_tz")
if tz:
if request.user.is_authenticated:
if not 'user_timezone' in request.user.user_profile.json_data or request.user.user_profile.json_data['user_timezone'] != tz:
request.user.user_profile.json_data['user_timezone'] = tz
request.user.user_profile.save(update_fields=['json_data'])
msg = f'user={str(request.user.id)} tz={str(tz)}'
print(msg)
timezone.activate(zoneinfo.ZoneInfo(tz))
else:
timezone.activate(zoneinfo.ZoneInfo("UTC"))
return self.get_response(request)

View File

@@ -1,15 +1,45 @@
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
from GeneralApp.views import Page404
from AuthApp.views import login_View
handler404 = Page404
urlpatterns = [
# path('admin/', admin.site.urls),
path('ckeditor/', include('ckeditor_uploader.urls')),
path('i18n/', include('django.conf.urls.i18n')),
# path('tz_detect/', include('tz_detect.urls')),
path('accounts/signup/', login_View, name='signup'),
path('accounts/login/cancelled/', login_View),
path('accounts/', include('allauth.urls')),
path('accounts/', include('allauth.socialaccount.urls')),
path('messages/', include('ChatServiceApp.urls')),
path('user_account/', include('AuthApp.js_urls')),
path('routes/', include('RoutesApp.js_urls')),
path('subscribes/', include('SubscribesApp.js_urls')),
path('messages/', include('ChatServiceApp.js_urls')),
path('reference_data/', include('ReferenceDataApp.js_urls')),
path('', include('ArticlesApp.js_urls')),
path('test_404', Page404, name='page_404'),
path('', include('PushMessages.urls')),
path('', include('SubscribesApp.urls')),
]
from django.conf.urls.i18n import i18n_patterns
@@ -19,17 +49,12 @@ urlpatterns += i18n_patterns(
path('', include('GeneralApp.urls')),
path('profile/', include('AuthApp.urls')),
path('user_account/', include('AuthApp.js_urls')),
path('routes/', include('RoutesApp.js_urls')),
path('routes/', include('RoutesApp.urls')),
path('messages/', include('ChatServiceApp.js_urls')),
path('reference_data/', include('ReferenceDataApp.js_urls')),
path('', include('ArticlesApp.urls_translate')),
)
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

8
env.sample Normal file
View File

@@ -0,0 +1,8 @@
EMAIL_HOST_USER=
EMAIL_HOST_PASSWORD=
POSTGRES_DB=
POSTGRES_USER=
CELERY_BROKER_URL=
CELERY_RESULT_BACKEND=

View File

@@ -0,0 +1,951 @@
# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
# This file is distributed under the same license as the PACKAGE package.
# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
#
#, fuzzy
msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2023-09-26 12:03+0300\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: .\AuthApp\admin.py:66 .\AuthApp\admin.py:148
msgid "Профиль пользователя"
msgstr ""
#: .\AuthApp\models.py:24
msgid "Перевозчик"
msgstr ""
#: .\AuthApp\models.py:25
msgid "Отправитель"
msgstr ""
#: .\AuthApp\models.py:32
msgid "Тип учетной записи"
msgstr ""
#: .\AuthApp\models.py:34 .\SubscribesApp\models.py:36
msgid "Пользователь"
msgstr ""
#: .\AuthApp\models.py:37
msgid "Аватар"
msgstr ""
#: .\AuthApp\models.py:41
msgid "Телефон"
msgstr ""
#: .\AuthApp\models.py:42 .\ReferenceDataApp\models.py:34
msgid "Страна"
msgstr ""
#: .\AuthApp\models.py:43 .\ReferenceDataApp\models.py:67
msgid "Город"
msgstr ""
#: .\AuthApp\models.py:45
msgid "Дата рождения"
msgstr ""
#: .\AuthApp\models.py:47
msgid "Дополнительные сведения"
msgstr ""
#: .\AuthApp\models.py:50
msgid "Создатель"
msgstr ""
#: .\AuthApp\models.py:70
msgid "Профиль"
msgstr ""
#: .\AuthApp\models.py:71
msgid "Профили"
msgstr ""
#: .\AuthApp\views.py:190
msgid "незарег. пользователь"
msgstr ""
#: .\BaseModels\base_models.py:21 .\BaseModels\base_models.py:22
msgid "Название"
msgstr ""
#: .\BaseModels\base_models.py:23
msgid "Название (множественное число)"
msgstr ""
#: .\BaseModels\base_models.py:25
msgid "Очередность отображения"
msgstr ""
#: .\BaseModels\base_models.py:26
msgid "Дата и время создания"
msgstr ""
#: .\BaseModels\base_models.py:27
msgid "Дата и время последнего изменения"
msgstr ""
#: .\BaseModels\base_models.py:28
msgid "Включено"
msgstr ""
#: .\BaseModels\base_models.py:30
msgid "Дополнительные данные"
msgstr ""
#: .\BaseModels\base_models.py:86
msgid "URL привязанной страницы"
msgstr ""
#: .\BaseModels\base_models.py:88
msgid ""
"можно изменить адрес страницы (!!! ВНИМАНИЕ !!! поисковые системы потеряют "
"страницу и найдут лишь спустя неделю...месяц)"
msgstr ""
#: .\BaseModels\base_models.py:89
msgid "Заголовок"
msgstr ""
#: .\BaseModels\base_models.py:90
msgid "Краткое описание"
msgstr ""
#: .\BaseModels\base_models.py:91
msgid "краткое описание страницы (до 240 символов)"
msgstr ""
#: .\BaseModels\base_models.py:92
msgid "Полное описание"
msgstr ""
#: .\BaseModels\base_models.py:94
msgid "Картинка"
msgstr ""
#: .\BaseModels\base_models.py:97
msgid "Отображать"
msgstr ""
#: .\BaseModels\base_models.py:98
msgid "Левая подложка"
msgstr ""
#: .\BaseModels\base_models.py:99
msgid "Правая подложка"
msgstr ""
#: .\BaseModels\base_models.py:102
msgid "Title (80 знаков)"
msgstr ""
#: .\BaseModels\base_models.py:103
msgid "Description (150 знаков)"
msgstr ""
#: .\BaseModels\base_models.py:105
msgid "Keywords (200 знаков)"
msgstr ""
#: .\BaseModels\base_models.py:106
msgid "Текст SEO статьи"
msgstr ""
#: .\BaseModels\base_models.py:108
msgid "FAQ Заголовок"
msgstr ""
#: .\ChatServiceApp\models.py:36 .\RoutesApp\models.py:63
msgid "Владелец"
msgstr ""
#: .\ChatServiceApp\models.py:38
msgid "Менеджер"
msgstr ""
#: .\ChatServiceApp\models.py:42
msgid "Тикет"
msgstr ""
#: .\ChatServiceApp\models.py:43
msgid "Тикеты"
msgstr ""
#: .\ChatServiceApp\models.py:68
#: .\templates\blocks\static_pages_blocks\b_about_service.html:118
#: .\templates\blocks\static_pages_blocks\b_contacts.html:37
#: .\templates\blocks\static_pages_blocks\b_customer_service.html:37
msgid "Сообщение"
msgstr ""
#: .\ChatServiceApp\models.py:69
msgid "Сообщения"
msgstr ""
#: .\GeneralApp\models.py:11
msgid "Статическая страница"
msgstr ""
#: .\GeneralApp\models.py:12
msgid "Статические страницы"
msgstr ""
#: .\GeneralApp\models.py:16
msgid "Блок на странице"
msgstr ""
#: .\GeneralApp\models.py:17
msgid "Блоки на страницах"
msgstr ""
#: .\GeneralApp\models.py:23
msgid "Миниатюра"
msgstr ""
#: .\GeneralApp\models.py:27
msgid "Параметр"
msgstr ""
#: .\GeneralApp\models.py:28
msgid "Параметры"
msgstr ""
#: .\ReferenceDataApp\models.py:35
msgid "Страны"
msgstr ""
#: .\ReferenceDataApp\models.py:68
msgid "Города"
msgstr ""
#: .\ReferenceDataApp\models.py:104
msgid "Аэропорт"
msgstr ""
#: .\ReferenceDataApp\models.py:105
msgid "Аэропорты"
msgstr ""
#: .\RoutesApp\funcs.py:30 .\RoutesApp\models.py:13
msgid "В аэропорту"
msgstr ""
#: .\RoutesApp\funcs.py:31 .\RoutesApp\funcs.py:46 .\RoutesApp\models.py:14
msgid "По городу"
msgstr ""
#: .\RoutesApp\funcs.py:32 .\RoutesApp\funcs.py:47 .\RoutesApp\models.py:15
msgid "По договоренности"
msgstr ""
#: .\RoutesApp\funcs.py:36 .\RoutesApp\models.py:19
msgid "Пассажир"
msgstr ""
#: .\RoutesApp\funcs.py:37 .\RoutesApp\funcs.py:51 .\RoutesApp\models.py:20
msgid "Груз"
msgstr ""
#: .\RoutesApp\funcs.py:38 .\RoutesApp\funcs.py:52 .\RoutesApp\models.py:21
msgid "Бандероль"
msgstr ""
#: .\RoutesApp\funcs.py:39 .\RoutesApp\funcs.py:53 .\RoutesApp\models.py:22
msgid "Посылка"
msgstr ""
#: .\RoutesApp\funcs.py:40 .\RoutesApp\funcs.py:54 .\RoutesApp\models.py:23
msgid "Письмо\\Документ"
msgstr ""
#: .\RoutesApp\models.py:7
msgid "-- Выберите cпособ перевозки --"
msgstr ""
#: .\RoutesApp\models.py:8
msgid "Авиатранспорт"
msgstr ""
#: .\RoutesApp\models.py:9
msgid "Наземный транспорт"
msgstr ""
#: .\RoutesApp\models.py:37
msgid "Тип опреации"
msgstr ""
#: .\RoutesApp\models.py:40
msgid "Выберите способ перевозки"
msgstr ""
#: .\RoutesApp\models.py:41
msgid "Дата и время выезда"
msgstr ""
#: .\RoutesApp\models.py:42
msgid "Дата и время прибытия"
msgstr ""
#: .\RoutesApp\models.py:43
msgid "Пункт выезда"
msgstr ""
#: .\RoutesApp\models.py:44
msgid "Пункт приезда"
msgstr ""
#: .\RoutesApp\models.py:46
msgid "Город отправки"
msgstr ""
#: .\RoutesApp\models.py:50
msgid "Город получения"
msgstr ""
#: .\RoutesApp\models.py:54
msgid "Откуда можете забрать?"
msgstr ""
#: .\RoutesApp\models.py:56
msgid "Куда можете доставить?"
msgstr ""
#: .\RoutesApp\models.py:57
msgid "Могу перевезти"
msgstr ""
#: .\RoutesApp\models.py:58
msgid "Укажите вес до (кг)"
msgstr ""
#: .\RoutesApp\models.py:59
msgid "Укажите номер для связи"
msgstr ""
#: .\RoutesApp\models.py:60
msgid "Дополнительный номер"
msgstr ""
#: .\RoutesApp\models.py:61
msgid "Получать уведомления по E-mail"
msgstr ""
#: .\RoutesApp\models.py:62
msgid "Получать уведомления по SMS"
msgstr ""
#: .\RoutesApp\models.py:73
msgid "Маршрут"
msgstr ""
#: .\RoutesApp\models.py:74
msgid "Маршруты"
msgstr ""
#: .\SubscribesApp\models.py:9
msgid "Опция подписки"
msgstr ""
#: .\SubscribesApp\models.py:10
msgid "Опции подписки"
msgstr ""
#: .\SubscribesApp\models.py:19
msgid "Подключенные опции"
msgstr ""
#: .\SubscribesApp\models.py:21
msgid "Название периода"
msgstr ""
#: .\SubscribesApp\models.py:22
msgid "Длительность подписки в часах"
msgstr ""
#: .\SubscribesApp\models.py:24
msgid "Цвет фона"
msgstr ""
#: .\SubscribesApp\models.py:25
msgid "Цвет текста"
msgstr ""
#: .\SubscribesApp\models.py:28 .\SubscribesApp\models.py:38
msgid "Подписка"
msgstr ""
#: .\SubscribesApp\models.py:29
msgid "Подписки"
msgstr ""
#: .\SubscribesApp\models.py:41
msgid "Последняя дата оплаты"
msgstr ""
#: .\SubscribesApp\models.py:42
msgid "Оплаченный период с"
msgstr ""
#: .\SubscribesApp\models.py:43
msgid "Оплаченный период до"
msgstr ""
#: .\SubscribesApp\models.py:45
msgid "Автопродление"
msgstr ""
#: .\SubscribesApp\models.py:48
msgid "Получать сообщения о окончании периода"
msgstr ""
#: .\SubscribesApp\models.py:51
msgid "Пользовательская подписка"
msgstr ""
#: .\SubscribesApp\models.py:52
msgid "Пользовательские подписки"
msgstr ""
#: .\TWB\settings.py:190
msgid "Russian"
msgstr ""
#: .\TWB\settings.py:191
msgid "English"
msgstr ""
#: .\templates\blocks\b_finded_routes.html:12
msgid ""
"\n"
" \tУпс... <span class=\"orange-text\">Ничего не найдено</"
"span>, попробуйте\n"
" изменить параметры поиска или оставьте заявку\n"
" на перевозку посылки\n"
" "
msgstr ""
#: .\templates\blocks\b_finded_routes.html:31
msgid "Показать ещё 10"
msgstr ""
#: .\templates\blocks\b_footer.html:9
msgid ""
"Подпишись и будь в курсе всех событий, а также получай подарки и бонусы от "
"Trip With Bonus"
msgstr ""
#: .\templates\blocks\b_footer.html:10
msgid "Введите ваш e-mail"
msgstr ""
#: .\templates\blocks\b_footer.html:21
msgid "Информация"
msgstr ""
#: .\templates\blocks\b_footer.html:24
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:22
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:152
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:39
#: .\templates\pages\p_main.html:175
msgid "Перевезти посылку"
msgstr ""
#: .\templates\blocks\b_footer.html:25
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:19
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:149
#: .\templates\pages\p_main.html:119
msgid "Отправить посылку"
msgstr ""
#: .\templates\blocks\b_footer.html:26
msgid "Для отправителя"
msgstr ""
#: .\templates\blocks\b_footer.html:27
msgid "Для перевозчика"
msgstr ""
#: .\templates\blocks\b_footer.html:35
msgid "О Trip With Bonus"
msgstr ""
#: .\templates\blocks\b_footer.html:36
msgid "Новости"
msgstr ""
#: .\templates\blocks\b_footer.html:37
msgid "Партнерам"
msgstr ""
#: .\templates\blocks\b_footer.html:38
msgid "Реклама"
msgstr ""
#: .\templates\blocks\b_footer.html:39
#: .\templates\blocks\static_pages_blocks\b_customer_service.html:3
msgid "Служба поддержки"
msgstr ""
#: .\templates\blocks\b_footer.html:40 .\templates\widgets\w_route_info.html:48
msgid "Контакты"
msgstr ""
#: .\templates\blocks\b_footer.html:44
msgid "Свяжитесь с нами:"
msgstr ""
#: .\templates\blocks\b_footer.html:59
msgid "Регистрация"
msgstr ""
#: .\templates\blocks\b_footer.html:60
msgid "Войти"
msgstr ""
#: .\templates\blocks\b_footer.html:70
msgid "Copyright © 2023. Все права защищены."
msgstr ""
#: .\templates\blocks\b_footer.html:73
msgid "Публичная оферта"
msgstr ""
#: .\templates\blocks\b_footer.html:75
msgid "Политика конфиденциальности"
msgstr ""
#: .\templates\blocks\b_footer.html:77
msgid "Правила пользования сервисом"
msgstr ""
#: .\templates\blocks\profile\b_subscribe_variants.html:11
msgid "У вас"
msgstr ""
#: .\templates\blocks\profile\b_subscribe_variants.html:11
msgid "не оформлена подписка"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:4
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:9
#: .\templates\pages\p_main.html:16
msgid ""
"Отправляй <span class=\"color_title\">посылку</span> в любую точку мира!"
msgstr ""
"Send your <span class=\"color_title\">parcel</span> anywhere in the world!"
#: .\templates\blocks\static_pages_blocks\b_about_service.html:7
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:8
#: .\templates\blocks\static_pages_blocks\b_partners.html:9
msgid "Узнать подробнее"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:8
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:25
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:76
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:14
msgid "Как это работает?"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:14
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:33
#: .\templates\blocks\static_pages_blocks\b_partners.html:15
#: .\templates\pages\p_main.html:55
msgid "О сервисе Trip With Bonus"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:18
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:37
#: .\templates\blocks\static_pages_blocks\b_partners.html:16
msgid ""
"Trip With Bonus- это сервис, который соединяет отправителя посылки и "
"перевозчика."
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:20
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:39
#: .\templates\blocks\static_pages_blocks\b_partners.html:18
msgid ""
"Вы можете разместить объявление о перевозке посылки и перевозчики со всего "
"мира откликнутся на ваше объявление или воспользовавшись поиском на сайте "
"найти перевозчика, который будет готов взять Вашу посылку и доставить в "
"указанное место наземным или авиатранспортом."
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:22
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:41
#: .\templates\blocks\static_pages_blocks\b_partners.html:20
msgid ""
"Если же Вы часто путешествуете или в разъездах, Вы можете самостоятельно "
"перевозить посылки и при этом получать бонусы и благодарности."
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:33
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:52
#: .\templates\blocks\static_pages_blocks\b_partners.html:24
msgid "Прямой контакт с перевозчиком/ отправителем"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:37
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:56
#: .\templates\blocks\static_pages_blocks\b_partners.html:28
msgid "Взаимовыгодное сотрудничество"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:41
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:60
#: .\templates\blocks\static_pages_blocks\b_partners.html:32
msgid "Тарифные планы на любой вкус и кошелёк."
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:45
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:64
#: .\templates\blocks\static_pages_blocks\b_partners.html:36
msgid "Возможность выстраивать логистику самостоятельно"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:49
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:68
#: .\templates\blocks\static_pages_blocks\b_partners.html:40
msgid "Простота и доступность пользования сервисом"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:53
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:72
#: .\templates\blocks\static_pages_blocks\b_partners.html:44
msgid "Общение и новые знакомства"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:59
msgid "Станьте нашим <span class=\"color_title\">партнером</span>"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:64
msgid ""
"Приглашаем к сотрудничесту и развитию, вместе с большой и сильной командой. "
"Мы предлагаем Вам"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:65
msgid "вместе с нами развивать сервис и стать партнером"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:71
msgid "Читать подробнее"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:83
#: .\templates\blocks\static_pages_blocks\b_contacts.html:3
msgid "Мы всегда на <span class=\"color_title\">связи</span>!"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:86
#: .\templates\blocks\static_pages_blocks\b_contacts.html:5
msgid "У вас есть вопрос? Отправьте нам сообщение"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_about_service.html:130
#: .\templates\blocks\static_pages_blocks\b_contacts.html:49
#: .\templates\blocks\static_pages_blocks\b_customer_service.html:49
msgid "Отправить сообщение"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:14
msgid "Почему это выгодно?"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:18
msgid "Полное брендирвоание страницы или раздела"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:22
msgid "Реклама для целевой аудитории"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:26
msgid "Индивидуальное согласование цены при объеме"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:30
msgid "Более чем 10 000 уникальных посещений в день"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:34
msgid "Высокая эффективность"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:40
msgid "Стоимость"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:41
msgid "Оставьте заявку и получите персональное коммерческое предложение"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_advertisement.html:59
#: .\templates\blocks\static_pages_blocks\b_partners.html:68
msgid "Оставить заявку"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_customer_service.html:5
msgid ""
"Пожалуйста опишите Ваш вопрос максимально подробно, а также укажите Ваш e-"
"mail для обратной связи."
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:12
msgid ""
"Путешествуй по миру и <span class=\"color_title\">перевози</span> посылки!"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:100
#: .\templates\pages\p_main.html:99
msgid "Найдите перевозчика"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:103
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:19
#: .\templates\pages\p_main.html:148
msgid "Разместите объявление"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:107
#: .\templates\pages\p_main.html:101
msgid ""
"Зайдите на сайт Trip With Bonus и в форме вверху страницы, заполните данные "
"для поиска перевозчика."
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:111
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:20
#: .\templates\pages\p_main.html:150
msgid ""
"Укажите откуда, куда хотите перевезти посылку, а также Вашу дату отправки и "
"прибытия. При желании Вы можете указать дополнительные параметры: тип, вес, "
"вид перевозки и т.д"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:117
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:26
#: .\templates\pages\p_main.html:105 .\templates\pages\p_main.html:156
msgid "Свяжитесь с перевозчиком"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:120
#: .\templates\pages\p_main.html:106
msgid ""
"Откройте контакты на сайте и договоритесь о месте встречи и условиях "
"перевозки"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:124
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:27
#: .\templates\pages\p_main.html:161
msgid ""
"В отобразившемся списке выберите подходящего отправителя и посылку, откройте "
"контакты и свяжитесь удобным способом. Если не нашли подходящего "
"отправителя с посылкой, разместите объявление о возможности перевезти "
"посылку и отправители Вас сами найдут"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:130
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:32
#: .\templates\pages\p_main.html:111 .\templates\pages\p_main.html:167
msgid "Передайте посылку"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:132
#: .\templates\pages\p_main.html:113
msgid "Встречайтесь, знакомьтесь и передавайте посылку"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:136
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:33
#: .\templates\pages\p_main.html:169
msgid ""
"Обсудите с отправителем все условия: время, место и прочие детали. Готово! "
"Доставьте посылку из пункта А в пункт Б и получите благодарность отправителя!"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:156
msgid "Тарифы"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:202
msgid "Оформи <span class=\"color_title\">подписку</span> сейчас и получи"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:203
msgid ""
"<span class=\"color_title\">1 день </span>пользования сервисом <span class="
"\"color_title\">в подарок!</span>"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_mover_search.html:205
msgid "Получить"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_partners.html:5
msgid "Станьте <span class=\"color_title\">партнером</span> Trip With Bonus"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_partners.html:50
msgid "Стань нашим партнером"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_partners.html:51
msgid "Оставь заявку и получи пресональное предложение о партнерстве"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:5
msgid "Путешествуй по миру и перевози посылки"
msgstr ""
#: .\templates\blocks\static_pages_blocks\b_send_parcel.html:6
msgid "Общайся, перевози посылки и получай бонусы"
msgstr ""
#: .\templates\pages\p_main.html:20
msgid "Сервис, который позволяет передавать посылки с путешественниками"
msgstr ""
#: .\templates\pages\p_main.html:21
msgid "без посредников. Знакомьтесь, встречайтесь и передавайте посылки."
msgstr ""
#: .\templates\pages\p_main.html:30
msgid "Автоперевозки"
msgstr ""
#: .\templates\pages\p_main.html:34
msgid "Авиаперевозки"
msgstr ""
#: .\templates\pages\p_main.html:58
msgid ""
"TWB - это сервис, созданный для того, чтобы отправитель и перевозчик нашли "
"друг-друга!"
msgstr ""
#: .\templates\pages\p_main.html:59
msgid "Наш сервис предлагает вам прямые контакты, а не является посредником !"
msgstr ""
#: .\templates\pages\p_main.html:67
msgid "Как отправить?"
msgstr ""
#: .\templates\pages\p_main.html:69
msgid "Как перевезти?"
msgstr ""
#: .\templates\pages\p_main.html:75 .\templates\pages\p_main.html:123
msgid "Вам нужно отправить посылку быстро и недорого?"
msgstr ""
#: .\templates\pages\p_main.html:183
msgid "Trip With Bonus это комфорт и эффективность!"
msgstr ""
#: .\templates\pages\p_main.html:188
#, python-format
msgid "+5%%"
msgstr ""
#: .\templates\pages\p_main.html:189
msgid "рост путешествий ежегодно"
msgstr ""
#: .\templates\pages\p_main.html:190
#, python-format
msgid ""
"В среднем на 5%% растёт количество путешествий ежегодно. Просто путешествуй "
"и получай бонусы."
msgstr ""
#: .\templates\pages\p_main.html:198
msgid "в 3 раза"
msgstr ""
#: .\templates\pages\p_main.html:199
msgid "быстрее других сервисов"
msgstr ""
#: .\templates\pages\p_main.html:200
msgid ""
"Почтовые сервисы доставляет посылки в среднем за 10 дней. С нами - быстрее!"
msgstr ""
#: .\templates\pages\p_main.html:208
msgid "+142"
msgstr ""
#: .\templates\pages\p_main.html:209
msgid "заявки ежедневно"
msgstr ""
#: .\templates\pages\p_main.html:210
msgid "На перевозку или отправку посылок в разные уголки мира"
msgstr ""
#: .\templates\pages\p_main.html:219
msgid "30+"
msgstr ""
#: .\templates\pages\p_main.html:220
msgid "стран"
msgstr ""
#: .\templates\pages\p_main.html:221
msgid "С TWB отправляй посылки по всему миру! С нами нет границ!"
msgstr ""
#: .\templates\pages\p_main.html:232
msgid "Частые вопросы"
msgstr ""
#: .\templates\pages\p_main.html:236
msgid "Как пользоваться сервисом?"
msgstr ""
#: .\templates\pages\p_main.html:240
msgid "Как разместить объявление о посылке/перевозке?"
msgstr ""
#: .\templates\pages\p_main.html:246
msgid "Как оплатить лицензию?"
msgstr ""
#: .\templates\pages\p_main.html:247
msgid ""
"Однозначно, явные признаки победы институционализации ограничены "
"исключительно образом мышления. Следует отметить, что новая модель "
"организационной деятельности обеспечивает широкому кругу (специалистов) "
"участие в формировании как самодостаточных, так и внешне зависимых "
"концептуальных решений. "
msgstr ""
#: .\templates\pages\p_main.html:252
msgid "Последние новости"
msgstr ""
#: .\templates\widgets\w_route_info.html:50
msgid "перевозчика"
msgstr ""
#: .\templates\widgets\w_route_info.html:53
msgid "отправителя"
msgstr ""

View File

@@ -2,12 +2,16 @@ Django==4.2.2
django-ckeditor==6.5.1
psycopg2-binary==2.9.6
requests
Pillow
django-modeltranslation==0.18.10
overpass
geopy
channels==4.0.0
daphne==4.0.0
channels-redis==4.1.0
django-colorfield
django-webpush==0.3.5
django-allauth==0.60.0
pytz==2024.1
celery==5.3.6
djangorestframework==3.14.0
python-decouple==3.8

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