Compare commits
2253 Commits
8eea53afc0
...
v2_test2
| Author | SHA1 | Date | |
|---|---|---|---|
| e74731b789 | |||
| 8694856fc9 | |||
| 3d2b70609d | |||
| dd260e229a | |||
| 0bc8f85bee | |||
| 496534f76a | |||
| d681f6739b | |||
| 64de7ea34c | |||
| 25c913c572 | |||
| f34a54a33c | |||
| b511cbbdaf | |||
| 17c040f0d3 | |||
| f4a1da9ded | |||
| 79de035ae7 | |||
| ddc844456d | |||
| fef72df927 | |||
| 134216c5f5 | |||
| 685c201840 | |||
| fd343f098e | |||
| 053d7f79da | |||
| 60a088236b | |||
| 768d82a026 | |||
| 1c51c7fbf5 | |||
| 2196890105 | |||
| 4bf27702a4 | |||
| 4bf65b6eef | |||
| 98c6622e1f | |||
| e0baa07c4f | |||
| e2d951ccbf | |||
| cdff6c1966 | |||
| 00fe34bc0e | |||
| 910ea908c3 | |||
| 017d3b18ef | |||
| 61efc14b9a | |||
| f3a28ddb30 | |||
| 1dc8a64f9f | |||
| 56371267bc | |||
| a68fe3970f | |||
| 217b1d89ea | |||
| 4f2e3776ed | |||
| 3c6f90f103 | |||
| e3fd8457c0 | |||
| 8119955f52 | |||
| b4b79e185e | |||
| fb07005e9a | |||
| cd7bfa07bb | |||
| 48a21b319a | |||
| 808d44555c | |||
| f7857cb5c2 | |||
| 15b5911013 | |||
| 8e6111dfb1 | |||
| c476fa6d77 | |||
| c59c5d929b | |||
| e1053073a1 | |||
| 0a56555f35 | |||
| 1188b45c20 | |||
| 97ffcf417e | |||
| 5801e10f80 | |||
| 3c08686f21 | |||
| eff021b4e2 | |||
| 8e2f5f6bac | |||
| 05798c4b49 | |||
| 876d91de01 | |||
| a67436b854 | |||
| 6e8db45cb8 | |||
| 51abe338c0 | |||
| 84aecc715e | |||
| 93764163d4 | |||
| 9e3888bccf | |||
| 293b7c9f6f | |||
| 70dd911445 | |||
| 8496a0844c | |||
| fe110b1eb9 | |||
| 0407cadd50 | |||
| b825504063 | |||
| ec55fa787f | |||
| cd55111003 | |||
| ee31eb045f | |||
| 56bcd5bee9 | |||
| b5155e7ce2 | |||
| ca05de3d7d | |||
| 04af851875 | |||
| 4c7126f414 | |||
| e217506627 | |||
| 6e0c5d1662 | |||
| 15143188b7 | |||
| da9dc4bb03 | |||
| ad34c1b670 | |||
| bb691a6ad0 | |||
| 2c00a38ceb | |||
| 8291f956c2 | |||
| a8335f10ee | |||
| db135af725 | |||
| feb2b9d697 | |||
| 2f0e1bb396 | |||
| 2a7a666f7d | |||
| 9bfcb00861 | |||
| 06eba5ff25 | |||
| 3c1f03de26 | |||
| 01ffddf9ef | |||
| b3f4eec2e9 | |||
| 34ef2f23b7 | |||
| 529b324817 | |||
| b3e8d75539 | |||
| 81e0812c66 | |||
| e667e5a0c9 | |||
| 6f9bfb38f5 | |||
| 6656457bcc | |||
| 07ee30f360 | |||
| 5f4ea5910d | |||
| c568494a8e | |||
| fd211c464f | |||
| ff58c07ca6 | |||
| 2ea6d61d39 | |||
| c5214d830f | |||
| 8f3bba620c | |||
| 9116b9efe1 | |||
| 575ee074b1 | |||
| 2d650cd51f | |||
| 4fab7ba2c0 | |||
| 72c658c594 | |||
| 89058ed264 | |||
| c247bfcefa | |||
| 74742c3e8e | |||
| 33239e3c90 | |||
| 97be421ee3 | |||
| 8484e48c56 | |||
| c07bf2538d | |||
| 8ac11d6234 | |||
| a0b08a9a94 | |||
| 9bc6d488f1 | |||
| b7a830ab6f | |||
| 7b915f0c6c | |||
| a68d469eda | |||
| a5b106edfe | |||
| a40ae5d37d | |||
| 4b57ad3f8b | |||
| cdedebd9f6 | |||
| c237875732 | |||
| 7f7a3d19fb | |||
| 1db2b0edb9 | |||
| 9280025f1c | |||
| 9666c9dbf4 | |||
| aac673f212 | |||
| 9a2a2d9aa2 | |||
| 9e8a5d1f37 | |||
| e938d5915a | |||
| b35ff27654 | |||
| cfe1a9d585 | |||
| 04f097c1a9 | |||
| c81448e43d | |||
| 6947d1c958 | |||
| ee803a94e2 | |||
| b2a76abe01 | |||
| 0b03bdc81a | |||
| eabfe3ab27 | |||
| 7357e750ae | |||
| 4231e85a8e | |||
| 1324c63d20 | |||
| 3f0fffda46 | |||
| d397e765df | |||
| 8f43ede3f0 | |||
| e09743b474 | |||
| 4c96c94549 | |||
| 66e00af171 | |||
| 0ad3f2eb9a | |||
| b8ee9697f6 | |||
| 0ac84ed6e0 | |||
| 72f04e0d48 | |||
| 75a312c1d6 | |||
| 446aa9a0e8 | |||
| 48f8d1b08e | |||
| 2b809ae9a6 | |||
| a76663ea81 | |||
| ed1383622f | |||
| 5ea5529c67 | |||
| 04c4e8c791 | |||
| c94c1a9202 | |||
| 0a67a1bb04 | |||
| 6274190e09 | |||
| 7d00e92aac | |||
| 951315a1a4 | |||
| 1e6c70122f | |||
| b867ab7d02 | |||
| 6044b302bb | |||
| e56d29c322 | |||
| a140ffe523 | |||
| b6c56b4890 | |||
| ab767233ec | |||
| 4b13a7ed02 | |||
| 7ba65e73ae | |||
| 4a58a47a8e | |||
| e8506e1b4e | |||
| 61805df66b | |||
| e98d426324 | |||
| 6c062ce3c8 | |||
| 8f47d4fb65 | |||
| 63e507d0ff | |||
| 5a16466766 | |||
| 4b0c923ec0 | |||
| 87f67aa24a | |||
| 93e48f3cbc | |||
| baadc67fcf | |||
| bc16aea3a4 | |||
| 8041e3eb4d | |||
| 3e4c0b2ab5 | |||
| 960c9ba5b0 | |||
| 3efec91d74 | |||
| 9e48c72245 | |||
| 4dac9a5eec | |||
| bf25c67f1e | |||
| 1245899c07 | |||
| 66f25b6937 | |||
| 2d44edf5ab | |||
| 7062c756b6 | |||
| 7c173c9195 | |||
| e070ab15ac | |||
| 400498c6fa | |||
| 3c5331c2d3 | |||
| 800b7ba493 | |||
| bc2689d1cf | |||
| ce9be11c0a | |||
| 43ba192496 | |||
| ee410315ee | |||
| f3c78f0575 | |||
| b98a4a5364 | |||
| 68aad97e46 | |||
| 8de6676b40 | |||
| 7a345a6c80 | |||
| e3adbeeea4 | |||
| b0db3534f2 | |||
| 6f646264dc | |||
| d83940b47d | |||
| 4996d02f71 | |||
| da6f47f439 | |||
| 54fbc3f4b2 | |||
| 6e2c54eccf | |||
| 89166100e3 | |||
| 3d28a1ee86 | |||
| 034d5faa0d | |||
| f58038ab90 | |||
| ad25cc67ec | |||
| 384d1e8aec | |||
| b0a1007a76 | |||
| 0419ba7847 | |||
| c52dbca9a1 | |||
| 43153a9fdf | |||
| c23dd1ff4c | |||
| 67d9528614 | |||
| 1151744247 | |||
| 9dd3fbe47d | |||
| f42e155f2b | |||
| f5a084684a | |||
| b22adac365 | |||
| a329720f1c | |||
| e03f4a015a | |||
| 94f677ae6f | |||
| 8afdfc87c5 | |||
| cc3739f701 | |||
| dbee8dabb1 | |||
| cfb7acfddb | |||
| e14072e829 | |||
| f80f8d8c7e | |||
| e9b3dc1da9 | |||
| 63137188e5 | |||
|
|
07031d693c | ||
| 531955bdc7 | |||
| 2cfa314c3c | |||
| cfe37ec57d | |||
| 995733d6e3 | |||
| aec622831d | |||
| e8f092a88b | |||
| a1da574c04 | |||
| 08e28d14c9 | |||
| cffd810184 | |||
| 774ee85f8d | |||
| a1c6db28a8 | |||
| bcd0c8900d | |||
| f3e3b608f1 | |||
| a839114cdf | |||
| a315a52791 | |||
| fc0d9f07c0 | |||
| 26b4b77271 | |||
| 09cfe8938f | |||
| 8989d13928 | |||
| a39c96cc61 | |||
| db8beca609 | |||
| 94e7881aa0 | |||
| e4a07e2c88 | |||
| 1414003e3f | |||
| b9f4804d74 | |||
| e2b63f6706 | |||
| 393f165095 | |||
| 547e4e8f9f | |||
| ffeab227c9 | |||
| 4452398773 | |||
| 454f4711f3 | |||
| 46c456af2c | |||
| 2f459e982a | |||
| b8d934fa28 | |||
| 1107c01d5c | |||
| b8ca85c20e | |||
| a29cf29041 | |||
| 05bf1d59df | |||
| 2cf129d0b9 | |||
| 488cd72f2c | |||
| 6fab07fd79 | |||
| 382b2d122c | |||
| b4beac26a7 | |||
| 86d78fdd06 | |||
| 92ad985355 | |||
| eaf94f0ceb | |||
| acee0ccec5 | |||
| fddea097d3 | |||
| 7dc25532a3 | |||
| 19721cf5da | |||
| a6d9d1595a | |||
| 5e0d5f4b91 | |||
| 09a6de1544 | |||
| a20b502b70 | |||
| 8bb8013f67 | |||
| 94c7c54cc1 | |||
| c257d22618 | |||
| b57dbb75e8 | |||
| 9781f33440 | |||
| 45b4236748 | |||
| 8b0c82c3ca | |||
| 5518a99ee9 | |||
| 63124a350e | |||
| c7de08ca49 | |||
| 41e4b23317 | |||
| b23f379db0 | |||
| 1faf9e0cc7 | |||
| 90e1a87bf4 | |||
| 9ec8d4189f | |||
| 8f08e360ef | |||
| 4371cd1a85 | |||
| f6ba0ab1ad | |||
| c60b9942f6 | |||
| 85f6e69d63 | |||
| 83aeab9341 | |||
| 8a539a2868 | |||
| 379db1cb39 | |||
| e51b23cb91 | |||
| e824a258ea | |||
| 526f035659 | |||
| 42c2843cab | |||
| a99ffcc9d2 | |||
| 40a2412b9b | |||
| cd2985a170 | |||
| cb84352475 | |||
| a19b4fe212 | |||
| f718df47c8 | |||
| 7620f97042 | |||
| 3bfb7d040d | |||
| aea50ed806 | |||
| 7dab17905b | |||
| a3a3565189 | |||
| 3f82f42e1e | |||
| 0de00c5a34 | |||
| 0daee07086 | |||
| 2619539415 | |||
| aa8bed6efb | |||
| 9b01d79955 | |||
| 17f21a25bb | |||
| 8c523e8c65 | |||
| d7956e24a6 | |||
| eaafbec1ea | |||
| 4ba5f6492f | |||
| 4355586afc | |||
| ed4dce4bf2 | |||
| a8c4d22b51 | |||
| 92f08b5174 | |||
| a7128908f4 | |||
| d86a531495 | |||
| f894cec38b | |||
| 0ef2f9aeb1 | |||
| 312a706d00 | |||
| d1ad6cfa24 | |||
| 2bc9aa7a1c | |||
| 3a497d09df | |||
| 1301af9d84 | |||
| 822a029104 | |||
| 257ae371f9 | |||
| ec1d52f694 | |||
| 46c7b6327d | |||
| 72d8ae6616 | |||
| b905fab6c5 | |||
| 3b10299249 | |||
| 2098032d81 | |||
| d37b694d5a | |||
| 345969fd61 | |||
| 632ff3c812 | |||
| bb60f9d914 | |||
| 111a7eaff7 | |||
| 61161e6335 | |||
| 327681d3db | |||
| 68c5bfe275 | |||
| 10814e9e5f | |||
| 617f90e1ac | |||
| 81af5990ae | |||
| 92e9b1ce63 | |||
| 87b21c5f55 | |||
| f61482ded6 | |||
| c999d994d6 | |||
| f609361ee9 | |||
| c3f116fbc3 | |||
| 79059796c8 | |||
| 61facdb5fb | |||
| b007c3ad23 | |||
| 0b5e37281b | |||
| 6e5e82401b | |||
| d419131eab | |||
| b8b3c19bfc | |||
| e95062c6eb | |||
| 2f4c472a41 | |||
| 3f0f1d4408 | |||
| f23ad1a33a | |||
| 1486879a58 | |||
| 3d6fb38937 | |||
| d981a83652 | |||
| 3bfe64d57c | |||
| 1ad58b05ef | |||
| 225a958cb4 | |||
| 0ac8e906a2 | |||
| a42f3c7e71 | |||
| ddf8428884 | |||
| 807edecbf4 | |||
| 6254b1dcda | |||
| 9df1bf2743 | |||
| a678fb467d | |||
| c24b7107a7 | |||
| c0596871da | |||
| f0ad2d654d | |||
| c2ef76b69b | |||
| 96f58732ba | |||
| 15340d254c | |||
| 890986c5de | |||
| 3d137aefad | |||
| 6c911dde43 | |||
| 37981602dd | |||
| d64d858f15 | |||
| f9906ea8fc | |||
| c660831146 | |||
| a8ba3f9a3a | |||
| 8439fc8736 | |||
| 04f7fbdbb2 | |||
| e2b205240e | |||
| 2695738ae3 | |||
| 5a3336a657 | |||
| b67b290279 | |||
| f34664cbb3 | |||
| 4919560500 | |||
| c2b6890311 | |||
| 38c4f3426d | |||
| 2400c334a9 | |||
| 114675703b | |||
| 0be38ac466 | |||
| 0e184f0b87 | |||
| 75258f1706 | |||
| ce591345d3 | |||
| 0770009443 | |||
| bf39639acc | |||
| 947f46c5ff | |||
| 651423ce90 | |||
| afb0ea0bed | |||
| e2b7b0d356 | |||
| c55d5c6676 | |||
| eb60bcec56 | |||
| 62325eb9fa | |||
| 18aae945ae | |||
| 4afe61546a | |||
| b6a0fcdf07 | |||
| 2fc153843f | |||
| 729f03ca47 | |||
| 96c0111428 | |||
| d0ccbb494f | |||
| f8aa0162a5 | |||
| 2b94d3f851 | |||
| fddda1ef0a | |||
| 035634df67 | |||
| 8267d99b4a | |||
| 854faf56f5 | |||
| 4bbb3428c0 | |||
| 7a40ca0986 | |||
| c93a15025b | |||
| 57a1c82db2 | |||
| b6af2c7997 | |||
| df7b3e1a8b | |||
| b7a6163251 | |||
| 9c39f9e07c | |||
| d33eff4935 | |||
| 11ea66f576 | |||
| d31f019afb | |||
| 52361a6a16 | |||
| e04ec83877 | |||
| 301f301868 | |||
| b2cb5463ef | |||
| d2754db97a | |||
| 568906b2df | |||
| fbae553657 | |||
| 03ecd45316 | |||
| b8aba1d951 | |||
| 4d76569131 | |||
| f90d87a301 | |||
| fe6375b6f9 | |||
| a30156cc1c | |||
| f8a8aa7577 | |||
| b527d18a3c | |||
| fc6a864f7c | |||
| 8eaff8ebbc | |||
| 3a18688143 | |||
| 0e40bae701 | |||
| 7aa7e32fda | |||
| 1aced3d20b | |||
| 9566082283 | |||
| 152c92bf65 | |||
| 4622424b9b | |||
| ef8337582f | |||
| 133fa7ea5f | |||
| 74e76fe6e7 | |||
| 2996e84433 | |||
| f8d1de5d30 | |||
| d33278faff | |||
| 22e40409d7 | |||
| 632cf0b9b4 | |||
| a9ed3e0bef | |||
| 28b505310d | |||
| c9aea0b778 | |||
| e1694cdc9c | |||
| 3a235b4f60 | |||
| 90405f64de | |||
| 35073f77c8 | |||
| 5cd0146c8c | |||
| ffabffb2ef | |||
| bb1e3e9691 | |||
| d89730ffbf | |||
| 431ab2f9b0 | |||
| c444055bd7 | |||
| a0dd229c12 | |||
| 074e141a22 | |||
| 40951fdfcb | |||
| e319a8af46 | |||
| a83ff0e080 | |||
| 22ae4b1563 | |||
| 23fe1c3647 | |||
| 079a7d3925 | |||
| 69373925cd | |||
| dbfc39639b | |||
| 11bbfd0e73 | |||
| 0075b474d7 | |||
| 99090a8a95 | |||
| 0e5ed13794 | |||
| ffa6ba2c47 | |||
| 4ae8f09430 | |||
| 17024d7350 | |||
| efec0754cd | |||
| 5a893faa43 | |||
| 5981f982f3 | |||
| fd81675ee9 | |||
| 3575391d3f | |||
| 563a4fde3c | |||
| 148d182e5e | |||
| aab8cf69fb | |||
| 3900cb1382 | |||
| d77fe01795 | |||
| 47eb056f87 | |||
| 14691d0ee4 | |||
| d6487d81c9 | |||
| 2f421bb0ac | |||
| 4b588eb6e4 | |||
| 3a911d928f | |||
| 69c037b30d | |||
| c84e884a7c | |||
| 0a38314003 | |||
| 2d86f36a91 | |||
| a0eeb7b642 | |||
| 9eb5936690 | |||
| cbe5271426 | |||
| 34827af5be | |||
| 6ed5652f16 | |||
| 8ed567496b | |||
| b374e7aeca | |||
| a0ee72bc81 | |||
| e8e4bf466b | |||
| ec01d11426 | |||
| c088d25033 | |||
| e9f3cdbc59 | |||
| 72f754e89c | |||
| c365f3274a | |||
| f8caafb2a9 | |||
| 2d147d5f09 | |||
| 7c4b554aea | |||
| 0091919b93 | |||
| d46e5cc238 | |||
| 97116a2070 | |||
| 27aaff4a4b | |||
| 32c2fd4e6c | |||
| 51b86323d8 | |||
| f0bd3ce088 | |||
| 474bb9f02e | |||
| a2175f9110 | |||
| aec0218787 | |||
| 567ee01619 | |||
| e4a929cfd6 | |||
| 27b0840704 | |||
| 525da42ae9 | |||
| 8d1199bc4f | |||
| dac8f602d8 | |||
| 26d04dfaa0 | |||
| 22306371a0 | |||
| 881f0cdbfa | |||
| 314d342f5c | |||
| d627b6a780 | |||
| 9528572f37 | |||
| 273caa5c5d | |||
| c9f04057a7 | |||
| 8599030ccb | |||
| 3fe5970811 | |||
| 723229d595 | |||
| bb319780b6 | |||
| 5c06aceb27 | |||
| 27b4dd072e | |||
| 2c1f70018e | |||
| 698f4578fc | |||
| fd70e388a7 | |||
| 07b06a4177 | |||
| 10e8d477e5 | |||
| 7daadc816c | |||
| 89db74c782 | |||
| 01f6136798 | |||
| 1bb4aa04a6 | |||
| 6b63f8b87e | |||
| cd6fde962b | |||
| 607a0f8245 | |||
| ad1db581a7 | |||
| ca5b0039a1 | |||
| 214ee3bd58 | |||
| b1f2cc2376 | |||
| f1f523209b | |||
| 9c23fd02fe | |||
| eee30b50db | |||
| 6ba35ccd96 | |||
| 5916d5d038 | |||
| 3eb0b499c6 | |||
| c2d021a516 | |||
| 46fdda269f | |||
| 536a1e967e | |||
| ac4fcb634d | |||
| d92fe4c22c | |||
| 3d49100656 | |||
| 9841524ec0 | |||
| 0fe8cb1bfb | |||
| 910b211840 | |||
| 4f7d6d05bd | |||
| b8feec88a7 | |||
| 6043e1f5a7 | |||
| 4981bdcaa6 | |||
| c29cc25581 | |||
| 0ce03ca73f | |||
| 80a226a302 | |||
| 0d41bd40e5 | |||
| d2fe96a599 | |||
| d0d41a4d2b | |||
| 5e7a153d6d | |||
| 55d414c1d0 | |||
| ac6a440151 | |||
| ff48ca83e2 | |||
| 358577745f | |||
| 087a913a48 | |||
| a9dedb7869 | |||
| 92e3d76b7e | |||
| 2da7195dd0 | |||
| 18f7fedbc2 | |||
| aa93813ba5 | |||
| def3d770ed | |||
| 6b8bcb8ebb | |||
| ff5afe518a | |||
| 6f42251f5f | |||
| 06917078ae | |||
| 4331b26542 | |||
| dd8a8ddd53 | |||
| abb0d488eb | |||
| be97b848d0 | |||
| 6bc112d689 | |||
| dc534a0c51 | |||
| e71ed05e6c | |||
| 2048ed6baf | |||
| c146bd6155 | |||
| 98665abf4d | |||
| 9954140d3a | |||
| fafee63a64 | |||
| c623d69767 | |||
| 670eb28bc0 | |||
| 144ff286f6 | |||
| 05f94de0b4 | |||
| c4650ce603 | |||
| 3971a8ee23 | |||
| a3ba6bc783 | |||
| b87df02714 | |||
| 713695cf7d | |||
| a25f30eda7 | |||
| eddb3a1858 | |||
| 5a89200f1d | |||
| 2f47a9e3db | |||
| 155b6272ec | |||
| 1479584bfc | |||
| f86be5bd97 | |||
| c87a7095ad | |||
| ac4df7a5f7 | |||
| c48839ff8c | |||
| ea296e3f05 | |||
| 8124ed62fe | |||
| cc643d2641 | |||
| 6de42c5ba9 | |||
| 36b7f4dee7 | |||
| 2328f09023 | |||
| 60faeeace9 | |||
| abe53dd88b | |||
| c76a18c5ff | |||
| 3725ce1882 | |||
| 6a69ff02b1 | |||
| 6ba41af305 | |||
| 5815a08b55 | |||
| 7805161829 | |||
| 6fa31f3866 | |||
| 0763faf224 | |||
| a3d3e12467 | |||
| ab22a3ec88 | |||
| 9d0a059909 | |||
| 1011f112b2 | |||
| d07ab2c71f | |||
| 96bfef04a8 | |||
| 779dd7e93d | |||
| 0abcb34829 | |||
| 33c028207a | |||
| 64b3e40ed0 | |||
| 0023676b28 | |||
| b90039f21a | |||
| b5f24ebf2e | |||
| f99a010e4a | |||
| a4944360a6 | |||
| a2b5d81c98 | |||
| 13b7c2572d | |||
| 47b12882ba | |||
| c008aa585b | |||
| 406b5e8480 | |||
| 584a196784 | |||
| 618f7751d1 | |||
| faaea1129a | |||
| dd7afc28f7 | |||
| 9ecf5ce073 | |||
| 422373f00e | |||
| 60636daeb8 | |||
| 9d47f4c2bc | |||
| 4d6dbddd28 | |||
| 3f00ff39d2 | |||
| bff6a81586 | |||
| 9cb8036d3c | |||
| a43de1fa91 | |||
| 93717bee2d | |||
| 8db1d6fdce | |||
| b41f8c7eca | |||
| c19405d32a | |||
| 7b75c533a8 | |||
| 649dbab901 | |||
| 1a4498d19f | |||
| 2786ef454d | |||
| 961c0dd2a5 | |||
| 0a0835d3a6 | |||
| bc87d10d59 | |||
| 792693848b | |||
| de229c5f78 | |||
| 03f9a836e6 | |||
| 91751574cc | |||
| d5453dada6 | |||
| 22adb18a39 | |||
| 0a3c40dd5d | |||
| b29825d62b | |||
| c9bda8aab2 | |||
| 284a4b064a | |||
| 6f20f53d75 | |||
| 473a047af9 | |||
| 481f9a881f | |||
| 6ceedddbc1 | |||
| bd9e0fad48 | |||
| 6b118d00b6 | |||
| 01e221196f | |||
| ccf016999b | |||
| cb79e47796 | |||
| 4076bd6065 | |||
| e54c258007 | |||
| c3b7401255 | |||
| 9af72d30f4 | |||
| dbe948ae5f | |||
| 885d36a90c | |||
| 7acfbc0113 | |||
| 43c5faf988 | |||
| 437142fba0 | |||
| acf7b702ee | |||
| 74ca1be884 | |||
| 26b987d6ce | |||
| 3f7f1d88ed | |||
| 59225b1688 | |||
| 108aee2c43 | |||
| 9062ce32a1 | |||
| 7c74a0f3fe | |||
| 4fb3a12f47 | |||
| 59bc25f31c | |||
| 87fbe7852c | |||
| 793d283d97 | |||
| 651ae18345 | |||
| 67eb32968e | |||
| c6d41513c0 | |||
| 67ba698b60 | |||
| 15d1c00ac6 | |||
| 9e9a82ffc0 | |||
| a0eb5210ab | |||
| 3dacc0316e | |||
| e2c347c912 | |||
| 758f8b1f55 | |||
| b446a8f519 | |||
| c8ba0dd770 | |||
| f13a1329ca | |||
| 4b3604098f | |||
| defbf6746f | |||
| bf3f26ec4f | |||
| 76018333b0 | |||
| e6e345b9fe | |||
| eafccabb66 | |||
| f2e0628de1 | |||
| 2e1f3a10ab | |||
| bf18c96dbd | |||
| 23718a5f3f | |||
| c6a1d5bdcf | |||
| f75e94f706 | |||
| 11c9bb6c23 | |||
| b1ff9c47da | |||
| 35c09dc70b | |||
| f0efae5987 | |||
| 65120cd2d4 | |||
| 231d062814 | |||
| 615922a881 | |||
| 61b2b824d5 | |||
| 317445998a | |||
| 4ad3813499 | |||
| 9a88243323 | |||
| df0f32c71a | |||
| 4ddd402442 | |||
| a05c4b3898 | |||
| a3ab0973c9 | |||
| 0f801aa44b | |||
| 7bc386bc44 | |||
| e148c60d70 | |||
| 4667352ec4 | |||
| e7cf694d88 | |||
| b2abb3046b | |||
| 6c1011e59e | |||
| ad909b98bf | |||
| 9d2e35246a | |||
| 48dc573c0f | |||
| e559660912 | |||
| aa3bec304a | |||
| c7bc22813f | |||
| 46e73db10a | |||
| ad451c2ae0 | |||
| 0b5b557a35 | |||
| fb9228c432 | |||
| fbcbe93042 | |||
| ca44deb077 | |||
| 61403bfac2 | |||
| 190efa4b74 | |||
| ca06836b11 | |||
| b31241872f | |||
| f80e2844bf | |||
| 1dae86f0e7 | |||
| 0c146caeef | |||
| 410733211b | |||
| 14df8969c2 | |||
| 4388414ac2 | |||
| 791ac8d436 | |||
| a3d6f498b1 | |||
| b8fdd61948 | |||
| 4437372d7c | |||
| f8d29d80b7 | |||
| 36fd9599af | |||
| 65eccde487 | |||
| 037b4cc562 | |||
| 9c971a6fa4 | |||
| a3faa17754 | |||
| 085f905125 | |||
| dc16ced786 | |||
| d5cba26098 | |||
| 001dd2cb87 | |||
| 16e860a29f | |||
| 6a5331d8eb | |||
| aff0b0fe98 | |||
| fd3612c370 | |||
| d14a46d3d7 | |||
| d7ace77de8 | |||
| 885e4722af | |||
| 72ed6369d8 | |||
| b23e440efd | |||
| 75ddb002fd | |||
| 28a36335ce | |||
| fba225aa70 | |||
| af800ac84c | |||
| 87e90d7152 | |||
| c25942a6ca | |||
| 692419816f | |||
| 3a109340fd | |||
| 5925ecb975 | |||
| 325acd3580 | |||
| 3ac85784a9 | |||
| 5c4e715970 | |||
| 89b57feb4e | |||
| fc194d3f85 | |||
| 12af0ea238 | |||
| 6e758cf62e | |||
| f7783c070b | |||
| fb3cd30db4 | |||
| ff949a8205 | |||
| 624653f581 | |||
| f3e5f02f07 | |||
| d0792e9e84 | |||
| f752eb5d4a | |||
| f0861fde84 | |||
| 4e7aa09c21 | |||
| e68a0dc151 | |||
| fc37bea98a | |||
| 8a91e611ac | |||
| 7208db6981 | |||
| 447a78cd3a | |||
| 95dff519b8 | |||
| 62aeebae95 | |||
| c2e03728f8 | |||
| a64fbab270 | |||
| 00ea16f631 | |||
| e3aafcdfe9 | |||
| 421a3a2dab | |||
| 61e4f7e450 | |||
| 3b52cab162 | |||
| fe9be61772 | |||
| 0818f880cc | |||
| cca062efec | |||
| 2eb7ac85d3 | |||
| 3469a3923c | |||
| 20fb49b3e4 | |||
| afe8feebc2 | |||
| ae6a68d85b | |||
| d80c45acd5 | |||
| 3a9ac2c289 | |||
| 2d68836724 | |||
| 0deb85b5a3 | |||
| 7b38f6e3b3 | |||
| 43f050f070 | |||
| 098b7c4fb5 | |||
| eae56199e0 | |||
| 71e8f0f465 | |||
| 66d7183278 | |||
| 92b92f2d65 | |||
| d5ab7d82eb | |||
| 1ebd9f9c0b | |||
| 7fd2c60ddb | |||
| f068182557 | |||
| 02945b0691 | |||
| 21b959e6a3 | |||
| 747091c744 | |||
| 7b70052ff1 | |||
| 49fd26bc4c | |||
| 77f365ef45 | |||
| 6d34d46e38 | |||
| 970c2e0837 | |||
| b87549d567 | |||
| 78abdb2fef | |||
| f73ebb08c9 | |||
| 628e8eec09 | |||
| 66ddcaec5b | |||
| 3901c82aae | |||
| 9f682b66a5 | |||
| c74aff7b91 | |||
| 1be50b1277 | |||
| af00d1a54b | |||
| dd4ad82248 | |||
| 38c08cbced | |||
| ed2c74a280 | |||
| 90ab1bb6b4 | |||
| 3e2220e5f2 | |||
| 33e27620b1 | |||
| fbdef2a1ec | |||
| 1834639b50 | |||
| f9606a3c88 | |||
| c803d7abbb | |||
| efd75817bc | |||
| 36fff15e2b | |||
| 7573bfb344 | |||
| 0c33d638ba | |||
| 7d3da34e22 | |||
| 0e2723a367 | |||
| b102c44031 | |||
| 4b480c35e5 | |||
| 8e7bcd8fd8 | |||
| c60c545031 | |||
| 51d075f799 | |||
| db54ec1650 | |||
| 14b362442f | |||
| fcfbeece87 | |||
| aea501aedb | |||
| f1d175fe84 | |||
| 0f432c4fd9 | |||
| b53e19c439 | |||
| bebd20abf0 | |||
| 5dc2293212 | |||
| 37f480c01e | |||
| f16efa24b0 | |||
| e11d05eb02 | |||
| 32cd45106d | |||
| 7b95893b34 | |||
| b6769f6350 | |||
| 5650b2d6b9 | |||
| f0fe8edf80 | |||
| b30df5b6ed | |||
| 0ef26556a2 | |||
| ea92feab27 | |||
| 1720e156d2 | |||
| 9ed06e741e | |||
| d39a3a78d0 | |||
| 09dc2984f3 | |||
| 660e3f8a99 | |||
| cc9797eb71 | |||
| 6a8aa73e63 | |||
| 689495e410 | |||
| 415fad4f4e | |||
| 396db7a439 | |||
| 61bea5639b | |||
| c9d6812d11 | |||
| 18dd435678 | |||
| a7a4112312 | |||
| cf8cb9a6a4 | |||
| b31f1ddb0f | |||
| 382ddd48e4 | |||
| 0b57fa9238 | |||
| fe7721270b | |||
| 49b35abb0b | |||
| 41dbb6bad7 | |||
| fde640e997 | |||
| fd1f4ff9b1 | |||
| 3a4c1b12a6 | |||
| ce68dbee8a | |||
| 1976a4c1dc | |||
| 0dc3419651 | |||
| 7769608e03 | |||
| 9772d7670b | |||
| 36e9edb51b | |||
| 6191980dc7 | |||
| 7179b67137 | |||
| 65362e34b8 | |||
| ec76d336f3 | |||
| 12d5e4546d | |||
| 4a97b02010 | |||
| aa492f6ca4 | |||
| 7547d2cb89 | |||
| 3febf729b6 | |||
| fbfdec2380 | |||
| fb665b409c | |||
| f86e76615d | |||
| 7cfe3f699a | |||
| 03ab2ae2cd | |||
| ad1016a427 | |||
| 6fc40452d9 | |||
| de098bdf55 | |||
| dcfd66324e | |||
| ff962b9658 | |||
| 006ff44858 | |||
| f9fdcc5314 | |||
| e49605dea6 | |||
| e429f48320 | |||
| 43ef016751 | |||
| 4a59adc73f | |||
| 2ebc28519e | |||
| 23de94b8cd | |||
| 42cf20137b | |||
| d5bb15e695 | |||
| 039985fe5f | |||
| d618c10e8b | |||
| 069f2094f0 | |||
| 2b6bbba265 | |||
| f7e032a5bc | |||
| 0d394fb6b7 | |||
| 9635a1dcf2 | |||
| 6ae3a88d7b | |||
| 512499ab12 | |||
| b8e3092fc4 | |||
| 4b9e5e5f34 | |||
| a7d9dfe79a | |||
| cc503ec6d1 | |||
| 7eb8edbe1f | |||
| 1f4ccaec4a | |||
| 66e0edebc5 | |||
| ebdc84f7e1 | |||
| cd1b8d4579 | |||
| a733649521 | |||
| 01d72c5f45 | |||
| 55681c7fb8 | |||
| 4a19726a99 | |||
| 61f26299b8 | |||
| 8a6317da2b | |||
| 1124149cf8 | |||
| 426071fe15 | |||
| c97aa44723 | |||
| de6bd9682e | |||
| 16e7ea3109 | |||
| 41fa14351a | |||
| 35f135a10b | |||
| 037abac188 | |||
| fecd32a254 | |||
| 446af5e438 | |||
| 71ecd65814 | |||
| 3e101493dc | |||
| 453d4b7349 | |||
| 40a8bc158a | |||
| 2c711ed370 | |||
| fc1654dedb | |||
| dfec56fef9 | |||
| 942db49daf | |||
| d4562e7ca4 | |||
| 3a0845fe2a | |||
| 3740ac642f | |||
| c1ccf00302 | |||
| 4400a6b13f | |||
| 66fbf984e9 | |||
| 30ebc6037a | |||
| ac60b08f2d | |||
| f32e127148 | |||
| 8e3b798bce | |||
| 57dddf528b | |||
| 35d5e05f00 | |||
| 4ecc89a4e7 | |||
| 87bc1be15a | |||
| b1c599e71d | |||
| da3fb36a22 | |||
| ded7761c91 | |||
| d4de2ef739 | |||
| 7637f6fdd1 | |||
| f663954f03 | |||
| f2fd95b04e | |||
| 3880b97cae | |||
| 2fb00ee112 | |||
| dc92877beb | |||
| d9103e62a7 | |||
| e5a8c3cb8f | |||
| 97b00d94cf | |||
| 381f0a8490 | |||
| b8b3fd02fa | |||
| 3b244f4de0 | |||
| c4d101275a | |||
| c9b21ded98 | |||
| f5115f82fb | |||
| d6a6b4f319 | |||
| f3e418cf68 | |||
| a74a0c1f57 | |||
| 0f10e274e6 | |||
| 57a419eeaa | |||
| aa18a3c542 | |||
| dea8d064d4 | |||
| d022fb231f | |||
| 415784bf06 | |||
| 246974c1b8 | |||
| 34d2fea31f | |||
| 0571de3dd0 | |||
| 4e2d453ff1 | |||
| f4ba38654b | |||
| 7831973cdb | |||
| 4f2c7aeb2c | |||
| 237666d96f | |||
| f6d0486a19 | |||
| 200a9c308c | |||
| 28d3138c43 | |||
| 0d41c95e3d | |||
| e5ea92df3d | |||
| 9f7550eaae | |||
| 3e88458fce | |||
| a5cbf8c128 | |||
| 704b88320a | |||
| 0aae04eca8 | |||
| 3f1cd5431f | |||
| 5081b6ce3e | |||
| 7e7fb437a6 | |||
| addca29f0f | |||
| f1775237fb | |||
| f09ff3f7df | |||
| 4259f84764 | |||
| 707bbf1863 | |||
| a989f85b2b | |||
| b62396a907 | |||
| 998c91c727 | |||
| 64b8e74612 | |||
| 55eff16b9c | |||
| 31857131be | |||
| 77de950af8 | |||
| 176c84cf2d | |||
| 15ea6a982c | |||
| 6230b77473 | |||
| 2f81a73078 | |||
| cf83ae059e | |||
| 4418d4ad6f | |||
| 239b6a0da1 | |||
| cc02495459 | |||
| 153d0380de | |||
| 87fb930c63 | |||
| 38a8cef3d9 | |||
| 6f7551abbf | |||
| 64d6860e58 | |||
| 88e6c7eee8 | |||
| 75c5553852 | |||
| 9d1815c21f | |||
| c93cda4eaf | |||
| cce969d870 | |||
| 0b177d845c | |||
| 42f288e0e4 | |||
| 3948c79891 | |||
| c8a28de46a | |||
| c660d8cb72 | |||
| a2688c4a15 | |||
| 9d2e9b7acd | |||
| f37ed72b39 | |||
| 6fd3c11f06 | |||
| 414ffc703d | |||
| f7a587b7a5 | |||
| 534defa36b | |||
| 1fbf846270 | |||
| e6a1cd967e | |||
| c9adf1a785 | |||
| 8380b7e66e | |||
| e01c7e81f2 | |||
| 4751dd9079 | |||
| f82ae16e31 | |||
| 8a00446b5c | |||
| 39f37bcd23 | |||
| c8e4df8150 | |||
| 1e9e2cb048 | |||
| 06661cb187 | |||
| fade021048 | |||
| 9d90138847 | |||
| b7a677d79d | |||
| 3660f5af6d | |||
| 61e1c35f02 | |||
| 89721ce797 | |||
| 55b4c0ac11 | |||
| 70df8a53c8 | |||
| 3311ab5b37 | |||
| 49ad367a70 | |||
| cc3a5c4a4f | |||
| f7af979b7e | |||
| 6f06f96a94 | |||
| a3ef86ba37 | |||
| d6da292d5f | |||
| ccb8c6585c | |||
| ea567008c6 | |||
| 6cbde10b11 | |||
| 7e23776501 | |||
| e6a91ebc72 | |||
| 06a2e883fb | |||
| 00b0e00fd3 | |||
| 8086d5344e | |||
| ea76733305 | |||
| f0f8af48b3 | |||
| 9bc02c7752 | |||
| 3067535fe0 | |||
| cc6bd781e0 | |||
| 10a1cb096d | |||
| 47c228d048 | |||
| 11c35c9abd | |||
| ba8bde1153 | |||
| 7c991024b7 | |||
| 710d500980 | |||
| cde0b55aab | |||
| 6660db1a89 | |||
| 8f76b484ba | |||
| b8790be7fe | |||
| bd4a2e7507 | |||
| 70002f4efb | |||
| d40eed3c5d | |||
| 130d601e54 | |||
| 7ae8d0ce48 | |||
| 494ff26fdd | |||
| 330a582a26 | |||
| 54d5669b3b | |||
| a7491c545c | |||
| 6515979251 | |||
| a97d9ec2bc | |||
| 2685e80161 | |||
| ea94031545 | |||
| 298e3861cf | |||
| ce230e80a5 | |||
| b12ae238d3 | |||
| d482570221 | |||
| dde8c5fdd9 | |||
| 259d6662fe | |||
| e4849ec659 | |||
| 88f679cd41 | |||
| f97eeb83c2 | |||
| 155e5bdf57 | |||
| e78c9a1efb | |||
| 4b74dd8f19 | |||
| c3be6d3e83 | |||
| 50baa907bd | |||
| 40207ab256 | |||
| 0733851e4e | |||
| 1aea397f9a | |||
| 335af09596 | |||
| 95c3c062fd | |||
| f47e3e550c | |||
| d3d0dc5a22 | |||
| ff4c573941 | |||
| 8d52620d32 | |||
| 5856fbb670 | |||
| 7cb8a2de39 | |||
| d57be37c34 | |||
| 28a14aaf76 | |||
| 359ed5770d | |||
| fe093aa9ac | |||
| 338a9ed08a | |||
| a8c954e8d9 | |||
| 50ee432965 | |||
| 4b3e3b8401 | |||
| 2abf0f62e9 | |||
| 69dd70d42d | |||
| 405cae84fe | |||
| ae9902965a | |||
| 0c20d7d0f4 | |||
| d4e3b75bcb | |||
| bbd3fd51dc | |||
| 9ecc961e42 | |||
| dd2cc62d01 | |||
| 7e316bbc73 | |||
| e455a19ff3 | |||
| 618064d16a | |||
| 05705a91cf | |||
| ec8ede8e28 | |||
| 6131d2534a | |||
| 775ffe42d7 | |||
| 400621572f | |||
| fe4539c9ed | |||
| a19017351b | |||
| e1a6fdd21f | |||
| 264c5ae372 | |||
| 0a00e950b6 | |||
| b12f2e784f | |||
| bc170e982f | |||
| c68c7b6109 | |||
| ac95ac04bc | |||
| a23ccb7ac4 | |||
| 09c74d8f85 | |||
| 4c747a0c5e | |||
| 3aa4b774bd | |||
| 93cf3aa695 | |||
| e75dd6c552 | |||
| 6ea03098b2 | |||
| 2cef14d2d8 | |||
| b7b6750ad2 | |||
| a729070654 | |||
| af42956f60 | |||
| ad8518a4c3 | |||
| 1c1867610d | |||
| 8c3ebb00e5 | |||
| abba3e6e10 | |||
| 058fe769b9 | |||
| 4b037443a6 | |||
| 28db905e14 | |||
| 41d7a996fd | |||
| 114a65746c | |||
| 6dd0cf7724 | |||
| 8e5a018c2a | |||
| 052f236206 | |||
| da3774527f | |||
| 93401402ca | |||
| c1ea4788a4 | |||
| b32a79f966 | |||
| cd748dc371 | |||
| defce7a85c | |||
| 63d5b62229 | |||
| 1acda2bb11 | |||
| db8f5e16e3 | |||
| c5c8fb8689 | |||
| b11478b4c0 | |||
| 26463c1853 | |||
| b0b6513bb7 | |||
| 0c6135d786 | |||
| 0167baec84 | |||
| cb5ab935ee | |||
| 96efd045b4 | |||
| e1846e0564 | |||
| ed809290ca | |||
| 0b2b9e184f | |||
| 4dd32820a3 | |||
| ffa6de7138 | |||
| 189b9233ab | |||
| 8c05b987ed | |||
| ed00864447 | |||
| ad9e6aa6b9 | |||
| 04daddefa7 | |||
| acf51fe626 | |||
| 9d44384d22 | |||
| a435e85d4c | |||
| c6edd2c533 | |||
| 928c737ead | |||
| 794d30ebf1 | |||
| 0864a07cdf | |||
| b64f58c237 | |||
| cc86400957 | |||
| b8c282f95f | |||
| 47e39a1d9c | |||
| 308e9c1df2 | |||
| d3fd7b7428 | |||
| 31833000bb | |||
| 02f7edade5 | |||
| 371457735a | |||
| ea65d62be8 | |||
| d1b8a7a4aa | |||
| 9b94b56c6e | |||
| e07cf3f9d6 | |||
| 906856b01b | |||
| 4ac3349377 | |||
| 3a9b421503 | |||
| c844389f09 | |||
| 444f23f9b2 | |||
| 721f2916cd | |||
| 5d707853b7 | |||
| 19621752b5 | |||
| f0e8cb5101 | |||
| ac2de4ddcc | |||
| 5471696d62 | |||
| adee3383ce | |||
| 0ab9631e0f | |||
| cc377963e0 | |||
| 5c22478108 | |||
| 51598dc022 | |||
| f620cd26af | |||
| 1f3a8a258b | |||
| 19983bf7a1 | |||
| 47ffae4b82 | |||
| 487647029a | |||
| 89ee4960c2 | |||
| 9c95358305 | |||
| 71c4ce41f0 | |||
| f60c96e9fd | |||
| 9ec605bed1 | |||
| 6ac082c7df | |||
| f552d6c0f2 | |||
| 52c65f598d | |||
| 9b12ca8ff3 | |||
| 31ba8a86bf | |||
| fe90ab8f50 | |||
| 58acad0eb8 | |||
| 3ce22bcb32 | |||
| 71846ade42 | |||
| 9c24034776 | |||
| 735a84bf9a | |||
| b65a668e77 | |||
| ac2b1e43df | |||
| 63cea80a20 | |||
| 67a51c882a | |||
| 21b873c80d | |||
| c86ebc7aa3 | |||
| 32f65de458 | |||
| 63d3ffddd4 | |||
| 70f6ccc0da | |||
| 5fa470bd9c | |||
| 01414ca4c6 | |||
| c781453055 | |||
| 8b4b06c0f7 | |||
| 0737446ea1 | |||
| 5f71eb5e0b | |||
| b89b9ed017 | |||
| 0df3067416 | |||
| f850ef5c31 | |||
| a520c3872a | |||
| 16c32ebdfb | |||
| b0640e88f9 | |||
| 6121b6ceb1 | |||
| 2d865d9c3d | |||
| 95f92efa70 | |||
| 840a61511e | |||
| a1ed21cd17 | |||
| c2b9b0c85b | |||
| 2cc45c7efc | |||
| 6dfc41fc0d | |||
| f421e31de1 | |||
| 53c5b2b53e | |||
| c024b74f5b | |||
| 598bcaaea4 | |||
| 571c0d08e0 | |||
| a56c8c2609 | |||
| 724c450120 | |||
| 1794911d27 | |||
| 01c7b01c55 | |||
| 4bf5488d79 | |||
| 9d2b523f29 | |||
| 9eb079c5bc | |||
| fc8c28203c | |||
| a03d9b3dec | |||
| 641e3da1d0 | |||
| 15b2110617 | |||
| 7a8f3bdbb5 | |||
| 65c90e1f11 | |||
| dd7795d845 | |||
| 5af924556e | |||
| cec55a36ed | |||
| 09ec97ec2f | |||
| 32b8116357 | |||
| c3079f4a65 | |||
| 78148def17 | |||
| 754f0ecc43 | |||
| a0e8698aa5 | |||
| ff14b4aa2a | |||
| c0e6bcf7cb | |||
| 71e34c6b03 | |||
| f5208def43 | |||
| 4f28e5d5a4 | |||
| 0a4329ea67 | |||
| df5279e3f6 | |||
| cba94595f1 | |||
| ed38aeed45 | |||
| 57f5392203 | |||
| 2b95eceea9 | |||
| e4f017df2a | |||
| 20e6357127 | |||
| 31ad83e561 | |||
| 06219e8f69 | |||
| 2f5485cc25 | |||
| cf70c543e3 | |||
| 463f63af19 | |||
| 84620e74f8 | |||
| 573b8ec0c3 | |||
| 4aa659d206 | |||
| 02e6733b13 | |||
| 7c6891e46f | |||
| 35a953b546 | |||
| 52edbd867d | |||
| 9f2ec3e79f | |||
| 4ec85b85cb | |||
| 4888fc7211 | |||
| ca1d177565 | |||
| 0b608e1b3e | |||
| 5ae0e75a96 | |||
| 54105c47f0 | |||
| 0f9ce1abd6 | |||
| c322053b02 | |||
| dab09b193d | |||
| 97fb0e3e13 | |||
| 7abad9c43d | |||
| ce67fddcc9 | |||
| b33664d3c1 | |||
| d0359907d7 | |||
| 914133e73e | |||
| de34cedacf | |||
| c4d0975313 | |||
| 2fffff65c4 | |||
| d78ac25828 | |||
| 4a267122e2 | |||
| 8a8fdbb8ec | |||
| ef8e664276 | |||
| ab84352918 | |||
| 51f15e2f67 | |||
| e3fb527fe6 | |||
| 96c1b23362 | |||
| 9b7fbe8fd3 | |||
| 2d25fd3e38 | |||
| e850bceacf | |||
| da7b1cb549 | |||
| 1747b0ebb5 | |||
| eebdd35ec8 | |||
| 87b95f50fb | |||
| d09ca59279 | |||
| 8e6312f1ad | |||
| b221e7a330 | |||
| d60d03f666 | |||
| d90449f0fe | |||
| 677eb001bf | |||
| 4f5b7c5c08 | |||
| b94d8e1d87 | |||
| 4f46a01469 | |||
| ae4d871d90 | |||
| bae7e05077 | |||
| 636d97250b | |||
| 6097d5a2bd | |||
| 2dbbc7dcaf | |||
| 11506903cc | |||
| ec23d2ac37 | |||
| 1e00d941d1 | |||
| 214eb0f7ef | |||
| a24d1ac987 | |||
| 7345e70568 | |||
| 9e9c645799 | |||
| 8539e9ce30 | |||
| 21fb8d0955 | |||
| 2dc85fdb6f | |||
| b99f34fad1 | |||
| 34a789337c | |||
| 29416b838e | |||
| f5b00402c9 | |||
| 10e38edab8 | |||
| e456c2606f | |||
| 36037c3cfa | |||
| d14e2dcbbe | |||
| b0f138489d | |||
| 31379e0949 | |||
| d078a10f64 | |||
| 691b38f725 | |||
| 6c6801c1f0 | |||
| cc00322a04 | |||
| 77f96a39b6 | |||
| 7002bd2600 | |||
| ff47788d36 | |||
| 9d226e3f6f | |||
| fa2c3ee569 | |||
| 704c44a636 | |||
| 52a0105806 | |||
| ad66adefbf | |||
| 68978d09ac | |||
| 28504cbc45 | |||
| f2580cf766 | |||
| 60a21740db | |||
| 528ab0a8e2 | |||
| 1af65b01f6 | |||
| deadb0c73c | |||
| ebc9bacbf1 | |||
| 7dd1fe5dfe | |||
| 436cd9aa58 | |||
| 27db6bd53f | |||
| aef46d5d9c | |||
| 1f330f3660 | |||
| 8b41767751 | |||
| e49de29785 | |||
| bc9b47f651 | |||
| be6313d2d0 | |||
| 0d60283b36 | |||
| d81688eb59 | |||
| df03f16c0a | |||
| 2ea763265e | |||
| cc52dc3fa5 | |||
| df370606a6 | |||
| 18dd7ff1f1 | |||
| 835cce2d45 | |||
| 5999a67257 | |||
| 63879809fd | |||
| 45fb9d0060 | |||
| 22932459cc | |||
| 0dca0557c7 | |||
| a46bbdec84 | |||
| cb926522bb | |||
| 26d2a5699d | |||
| 5947b6fcb9 | |||
| e59028a11a | |||
| 1f71c4eba1 | |||
| bbf660a2eb | |||
| 0efe073a2a | |||
| 40b18a2a89 | |||
| cee09d18db | |||
| 7fc8beeb00 | |||
| bd5317fa27 | |||
| f7d4bbf2b3 | |||
| 3015a3e8f2 | |||
| 227aba33ee | |||
| 770289f1ae | |||
| 7f2e216db4 | |||
| c8a902998f | |||
| c23ccf6212 | |||
| 1b809790a2 | |||
| d5d702c431 | |||
| 2757ea4d95 | |||
| 79c32a013e | |||
| ecdf99a187 | |||
| fa06af7695 | |||
| 853706b36b | |||
| dcfe131237 | |||
| 075d2915d3 | |||
| 5081aca37d | |||
| 335101166c | |||
| a48777343c | |||
| 8c80f43819 | |||
| 15b1c9b5eb | |||
| 1298765964 | |||
| 7dba4864e9 | |||
| fcf3063e09 | |||
| f1bda3ce68 | |||
| aa6fce9f3d | |||
| e852a2b358 | |||
| db3d47b36f | |||
| 7fbc7b6e9d | |||
| 73888ec864 | |||
| 53548046b3 | |||
| 879f452690 | |||
| bf96a97d0b | |||
| d7c1585b21 | |||
| d36a59b2a0 | |||
| ee7b6b39d5 | |||
| 24215a9526 | |||
| b4e8a730a0 | |||
| 44b1b8ac56 | |||
| 51d5ed1dae | |||
| 34737e04bc | |||
| c3163a8571 | |||
| 411568fecd | |||
| e19c9610e8 | |||
| 6c0b187d25 | |||
| 252ca6f494 | |||
| e206e7e64f | |||
| 2d42d03dc8 | |||
| 01863bcf38 | |||
| 1f3bb2dce9 | |||
| 96f48f88df | |||
| 3312b825fc | |||
| e97d52d8a7 | |||
| 0a2019e833 | |||
| 6198ccaa97 | |||
| 63f0fe8dd9 | |||
| 00799916c6 | |||
| 2fce92acf8 | |||
| fdeb2a4ae1 | |||
| 59fb628ced | |||
| 1d56d81a3e | |||
| 19f62a0593 | |||
| 26e923ab58 | |||
| 58f42c4651 | |||
| a470bc3325 | |||
| b5652acad6 | |||
| 46bc6ea65a | |||
| f7fa3ef681 | |||
| 270c56b1b5 | |||
| 380dc4d77c | |||
| ca01e1a728 | |||
| 1f39da2b45 | |||
| bac7b7a2d5 | |||
| 4cd6d1d0f5 | |||
| 8b55479d0a | |||
| b9efd41a48 | |||
| 90f880ee70 | |||
| c8c0f43d4d | |||
| de0446c799 | |||
| 4ee4d3f836 | |||
| 408da784e1 | |||
| 5e77e204ba | |||
| 3a087616ee | |||
| e97dc7fcee | |||
| 1873933416 | |||
| 45001b5d3c | |||
| db0a40de71 | |||
| fc4095a7e4 | |||
| ec8fded136 | |||
| 237524be8e | |||
| 4354d1369d | |||
| 0457f1f476 | |||
| 05ea2a4076 | |||
| 8eaf664de6 | |||
| 3b615607a9 | |||
| 0e810aa790 | |||
| f92f7b6ba8 | |||
| fcb5df09b1 | |||
| 188866eda1 | |||
| f1246b3b27 | |||
| 2fcc7321cb | |||
| e8b465be5a | |||
| ca03dcaa6e | |||
| fb31feac39 | |||
| f5726c8e2e | |||
| 2d1a3464e7 | |||
| 16eda7c0af | |||
| 1e152c7dab | |||
| 6a2debcc2d | |||
| ec07a69c1e | |||
| df02cc1529 | |||
| 0d1ca2bb51 | |||
| b0de8e9159 | |||
| 3ca5f61221 | |||
| 2a56bb093b | |||
| 30b090c527 | |||
| 4f294455cc | |||
| fa360267a0 | |||
| 40fc623123 | |||
| f1596e1856 | |||
| 66f348d4a0 | |||
| 1e59047071 | |||
| 2f31fa3e10 | |||
| 27f8663051 | |||
| cf79c78d41 | |||
| 82fa7edc18 | |||
| 48da2b2848 | |||
| 80a87fb8e7 | |||
| 00952a0e3e | |||
| 945d401004 | |||
| fcc86d079a | |||
| c6a8862b43 | |||
| 352daf8c36 | |||
| 0be71f7497 | |||
| 393d2de7bc | |||
| cd8b59e225 | |||
| e36cb78a15 | |||
| a06830ae9d | |||
| 573ca32fe1 | |||
| a3e5c50940 | |||
| ddf9f74db5 | |||
| e6bc30cb80 | |||
| 20ed037311 | |||
| 9562796884 | |||
| 0ea84c3db4 | |||
| a2925ad45c | |||
| 895df67b1c | |||
| 00f99ce667 | |||
| 4db864ea7b | |||
| 7dc7ead417 | |||
| 5c3bffdda4 | |||
| dd8551c0c6 | |||
| d976ce9c89 | |||
| 4a8d2ad8c8 | |||
| 1018b4b318 | |||
| 3c17276e27 | |||
| bf451393b6 | |||
| 4fa6ce6141 | |||
| 51a65743d1 | |||
| 60bfa0fadb | |||
| e86aa76002 | |||
| 2d48ecc855 | |||
| a73adde79a | |||
| 4d8ee4bfcc | |||
| 0acfaca8f6 | |||
| 9dd4577bc7 | |||
| 66c142a4d6 | |||
| 54126265b2 | |||
| ebe09382c0 | |||
| 957b8c9041 | |||
| e8e9d6af41 | |||
| 31c51d6c0e | |||
| 64fd9c7b17 | |||
| 9e0b725b2f | |||
| 3588bd14e3 | |||
| 4f016990ea | |||
| 571e4b0cc1 | |||
| f5361f679a | |||
| 662138ac00 | |||
| 62c7694c50 | |||
| 4c6e2200c0 | |||
| 9d0998fb2b | |||
| e9d4d71c9d | |||
| a970f06dc8 | |||
| 7cbe8db366 | |||
| 8e07b3c304 | |||
| 9e3306f1be | |||
| 122e041b08 | |||
| 35da421ad2 | |||
| cc82bb8f36 | |||
| 4b05dcc7a7 | |||
| e280283e40 | |||
| b13c76816d | |||
| cbee5af4ed | |||
| 26877620b1 | |||
| 3c3daf13cd | |||
| 87b8b96f15 | |||
| 509052cc61 | |||
| d573df0359 | |||
| 5890415b06 | |||
| 9bef9366b3 | |||
| 9ed3e7d0dd | |||
| e2bed36c5f | |||
| 11cfcda4fb | |||
| 12213d83c4 | |||
| 24f26dd0ea | |||
| 3465563b27 | |||
| 42a5452fc5 | |||
| 56bc23375d | |||
| 05cd51ffc0 | |||
| bea4c1420b | |||
| 1c569b7358 | |||
| 3ece611ea0 | |||
| 604a9c198a | |||
| 8b03b33e22 | |||
| 0edc2c3b0b | |||
| 2e2b19d5e0 | |||
| 2bf5b18197 | |||
| 5e79bf8263 | |||
| 25b785c5ea | |||
| c1b8896799 | |||
| af5ed27902 | |||
| 9954efee98 | |||
| 5d806d1498 | |||
| 2dd03e4d2f | |||
| eb0c143166 | |||
| 6d70fad14b | |||
| a5362bd2ad | |||
| 0a8151f8fa | |||
| 437ee3066f | |||
| f5c709eb7b | |||
| 0a8d836d56 | |||
| c6cc191e17 | |||
| fd99e96d93 | |||
| 640e68d096 | |||
| cff935e633 | |||
| 1d64583697 | |||
| 7c7452a93d | |||
| 03882addc1 | |||
| 9ea81709eb | |||
| 27728a24a1 | |||
| ffbdd5aeb0 | |||
| 78ade08e65 | |||
| aeb90e106e | |||
| 4f91767c8a | |||
| bb94eba766 | |||
| 9e9cd03a21 | |||
| 7eeabdca79 | |||
| dcd7c36f66 | |||
| cf5a404b46 | |||
| e60c18c2e2 | |||
| 80370f11ae | |||
| b7ff089cca | |||
| 9bfa6dfee6 | |||
| 2b75e42c4c | |||
| 577b85627b | |||
| 067b7db2f0 | |||
| 0664a89c4d | |||
| 7684944246 | |||
| 198dac6ad0 | |||
| 724d5394d7 | |||
| 993daa4f82 | |||
| 9c9c1c024c | |||
| d873e7bb64 | |||
| 1962306282 | |||
| 05ce9a22ad | |||
| 589c441c83 | |||
| 5aaa26d503 | |||
| fbe54ad65a | |||
| d53d16071e | |||
| 5b9d311b06 | |||
| db609b47d8 | |||
| fa2186b407 | |||
| 58d28bceba | |||
| 85db13c885 | |||
| 41d8cb50dc | |||
| bbad0dc726 | |||
| bba583ae56 | |||
| cb30dc452d | |||
| dd761941cf | |||
| 94feb77a71 | |||
| 8eb1d22ae3 | |||
| 54b6d9d37e | |||
| 98c1a5facc | |||
| 1a6b176c24 | |||
| 9344c6fdd4 | |||
| ee4d3e7a84 | |||
| 7cfd5ce60b | |||
| 2f089ecaa3 | |||
| f1da9c28e7 | |||
| 812b27ca93 | |||
| 29998c6239 | |||
| ca1bbaa4f9 | |||
| 9d98983d26 | |||
| 442ee4da33 | |||
| bc3de1824d | |||
| 5f0d53fcb6 | |||
| dd0d0ec17b | |||
| cd2d3d10c4 | |||
| e77b18072c | |||
| fb8ccb5721 | |||
| 1207cdd105 | |||
| e36af0a49c | |||
| a983d70ffa | |||
| 417d0dc3be | |||
| bc8d6df5d0 | |||
| 1fb00ee19c | |||
| e2ff4baef5 | |||
| 662b2a0bed | |||
| f9694456f7 | |||
| 99f8dee269 | |||
| 9dd94ffac0 | |||
| cc74155226 | |||
| 3fc9f42378 | |||
| 6f03ba3720 | |||
| e5d7116cad | |||
| 73f791c8d9 | |||
| c3e42dbe33 | |||
| a80ecaa473 | |||
| f8ed2b6fd2 | |||
| e1c7a04ccc | |||
| 93771ebcf7 | |||
| 280ce76a8e | |||
| b3ea94db3d | |||
| 48e0e36f91 | |||
| 29bdb33466 | |||
| 5e5ba0b6fe | |||
| 21a509c6a5 | |||
| 80cb5d4c67 | |||
| f1c602abe5 | |||
| ac57eceb66 | |||
| e21d392ee2 | |||
| bca6d28542 | |||
| 19ef148991 | |||
| 9978d829ac | |||
| b3901bd430 | |||
| 0d545172d6 | |||
| 8f3773cda4 | |||
| 014ccf8f45 | |||
| f62c603ad4 | |||
| 66562b58d3 | |||
| 34fc82ea19 | |||
| 6d27151e6c | |||
| d69e3c6c1f | |||
| 392ea786d1 | |||
| 53590cd94e | |||
| 5812a41b2b | |||
| 81d9bc08f6 | |||
| b3d6c3e2e5 | |||
| b477c6f35d | |||
| 6d121c8879 | |||
| 9b7b7d52a4 | |||
| 0c8dc6f77b | |||
| d1ce756cc5 | |||
| 075459ad79 | |||
| 1dd6e356f8 | |||
| eaab071a1e | |||
| 53b6883a2c | |||
| 90a247746c | |||
| bf67fc98a7 | |||
| 67169a85d1 | |||
| d99f10fb6b | |||
| 43cea4a0dd | |||
| 487e925069 | |||
| 635a89fda9 | |||
| bf08964ff2 | |||
| 3f8b3e5c0f | |||
| d1c133309b | |||
| b0822f4076 | |||
| 8aed79ce43 | |||
| 5d1110b679 | |||
| 33a648d515 | |||
| eba4bd3735 | |||
| 712287785a | |||
| 6b8511da3d | |||
| 09cd04078c | |||
| ddec69a7d6 | |||
| 97d41e30b8 | |||
| f0acbcc8bf | |||
| 7a179602d7 | |||
| 0394771764 | |||
| ef2f2cc8e8 | |||
| 9a2af552d1 | |||
| 6bb2a4249e | |||
| 34f4f4b6d4 | |||
| 2eee8845b8 | |||
| fec98f1aa1 | |||
| a06e9c3435 | |||
| ba068613fa | |||
| 1b0e29aba8 | |||
| 8c2043198f | |||
| 5ce185cf01 | |||
| 7f2db62c47 | |||
| a40a3602b8 | |||
| 4e4c5ff6ee | |||
| 55ddbe917f | |||
| ebf57692ff | |||
| ec317aa8ba | |||
| 1ea38af75f | |||
| 9a1ee8d971 | |||
| b3bee232c3 | |||
| 29d3431dde | |||
| db430edb90 | |||
| cf627fdb7a | |||
| 3e342e6d19 | |||
| dcead848c2 | |||
| 139cf7eed1 | |||
| 5ff0b100c5 | |||
| c8484f1451 | |||
| b3a2ff0f15 | |||
| 03104bcd88 | |||
| 601cee0377 | |||
| d6bd2674f2 | |||
| 94e0ef6ee1 | |||
| 124c2b798c | |||
| 541610ac18 | |||
| 881916afcf | |||
| f738fb0386 | |||
| 5a64aeb401 | |||
| e34a841264 | |||
| 76112fab8e | |||
| 335c10a304 | |||
| 7114e122e9 | |||
| b2e218b064 | |||
| 406d7f7bc6 | |||
| 09f277a033 | |||
| e3f323d894 | |||
| 5977eae8ff | |||
| a2b48fcfcb | |||
| f9b90c2a2a | |||
| c1ac5878cf | |||
| 91be41d8be | |||
| 609c140904 | |||
| 03f2d90d29 | |||
| 4feb90cd0b | |||
| e745fb777e | |||
| 993cebff6b | |||
| de07bdca0f | |||
| e322f5e0c1 | |||
| d779b0a62b | |||
| f7f40f61a3 | |||
| 9f1e952b19 | |||
| 9d0eabc9ff | |||
| e7fe934911 | |||
| 0b35e91159 | |||
| 586428b857 | |||
| 53739a9778 | |||
| 88a1534eb2 | |||
| d9563aff29 | |||
| d4c123a775 | |||
| d6da910a3a | |||
| b7e01aa6bc | |||
| 6908fbb32f | |||
| 265444eda6 | |||
| bf14377841 | |||
| b0a9c3c873 | |||
| c091f12a92 | |||
| 8a70dc686e | |||
| 283041e331 | |||
| 6461c35f1b | |||
| 4f0b20a20e | |||
| dd19068bac | |||
| 64c3a5cb03 | |||
| 1c17f1468d | |||
| 840993a0fb | |||
| ee9436642a | |||
| 5bf116cff5 | |||
| 7d743652c0 | |||
| f7c78ffb40 | |||
| 21cf7fca80 | |||
| 2388ad154c | |||
| 8ffcbfb888 | |||
| ff008bfc7d | |||
| e4d4813909 | |||
| a701a54592 | |||
| e5dea6a83c | |||
| 2b97b678aa | |||
| e886803186 | |||
| 19eb2957e4 | |||
| 972baac51d | |||
| dab15df837 | |||
| 2e9200953e | |||
| 47ceecdb59 | |||
| 095296a9a8 | |||
| 1f13bd5ea9 | |||
| dce31b11b7 | |||
| 23be24a41d | |||
| 583bcfc9a8 | |||
| 0e9ba5cc6b | |||
| b480f8a239 | |||
| 54282c137c | |||
| 95eeea181c | |||
| 70ff3841f6 | |||
| 59cf5b17b5 | |||
| df9acda9a3 | |||
| 2a721f0bc7 | |||
| 7e4e1a3563 | |||
| 968f9e5ab2 | |||
| 98e7b80700 | |||
| 3da6289f22 | |||
| 2553c61685 | |||
| 8114bd95a5 | |||
| fafe645f37 | |||
| a0ed17bcdc | |||
| 57d57e87bc | |||
| 183a77a323 | |||
| 12e31a386c | |||
| c4d69adc43 | |||
| d3a4539305 | |||
| 12b97c1f61 | |||
| 867ce7e419 | |||
| 088c12d3cf | |||
| fc9951ab46 | |||
| 2c8572e679 | |||
| 624e0729cc | |||
| c0ed5d5803 | |||
| 45234121d4 | |||
| a0113542c0 | |||
| eaca92106d | |||
| 33fc6b2063 | |||
| 696349befe | |||
| fddb091e7a | |||
| 89d7d2aba7 | |||
| 0fb2612ea1 | |||
| 68a69ba6a8 | |||
| 65e098e6a7 | |||
| d90d2ef671 | |||
| e420dc17f2 | |||
| 1cf3041e95 | |||
| 5688746e4f | |||
| 9c58c20c31 | |||
| 5586f73cae | |||
| 4d07d613ad | |||
| 1bff51c09c | |||
| 489467dc3c | |||
| 578d86456d | |||
| 0febab97c8 | |||
| 95f76e769a | |||
| 6ddc4085d3 | |||
| 6a8a0d2abb | |||
| 7a3cb01e35 | |||
| 4c569165f1 | |||
| c58c25b049 | |||
| 1ff6875e58 | |||
| 86bef4df2d | |||
| e377eecacf | |||
| 9a92cdc64f | |||
| d4d64aa467 | |||
| 63d1661612 | |||
| c7fe7a1904 | |||
| 57780ef93e | |||
| 0e882b89f0 | |||
| 90da6ba84c | |||
| bb2bff9a40 | |||
| 183919944a | |||
| 0d6f09249b | |||
| b4a9320fdd | |||
| 5fc585b7c5 | |||
| 0439491495 | |||
| 814d5d8866 | |||
| 8b48b528ad | |||
| 99d05932a6 | |||
| 2b42aa1173 | |||
| 57c6badfa9 | |||
| fcc685e1cc | |||
| 1125a737fd | |||
| 60ba920aaf | |||
| b682b9afd8 | |||
| d3e0a4b6c4 | |||
| 16834ee33b | |||
| 603f8ddce4 | |||
| 9a694218bf | |||
| 3ae7b07eab | |||
| f2019ec81b | |||
| ced379ab20 | |||
| 487f9615bc | |||
| 053fadb7e2 | |||
| b768988a5d | |||
| 0866e605e6 | |||
| 7c320d2709 | |||
| 113e576d4c | |||
| a7b014da20 | |||
| 663e7ac4be | |||
| 7a7663594d | |||
| a727ce10fb | |||
| 86a43d69e2 | |||
| 6de8a4bdd7 | |||
| 4684dcb589 | |||
| 30302cfb39 | |||
| 48a3c9d885 | |||
| a65b41080e |
4
.gitignore
vendored
@@ -415,3 +415,7 @@ fabric.properties
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
# packages for node
|
||||
package.json
|
||||
package-lock.json
|
||||
|
||||
|
||||
0
ArticlesApp/__init__.py
Normal file
157
ArticlesApp/admin.py
Normal file
@@ -0,0 +1,157 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from ArticlesApp.models import ArticleModel, UserPageModel
|
||||
from sets.admin import *
|
||||
|
||||
|
||||
# class Admin_ArticlesPageSets(Admin_BaseIconModel):
|
||||
#
|
||||
# fieldsets = (
|
||||
# (None, {
|
||||
# 'classes': ['wide'],
|
||||
# 'fields': (
|
||||
# 'name', 'url', 'description', 'picture'
|
||||
# )
|
||||
#
|
||||
#
|
||||
# }),
|
||||
# ('Управление отображением страницы', {
|
||||
# 'classes': ['wide'],
|
||||
# 'fields': (
|
||||
# # ('serviceBlockStateShow'),
|
||||
# # ('articlesCountInBlock'),
|
||||
# # ('advicesCountInBlock'),
|
||||
# )
|
||||
# }),
|
||||
#
|
||||
# ('SEO', {
|
||||
# 'classes': ['wide', 'collapse'],
|
||||
# 'fields': ('seo_title', 'seo_description','seo_keywords', 'seo_text')
|
||||
# }),
|
||||
#
|
||||
# )
|
||||
#
|
||||
# prepopulated_fields = {"url": ("name",)}
|
||||
# list_display = ('name', 'url', 'seo_title', 'createDT')
|
||||
# # list_editable = ('dish_price', 'allowForDelivery', 'dish_maxDiscount','dish_rating',
|
||||
# # 'dish_enabled')
|
||||
# # filter_horizontal = ['service_works',]
|
||||
# # date_hierarchy = 'createDT'
|
||||
# # list_filter = ('device_name', 'vendor',)
|
||||
# list_display_links = ('name',)
|
||||
# save_on_top = True
|
||||
#
|
||||
# def has_add_permission(self, request):
|
||||
# return not ArticlesPageSets.objects.all().count() > 0
|
||||
#
|
||||
# def has_delete_permission(self, request, obj=None):
|
||||
# if not request.user.is_superuser:
|
||||
# return False
|
||||
# else:
|
||||
# return True
|
||||
#
|
||||
# admin.site.register(ArticlesPageSets,Admin_ArticlesPageSets)
|
||||
|
||||
|
||||
|
||||
|
||||
class Admin_Article(Admin_Trans_BaseModelViewPage):
|
||||
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'classes': ['wide'],
|
||||
'fields': (('name'),
|
||||
('url'),
|
||||
# 'pub_DT',
|
||||
'picture',
|
||||
# ('devices'),
|
||||
)
|
||||
}),
|
||||
(_('Статья'), {
|
||||
'classes': ['wide'],
|
||||
'fields': (
|
||||
'description', 'text',
|
||||
)
|
||||
}),
|
||||
|
||||
('SEO', {
|
||||
'classes': ['wide', 'collapse'],
|
||||
'fields': ('seo_title', 'seo_description','seo_keywords')
|
||||
}),
|
||||
|
||||
)
|
||||
|
||||
prepopulated_fields = {"url": ("name",)}
|
||||
list_display = (
|
||||
# 'image_thumb',
|
||||
'name', 'url', 'order', 'createDT')
|
||||
list_editable = ('order',) # 'allowForDelivery', 'dish_maxDiscount','dish_rating',
|
||||
# 'dish_enabled')
|
||||
# filter_horizontal = ['sites']
|
||||
date_hierarchy = 'createDT'
|
||||
list_filter = ('enable', 'createDT')
|
||||
list_display_links = (
|
||||
'name',
|
||||
# 'image_thumb',
|
||||
)
|
||||
save_on_top = True
|
||||
|
||||
admin.site.register(ArticleModel, Admin_Article)
|
||||
|
||||
# class Admin_ArticleSliders(admin.TabularInline):
|
||||
# model = ArticleModel.SliderSets.through
|
||||
|
||||
|
||||
class Admin_UserPage(Admin_Trans_BaseModelViewPage):
|
||||
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'classes': ['wide'],
|
||||
'fields': (('name'),
|
||||
('url'),
|
||||
# 'pub_DT',
|
||||
# ('devices'),
|
||||
)
|
||||
}),
|
||||
(_('Статья'), {
|
||||
'classes': ['wide'],
|
||||
'fields': ('picture', 'description', 'text')
|
||||
}),
|
||||
|
||||
('SEO', {
|
||||
'classes': ['wide', 'collapse'],
|
||||
'fields': ('seo_title', 'seo_description','seo_keywords')
|
||||
}),
|
||||
|
||||
)
|
||||
|
||||
prepopulated_fields = {"url": ("name",)}
|
||||
list_display = (
|
||||
# 'image_thumb',
|
||||
'name', 'url', 'order', 'seo_title', 'createDT')
|
||||
list_editable = ('order', ) # 'allowForDelivery', 'dish_maxDiscount','dish_rating',
|
||||
# 'dish_enabled')
|
||||
# filter_horizontal = ['sites']
|
||||
date_hierarchy = 'createDT'
|
||||
list_filter = ('enable', 'createDT')
|
||||
list_display_links = (
|
||||
'name',
|
||||
# 'image_thumb',
|
||||
)
|
||||
save_on_top = True
|
||||
|
||||
actions = ['create_copy']
|
||||
|
||||
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
if request.user.is_superuser:
|
||||
return True
|
||||
|
||||
if obj and obj.url in ('for-partners', 'dealers', 'contacts'):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
admin.site.register(UserPageModel,Admin_UserPage)
|
||||
45
ArticlesApp/funcs.py
Normal file
@@ -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
@@ -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'),
|
||||
|
||||
]
|
||||
61
ArticlesApp/js_views.py
Normal file
@@ -0,0 +1,61 @@
|
||||
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
|
||||
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
|
||||
errors_Dict = {
|
||||
'errors': {
|
||||
'all__': f'{_("ошибка в запросе")} = {str(e)}'
|
||||
}
|
||||
}
|
||||
return JsonResponse(errors_Dict, status=400)
|
||||
103
ArticlesApp/migrations/0001_initial.py
Normal file
@@ -0,0 +1,103 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-28 17:17
|
||||
|
||||
import ckeditor.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='ArticleModel',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
|
||||
('name_ru', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
|
||||
('name_en', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
|
||||
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
|
||||
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
|
||||
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
|
||||
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
|
||||
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
|
||||
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
|
||||
('url', models.TextField(help_text='можно изменить адрес страницы (!!! ВНИМАНИЕ !!! поисковые системы потеряют страницу и найдут лишь спустя неделю...месяц)', unique=True, verbose_name='URL привязанной страницы')),
|
||||
('title', models.TextField(blank=True, null=True, verbose_name='Заголовок')),
|
||||
('description', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
|
||||
('description_ru', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
|
||||
('description_en', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
|
||||
('picture', models.ImageField(blank=True, null=True, upload_to='uploads/', verbose_name='Картинка')),
|
||||
('visible', models.BooleanField(default=True, verbose_name='Отображать')),
|
||||
('background_image_left', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Левая подложка')),
|
||||
('background_image_right', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Правая подложка')),
|
||||
('seo_title', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
|
||||
('seo_title_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
|
||||
('seo_title_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
|
||||
('seo_description', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
|
||||
('seo_description_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
|
||||
('seo_description_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
|
||||
('seo_keywords', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
|
||||
('seo_keywords_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
|
||||
('seo_keywords_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
|
||||
('seo_text', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
|
||||
('seo_text_ru', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
|
||||
('seo_text_en', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
|
||||
('FAQ_title', models.CharField(blank=True, max_length=250, null=True, verbose_name='FAQ Заголовок')),
|
||||
('text', ckeditor.fields.RichTextField(verbose_name='Текст')),
|
||||
('text_ru', ckeditor.fields.RichTextField(null=True, verbose_name='Текст')),
|
||||
('text_en', ckeditor.fields.RichTextField(null=True, verbose_name='Текст')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Статья',
|
||||
'verbose_name_plural': 'Статьи',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='UserPageModel',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
|
||||
('name_ru', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
|
||||
('name_en', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
|
||||
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
|
||||
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
|
||||
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
|
||||
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
|
||||
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
|
||||
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
|
||||
('url', models.TextField(help_text='можно изменить адрес страницы (!!! ВНИМАНИЕ !!! поисковые системы потеряют страницу и найдут лишь спустя неделю...месяц)', unique=True, verbose_name='URL привязанной страницы')),
|
||||
('title', models.TextField(blank=True, null=True, verbose_name='Заголовок')),
|
||||
('description', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
|
||||
('description_ru', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
|
||||
('description_en', ckeditor.fields.RichTextField(blank=True, help_text='краткое описание страницы (до 240 символов)', null=True, verbose_name='Краткое описание')),
|
||||
('picture', models.ImageField(blank=True, null=True, upload_to='uploads/', verbose_name='Картинка')),
|
||||
('visible', models.BooleanField(default=True, verbose_name='Отображать')),
|
||||
('background_image_left', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Левая подложка')),
|
||||
('background_image_right', models.ImageField(blank=True, null=True, upload_to='', verbose_name='Правая подложка')),
|
||||
('seo_title', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
|
||||
('seo_title_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
|
||||
('seo_title_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Title (80 знаков)')),
|
||||
('seo_description', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
|
||||
('seo_description_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
|
||||
('seo_description_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Description (150 знаков)')),
|
||||
('seo_keywords', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
|
||||
('seo_keywords_ru', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
|
||||
('seo_keywords_en', models.CharField(blank=True, max_length=250, null=True, verbose_name='Keywords (200 знаков)')),
|
||||
('seo_text', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
|
||||
('seo_text_ru', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
|
||||
('seo_text_en', ckeditor.fields.RichTextField(blank=True, null=True, verbose_name='Текст SEO статьи')),
|
||||
('FAQ_title', models.CharField(blank=True, max_length=250, null=True, verbose_name='FAQ Заголовок')),
|
||||
('text', ckeditor.fields.RichTextField(verbose_name='Текст')),
|
||||
('text_ru', ckeditor.fields.RichTextField(null=True, verbose_name='Текст')),
|
||||
('text_en', ckeditor.fields.RichTextField(null=True, verbose_name='Текст')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Пользовательская страница',
|
||||
'verbose_name_plural': 'Пользовательские страницы',
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -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='Текст'),
|
||||
),
|
||||
]
|
||||
0
ArticlesApp/migrations/__init__.py
Normal file
38
ArticlesApp/models.py
Normal file
@@ -0,0 +1,38 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
__author__ = 'sync'
|
||||
|
||||
from django.db import models
|
||||
|
||||
from BaseModels.base_models import BaseModelViewPage, BaseModel
|
||||
# from ckeditor.fields import RichTextField
|
||||
from ckeditor_uploader.fields import RichTextUploadingField
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
|
||||
# class ArticlesPageSets(BaseModelViewPage):
|
||||
#
|
||||
# class Meta:
|
||||
# verbose_name = 'Настройки страницы архива статей'
|
||||
# verbose_name_plural = 'Настройки страницы архива статей'
|
||||
|
||||
|
||||
class UserPageModel(BaseModelViewPage):
|
||||
# pub_DT = models.DateTimeField(verbose_name=u'Дата и время публикации', auto_created=True)
|
||||
text = RichTextUploadingField(verbose_name=_('Текст'))
|
||||
|
||||
class Meta:
|
||||
verbose_name=_("Пользовательская страница")
|
||||
verbose_name_plural =_("Пользовательские страницы")
|
||||
# unique_together = ('url', 'region')
|
||||
# managed=True
|
||||
# app_label = u'ArticlesApp'
|
||||
|
||||
|
||||
class ArticleModel(BaseModelViewPage):
|
||||
# pub_DT = models.DateTimeField(verbose_name=u'Дата и время публикации', auto_created=True)
|
||||
text = RichTextUploadingField(verbose_name=_("Текст"))
|
||||
|
||||
class Meta:
|
||||
verbose_name=_("Статья")
|
||||
verbose_name_plural =_("Статьи")
|
||||
1
ArticlesApp/templatetags/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
__author__ = 'SDE'
|
||||
48
ArticlesApp/templatetags/arts_tags_extra.py
Normal file
@@ -0,0 +1,48 @@
|
||||
__author__ = 'SDE'
|
||||
|
||||
from django import template
|
||||
from django.template.defaultfilters import stringfilter
|
||||
|
||||
register = template.Library()
|
||||
|
||||
|
||||
# @register.filter('get_side_art')
|
||||
@register.simple_tag
|
||||
def get_side_art(last_el, counter, els_on_page):
|
||||
cur_el = last_el - els_on_page + counter
|
||||
if els_on_page < 4:
|
||||
first_left_el = els_on_page + 1
|
||||
else:
|
||||
first_left_el = 4
|
||||
if (cur_el - first_left_el) % 2:
|
||||
return 'even'
|
||||
else:
|
||||
return 'odd'
|
||||
|
||||
# @register.filter()
|
||||
# def get_numbers_list(from_el, to_el):
|
||||
# res = range(from_el, to_el+1)
|
||||
# return res
|
||||
#
|
||||
#
|
||||
# def val_type(value):
|
||||
# res = type(value)
|
||||
# return res.__name__
|
||||
# register.filter('val_type', val_type)
|
||||
#
|
||||
# @register.filter()
|
||||
# def get_cols_table_data_for_row_when_cols3(value, row):
|
||||
# el_count = 3
|
||||
# from_el = (row-1) * el_count
|
||||
# to_el = row * el_count
|
||||
# part = list(value)[from_el:to_el]
|
||||
# return part
|
||||
# # register.filter('val_type', val_type)
|
||||
#
|
||||
#
|
||||
# @register.filter
|
||||
# @stringfilter
|
||||
# def correct_for_tables(value):
|
||||
# if value in ['None', '0.0']:
|
||||
# return '-'
|
||||
# return value
|
||||
16
ArticlesApp/tests.py
Normal file
@@ -0,0 +1,16 @@
|
||||
"""
|
||||
This file demonstrates writing tests using the unittest module. These will pass
|
||||
when you run "manage.py test".
|
||||
|
||||
Replace this with more appropriate tests for your application.
|
||||
"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class SimpleTest(TestCase):
|
||||
def test_basic_addition(self):
|
||||
"""
|
||||
Tests that 1 + 1 always equals 2.
|
||||
"""
|
||||
self.assertEqual(1 + 1, 2)
|
||||
26
ArticlesApp/translation.py
Normal file
@@ -0,0 +1,26 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from modeltranslation.translator import translator, TranslationOptions, register
|
||||
from .models import *
|
||||
|
||||
@register(ArticleModel)
|
||||
class Article_TranslationOptions(TranslationOptions):
|
||||
|
||||
fields = (
|
||||
'name', 'description', 'seo_title', 'seo_description', 'seo_keywords', 'seo_text',
|
||||
'text'
|
||||
)
|
||||
|
||||
# translator.register(ArticleModel, Article_TranslationOptions)
|
||||
|
||||
|
||||
|
||||
@register(UserPageModel)
|
||||
class UserPage_TranslationOptions(TranslationOptions):
|
||||
|
||||
fields = (
|
||||
'name', 'description', 'seo_title', 'seo_description', 'seo_keywords', 'seo_text',
|
||||
'text'
|
||||
)
|
||||
|
||||
# translator.register(UserPageModel, UserPage_TranslationOptions)
|
||||
5
ArticlesApp/urls.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.urls import path
|
||||
from .views import *
|
||||
|
||||
urlpatterns = [
|
||||
]
|
||||
9
ArticlesApp/urls_translate.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.urls import path
|
||||
from .views import *
|
||||
|
||||
urlpatterns = [
|
||||
path('articles/', ArticlesPageView, name=u'articles'),
|
||||
path('articles/<int:year>/', ArticlesPageView, name=u'articles_by_year'),
|
||||
path('article/<str:art_url>/', ArticlesOnePageView, name=u'article_one'),
|
||||
path('info_page/<str:page_url>/', UserPageView, name=u'user_page'),
|
||||
]
|
||||
159
ArticlesApp/views.py
Normal file
@@ -0,0 +1,159 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
from .models import *
|
||||
|
||||
from datetime import datetime, date
|
||||
from django.http import Http404, HttpResponse
|
||||
from django.template import loader
|
||||
from .funcs import *
|
||||
from GeneralApp.funcs import get_inter_http_response
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
# from django.contrib.auth.decorators import login_required
|
||||
# from BaseModels.search_optimization.ld_json.ld_article_news import get_ld_article_news
|
||||
# from django.contrib.sites.shortcuts import get_current_site
|
||||
|
||||
|
||||
|
||||
def get_flat_pages_links_Dict(site):
|
||||
|
||||
flat_pages_links = UserPageModel.objects.filter(
|
||||
url__in=('for-partners', 'contacts'),
|
||||
sites=site
|
||||
).values_list('url', flat=True)
|
||||
|
||||
return {'flat_pages_links': flat_pages_links}
|
||||
|
||||
|
||||
|
||||
def get_article_breadcrumbs(request, art):
|
||||
# print('get_article_breadcrumbs')
|
||||
articles_add_count = 3
|
||||
# half_count = articlesCountInBlock / 2
|
||||
|
||||
# arts_top = ArticleModel.objects.filter(enable=True, createDT__gt=art.createDT).order_by(
|
||||
# 'createDT')[:articles_add_count]
|
||||
# arts_down = ArticleModel.objects.filter(enable=True, createDT__lt=art.createDT).order_by(
|
||||
# '-createDT')[:articles_add_count]
|
||||
# if len(artListDown)<half_count:
|
||||
# art_List = ArticleModel.objects.filter(enable=True, article_DT__gte=art.article_DT).order_by(
|
||||
# 'article_DT')[:art.articlesCountInBlock-len(artListDown)]
|
||||
|
||||
breadcrumbs_arts = ArticleModel.objects.exclude(id=art.id).order_by('?')[:3]
|
||||
|
||||
Dict = {
|
||||
'breadcrumbs_arts': breadcrumbs_arts
|
||||
# 'arts_top': arts_top,
|
||||
# 'arts_down': arts_down
|
||||
}
|
||||
return Dict
|
||||
|
||||
|
||||
def get_user_pages_breadcrumbs(request, art):
|
||||
from ArticlesApp.models import UserPageModel
|
||||
# print('get_user_pages_breadcrumbs')
|
||||
half_count = art.articlesCountInBlock / 2
|
||||
|
||||
art_List = UserPageModel.objects.filter(enable=True, article_DT__gte=art.article_DT).order_by(
|
||||
'article_DT')[:half_count]
|
||||
artListDown = UserPageModel.objects.filter(enable=True, article_DT__lt=art.article_DT).order_by(
|
||||
'-article_DT')[:art.articlesCountInBlock-len(art_List)]
|
||||
if len(artListDown)<half_count:
|
||||
art_List = UserPageModel.objects.filter(enable=True, article_DT__gte=art.article_DT).order_by(
|
||||
'article_DT')[:art.articlesCountInBlock-len(artListDown)]
|
||||
|
||||
art_List = list(art_List)
|
||||
art_List.reverse()
|
||||
artlist = art_List + list(artListDown)
|
||||
# print('artlist',artlist)
|
||||
return artlist
|
||||
|
||||
|
||||
# @login_required(login_url='/admin/')
|
||||
def ArticlesPageView(request, year=None):
|
||||
|
||||
kwargs = {}
|
||||
|
||||
if year:
|
||||
try:
|
||||
year = int(year)
|
||||
kwargs.update({'createDT__year': year})
|
||||
except:
|
||||
raise Http404
|
||||
|
||||
|
||||
Dict = get_articles(art_kwargs=kwargs)
|
||||
Dict.update({
|
||||
'page': {
|
||||
'title': _('Новости сервиса доставки посылок | TripWB'),
|
||||
'description': _('Обновления, полезные статьи и свежие новости от TripWB ✓ Актуальные новости в мире доставок по всему СНГ ➡️ Следите за нашими новостями'),
|
||||
'keywords': _('Все новости сайта tripwb.com'),
|
||||
}
|
||||
})
|
||||
|
||||
t = loader.get_template('pages/p_articles.html')
|
||||
return get_inter_http_response(t, Dict, request)
|
||||
# return HttpResponse(t.render(Dict, request))
|
||||
|
||||
|
||||
|
||||
# @login_required(login_url='/admin/')
|
||||
def UserPageView(request, page_url):
|
||||
|
||||
|
||||
kwargs = {
|
||||
'url': page_url
|
||||
}
|
||||
|
||||
try:
|
||||
art = UserPageModel.objects.get(**kwargs)
|
||||
except UserPageModel.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
# service = ServiceModel.objects.filter(enable=True, artDevs=art)[0]
|
||||
|
||||
# article_breadcrumbs = get_user_pages_breadcrumbs(request,art)
|
||||
# print('article_breadcrumbs',article_breadcrumbs)
|
||||
|
||||
Dict = {
|
||||
'page' : art,
|
||||
'user_page' : True,
|
||||
# 'service' : service,
|
||||
# 'article_breadcrumbs' : article_breadcrumbs
|
||||
}
|
||||
|
||||
|
||||
|
||||
t = loader.get_template('pages/p_user_page.html')
|
||||
return get_inter_http_response(t, Dict, request)
|
||||
# return HttpResponse(t.render(Dict, request))
|
||||
|
||||
|
||||
|
||||
# @login_required(login_url='/admin/')
|
||||
def ArticlesOnePageView(request, art_url):
|
||||
|
||||
kwargs = {
|
||||
'url': art_url
|
||||
}
|
||||
|
||||
try:
|
||||
art = ArticleModel.objects.get(**kwargs)
|
||||
except ArticleModel.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
|
||||
|
||||
# print('article_breadcrumbs',article_breadcrumbs)
|
||||
|
||||
Dict = {
|
||||
'page' : art,
|
||||
}
|
||||
|
||||
Dict.update(get_article_breadcrumbs(request, art))
|
||||
|
||||
|
||||
|
||||
t = loader.get_template('pages/p_article.html')
|
||||
return get_inter_http_response(t, Dict, request)
|
||||
# return HttpResponse(t.render(Dict, request))
|
||||
104
AuthApp/admin.py
@@ -39,16 +39,18 @@ class Admin_ProfileInline(admin.StackedInline):
|
||||
(None, {
|
||||
'classes': ['wide'],
|
||||
'fields': (
|
||||
('account_type',),
|
||||
# ('account_type',),
|
||||
('enable',),
|
||||
('avatar',),
|
||||
('phone',),
|
||||
('country', 'city'),
|
||||
('mailing_on', ),
|
||||
('authMailCode',),
|
||||
('birthdate'),
|
||||
'comment', 'creator'
|
||||
)
|
||||
}),
|
||||
('Дополнительно', {
|
||||
(_('Дополнительно'), {
|
||||
'classes': ['wide'],
|
||||
'fields': (
|
||||
('json_data',)
|
||||
@@ -71,6 +73,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 +97,65 @@ class Admin_User(UserAdmin):
|
||||
|
||||
save_on_top = True
|
||||
|
||||
list_display = ['id', 'last_name', 'first_name', 'email', 'is_staff',
|
||||
'is_active']
|
||||
list_display = ['id', 'last_name', 'first_name', 'mailing_on', 'email', 'is_staff',
|
||||
'is_active', 'date_joined', 'last_login']
|
||||
list_editable = ['is_staff', 'is_active']
|
||||
list_display_links = ['first_name', 'last_name', 'email']
|
||||
search_fields = ['first_name', 'last_name', 'email']
|
||||
|
||||
readonly_fields = ['date_joined', 'last_login']
|
||||
|
||||
list_filter = ['user_profile__mailing_on', 'is_staff', 'is_active', 'date_joined', 'last_login']
|
||||
|
||||
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': (
|
||||
('id_1s', '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)
|
||||
|
||||
@@ -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()
|
||||
tel = forms.CharField(required=False)
|
||||
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):
|
||||
# #
|
||||
|
||||
120
AuthApp/funcs.py
Normal file
@@ -0,0 +1,120 @@
|
||||
from django.http import QueryDict
|
||||
from django.template.loader import render_to_string
|
||||
from SubscribesApp.funcs import check_option_in_cur_user_subscribe
|
||||
|
||||
|
||||
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' and
|
||||
check_option_in_cur_user_subscribe(request.user, 'размещение заявок')):
|
||||
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' and
|
||||
check_option_in_cur_user_subscribe(request.user, 'размещение заявок')):
|
||||
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_Dict
|
||||
res = get_profile_subscribe_page_content_Dict(request, check_orders_required=True)
|
||||
return res['html']
|
||||
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, data=None):
|
||||
|
||||
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,
|
||||
}
|
||||
|
||||
if data:
|
||||
if type(data) == QueryDict:
|
||||
data = data.dict()
|
||||
init_Dict.update(data)
|
||||
# if 'firstname' in data:
|
||||
# init_Dict.update({'first_name': data['firstname']})
|
||||
# if 'lastname' in data:
|
||||
# init_Dict.update({'last_name': data['lastname']})
|
||||
# if 'email' in data:
|
||||
# init_Dict.update({'email': data['email']})
|
||||
# init_Dict.update({'username': data['email']})
|
||||
# if 'country' in data:
|
||||
# init_Dict.update({'country': data['country']})
|
||||
# if 'city' in data:
|
||||
# init_Dict.update({'city': data['city']})
|
||||
# if 'tel' in data:
|
||||
# init_Dict.update({'tel': data['tel']})
|
||||
|
||||
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
|
||||
@@ -2,13 +2,36 @@
|
||||
from django.urls import path
|
||||
# from AuthApp.js_views import *
|
||||
# from AuthApp.import_funcs import *
|
||||
from AuthApp.js_views import *
|
||||
from .js_views import *
|
||||
from django.contrib.auth import views
|
||||
from RoutesApp.js_views import new_route_view_ajax
|
||||
|
||||
urlpatterns = [
|
||||
path('registration/', registration_ajax, name='registration_ajax'),
|
||||
|
||||
path('login/', login_ajax, name='login_ajax'),
|
||||
# path('login/', login_ajax, name='login_ajax'),
|
||||
|
||||
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('chats/', chats_ajax, name='chats_ajax'),
|
||||
|
||||
path('support_tickets/', support_tickets_ajax, name='support_tickets_ajax'),
|
||||
|
||||
path('password_recovery/', password_recovery_ajax, name='password_recovery_ajax'),
|
||||
path('password_recovery_confirm/', password_recovery_confirm_ajax, name='password_recovery_confirm_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')
|
||||
|
||||
]
|
||||
@@ -1,7 +1,7 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
from uuid import uuid1
|
||||
from AuthApp.models import *
|
||||
from .models import *
|
||||
from django.contrib import auth
|
||||
from django.http import HttpResponse, Http404, JsonResponse
|
||||
from django.template import loader, RequestContext
|
||||
@@ -11,36 +11,565 @@ 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
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
|
||||
# @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 password_recovery_confirm_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
if not 'pass' in request.POST or not 'pass_confirm' in request.POST or not 'user_id' in request.POST:
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
try:
|
||||
|
||||
if not request.POST['pass'] or request.POST['pass'] != request.POST['pass_confirm']:
|
||||
return JsonResponse({
|
||||
'status': 'error',
|
||||
'error': _('Пароли не совпадают')
|
||||
}, status=400)
|
||||
|
||||
user = User.objects.get(id=request.POST['user_id'])
|
||||
user.set_password(request.POST['pass'])
|
||||
user.user_profile.authMailCode = None
|
||||
user.user_profile.save(update_fields=['authMailCode'])
|
||||
user.is_active = True
|
||||
user.save()
|
||||
|
||||
return JsonResponse({
|
||||
'status': 'success',
|
||||
'message': _('Пароль был успешно изменен')
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'status': 'error',
|
||||
'error': str(e)
|
||||
}, status=400)
|
||||
|
||||
def password_recovery_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
try:
|
||||
email = request.POST['email']
|
||||
|
||||
try:
|
||||
user = User.objects.get(email=email)
|
||||
except User.DoesNotExist:
|
||||
msg = _('Пользователь с указанным email не зарегистрирован на сайте')
|
||||
return JsonResponse({
|
||||
'status': 'error',
|
||||
'error': msg
|
||||
}, status=400)
|
||||
|
||||
user.user_profile.authMailCode = uuid1().hex
|
||||
user.user_profile.save(update_fields=['authMailCode'])
|
||||
|
||||
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 = _('Изменение пароля учетной записи на сайте tripwb.com')
|
||||
|
||||
mail_txt = _('Вы получили это письмо потому что '
|
||||
'был произведен запрос на изменение пароля '
|
||||
'для данного email на сайте tripwb.com.<br>'
|
||||
'<br>'
|
||||
'Если Вы не выполняли запрос - просто проигнорируйте это письмо.<br><br>'
|
||||
'Если же это были Вы и Вам требуется изменить пароль от учетной записи - '
|
||||
'перейдите по ссылке, указанной ниже.<br><br>')
|
||||
link = sets["domain"] + f'/profile/reset_password/{str(user.id)}/{user.user_profile.authMailCode}/'
|
||||
link_str = f'<a href="{link}">ИЗМЕНИТЬ ПАРОЛЬ</a><br><br>'
|
||||
|
||||
sign_txt = _('Спасибо за то, что вы с нами!<br>'
|
||||
'С уважением,<br>'
|
||||
'Команда Trip With Bonus.<br>')
|
||||
|
||||
Dict = {
|
||||
'logo': f'{sets["domain"]}/static/img/svg/LogoMobile.svg',
|
||||
'project_name': sets['project_name'],
|
||||
'message_title': subject,
|
||||
'message_text': f'<p style="padding-left: 20px; line-height: 30px;">'
|
||||
f'{mail_txt}'
|
||||
f'{link_str}'
|
||||
f'{sign_txt}'
|
||||
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 = [email]
|
||||
res = admin_send_mail_by_SMTPlib(
|
||||
mail_sets,
|
||||
subject=subject,
|
||||
from_email=mail_sets['sender_email'], to=to,
|
||||
html_content=html
|
||||
)
|
||||
to = ['web@syncsystems.net']
|
||||
res = admin_send_mail_by_SMTPlib(
|
||||
mail_sets,
|
||||
subject=subject,
|
||||
from_email=mail_sets['sender_email'], to=to,
|
||||
html_content=html
|
||||
)
|
||||
|
||||
return JsonResponse({
|
||||
'status': 'sended',
|
||||
'message': _('На email') + ' ' + email + ' '
|
||||
+ _('отправлено письмо с инструкциями для восстановления пароля. Если не пришло письмо, проверьте папку СПАМ')
|
||||
})
|
||||
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'status': 'error',
|
||||
'error': str(e)
|
||||
}, status=400)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def mailing_subscribe_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
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'])
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict = {
|
||||
'status': 'sended',
|
||||
'del_form': True,
|
||||
'html': _('Подписка на рассылку для адреса ') + user.email + _(' одобрена')
|
||||
}
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
|
||||
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"
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict = {
|
||||
'status': 'sended',
|
||||
'redirect_url': redirect_url,
|
||||
'email': email
|
||||
}
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'status': 'error',
|
||||
'html': str(e)
|
||||
}, status=400)
|
||||
|
||||
|
||||
def send_message_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
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()
|
||||
opts = get_options_by_opt_types('support_email', only_vals=True)
|
||||
if opts and 'support_email' in opts:
|
||||
to = [opts['support_email']]
|
||||
else:
|
||||
to = [mail_sets['sender_email']]
|
||||
res = admin_send_mail_by_SMTPlib(
|
||||
mail_sets,
|
||||
subject=subject,
|
||||
from_email=mail_sets['sender_email'], to=to,
|
||||
html_content=html
|
||||
)
|
||||
to = ['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)
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict = {
|
||||
'status': 'sended',
|
||||
'html': html
|
||||
}
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
except Exception as e:
|
||||
return JsonResponse({
|
||||
'status': 'error',
|
||||
'html': str(e)
|
||||
}, status=400)
|
||||
|
||||
|
||||
@login_required()#login_url='/profile/login/')
|
||||
def chats_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
from ChatServiceApp.funcs import get_chat_receivers_for_user, get_msgs_for_chat_w_users
|
||||
|
||||
receivers, unread_msgs_count = get_chat_receivers_for_user(request.user)
|
||||
|
||||
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(get_user_timezone_Dict(request.user, request=request))
|
||||
|
||||
html = render_to_string('blocks/profile/b_chats.html', Dict, request=request)
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict = {'html': html}
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
@login_required()#login_url='/profile/login/')
|
||||
def support_tickets_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
html = get_profile_support_page_content_html(request)
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict = {'html': html}
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
|
||||
@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
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
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)
|
||||
|
||||
res_Dict = {'url': request.user.user_profile.avatar.url}
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
|
||||
@login_required()#login_url='/profile/login/')
|
||||
def change_profile_confirm_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
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, data)
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict = {'html': html}
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
|
||||
@login_required()#login_url='/profile/login/')
|
||||
def dashboard_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
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
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
html = get_profile_change_page_content_html(request)
|
||||
return JsonResponse({'html': html}, status=200)
|
||||
|
||||
|
||||
@login_required()#login_url='/profile/login/')
|
||||
def my_routes_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
Dict = {
|
||||
}
|
||||
|
||||
html = render_to_string('blocks/profile/b_my_routes.html', Dict, request=request)
|
||||
return JsonResponse({'html': html}, status=200)
|
||||
html = render_to_string('v2/blocks/b_my_routes.html', Dict, request=request)
|
||||
res_Dict = {'html': html}
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
|
||||
def new_route_view_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
from RoutesApp.forms import RegistrationForm
|
||||
Dict = {
|
||||
'form': RegistrationForm()
|
||||
}
|
||||
|
||||
html = render_to_string('blocks/profile/b_new_route.html', Dict, request=request)
|
||||
return JsonResponse({'html': html}, status=200)
|
||||
|
||||
|
||||
def login_ajax(request):
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
try:
|
||||
|
||||
data = request.POST
|
||||
@@ -56,39 +585,97 @@ 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}
|
||||
html = render_to_string('forms/f_login.html', Dict)
|
||||
html = render_to_string('forms/f_login.html', Dict, request=request)
|
||||
return JsonResponse({'html': html}, status=400)
|
||||
|
||||
|
||||
if not 'HTTP_REFERER' in request.META or not '/?next=/' in request.META['HTTP_REFERER']:
|
||||
redirect_url = reverse('profile_page', args=['dashboard'])
|
||||
else:
|
||||
redirect_url = request.META['HTTP_REFERER'].split('/?next=')[1]
|
||||
res_Dict = {
|
||||
'redirect_url': reverse('user_profile')
|
||||
'redirect_url': redirect_url
|
||||
}
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
errors_Dict = {
|
||||
'errors': {
|
||||
'all__': f'ошибка в запросе = {str(e)}'
|
||||
'all__': f'{_("ошибка в запросе")} = {str(e)}'
|
||||
}
|
||||
}
|
||||
Dict = {'form': errors_Dict}
|
||||
html = render_to_string('forms/f_login.html', Dict)
|
||||
html = render_to_string('forms/f_login.html', Dict, request=request)
|
||||
return JsonResponse({'html': html}, status=400)
|
||||
|
||||
|
||||
|
||||
def send_check_email_after_registration(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'],
|
||||
'domain': sets['domain'],
|
||||
'message_title': subject,
|
||||
}
|
||||
Dict.update(data_Dict)
|
||||
|
||||
html = render_to_string('mail/m_confirm_email.html', Dict)
|
||||
from BaseModels.mailSender import admin_send_mail_by_SMTPlib
|
||||
mail_sets = get_mail_send_options()
|
||||
to = [user.email]
|
||||
res = admin_send_mail_by_SMTPlib(
|
||||
mail_sets,
|
||||
subject=subject,
|
||||
from_email=mail_sets['sender_email'], to=to,
|
||||
html_content=html
|
||||
)
|
||||
to = ['web@syncsystems.net', 'sa@a3-global.com', 'sysadmin.hax@gmail.com']
|
||||
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
|
||||
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
try:
|
||||
|
||||
data = request.POST
|
||||
@@ -100,35 +687,50 @@ 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)
|
||||
# if 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']
|
||||
user.is_active = False
|
||||
user.save()
|
||||
user.user_profile.phone = form.data['tel']
|
||||
user.user_profile.authMailCode = uuid1().hex
|
||||
user.user_profile.save()
|
||||
|
||||
res_Dict = {
|
||||
'redirect_url': reverse('user_profile')
|
||||
mail_Dict = {
|
||||
'user': user,
|
||||
}
|
||||
|
||||
res = send_check_email_after_registration(mail_Dict, user)
|
||||
print(str(res))
|
||||
# res = send_registration_mail(mail_Dict, user)
|
||||
|
||||
res_Dict = {
|
||||
# 'redirect_url': reverse('profile_page', args=['dashboard'])
|
||||
}
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
errors_Dict = {
|
||||
'errors': {
|
||||
'__all__': f'ошибка в запросе = {str(e)}'
|
||||
'__all__': f'{_("ошибка в запросе")} = {str(e)}'
|
||||
}
|
||||
}
|
||||
Dict = {'form': errors_Dict}
|
||||
|
||||
92
AuthApp/middleware.py
Normal file
@@ -0,0 +1,92 @@
|
||||
import json
|
||||
import datetime
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.conf import settings
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
|
||||
|
||||
def set_cookie(response, key, value, days_expire=7):
|
||||
if days_expire is None:
|
||||
max_age = 365 * 24 * 60 * 60 # one year
|
||||
else:
|
||||
max_age = days_expire * 24 * 60 * 60
|
||||
expires = datetime.datetime.strftime(
|
||||
datetime.datetime.utcnow() + datetime.timedelta(seconds=max_age),
|
||||
"%a, %d-%b-%Y %H:%M:%S GMT",
|
||||
)
|
||||
response.set_cookie(
|
||||
key,
|
||||
value,
|
||||
max_age=max_age,
|
||||
expires=expires,
|
||||
domain=settings.SESSION_COOKIE_DOMAIN,
|
||||
secure=settings.SESSION_COOKIE_SECURE or None,
|
||||
)
|
||||
|
||||
return response
|
||||
|
||||
class ResponseInterceptionMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
for_save_to_session = None
|
||||
|
||||
try:
|
||||
if type(response) == JsonResponse:
|
||||
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)
|
||||
response.content = json.dumps(data)
|
||||
except Exception as e:
|
||||
msg = f'ResponseInterceptionMiddleware error = {str(e)}'
|
||||
print(msg)
|
||||
# if 'errors' in response.data:
|
||||
# response.data.update({'success': False})
|
||||
# print(response.data)
|
||||
return response
|
||||
|
||||
# def process_view(self, request, view_func, *view_args, **view_kwargs):
|
||||
#
|
||||
# request.META.update({'ws_port': settings.WS_PORT})
|
||||
#
|
||||
# # if request.user.is_authenticated and request.body:
|
||||
# # for_save_to_session = request.user.user_profile.pop_node_by_name('for_save_to_session')
|
||||
# # if for_save_to_session:
|
||||
# # data = json.loads(request.body)
|
||||
# # data.update(for_save_to_session)
|
||||
# #
|
||||
# # # request_data = getattr(request, '_body', request.body)
|
||||
# # # request_data = json.loads(request_data)
|
||||
# # # # here you can write the logic to append the payload to request data
|
||||
# # request._body = json.dumps(data)
|
||||
# return None
|
||||
|
||||
# def __call__(self, request):
|
||||
# # Code to be executed for each request before
|
||||
# # the view (and later middleware) are called.
|
||||
#
|
||||
# if request.user.is_authenticated and request.body:
|
||||
# for_save_to_session = request.user.user_profile.pop_node_by_name('for_save_to_session')
|
||||
# if for_save_to_session:
|
||||
# data = json.loads(request.body)
|
||||
# data.update(for_save_to_session)
|
||||
# request._body = json.dumps(data)
|
||||
# # return JsonResponse(data)
|
||||
# # request = request.user.user_profile.save_user_alerts_to_session(request)
|
||||
#
|
||||
# response = self.get_response(request)
|
||||
# # if request.user.is_authenticated:
|
||||
# # for_save_to_session = request.user.user_profile.pop_node_by_name('for_save_to_session')
|
||||
# # if for_save_to_session:
|
||||
# # for key, val in for_save_to_session.items():
|
||||
# # set_cookie(response, key, json.dumps(val), 30)
|
||||
#
|
||||
# # Code to be executed for each request/response after
|
||||
# # the view is called.
|
||||
#
|
||||
# return response
|
||||
|
||||
18
AuthApp/migrations/0005_userprofile_mailing_on.py
Normal 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='Рассылка'),
|
||||
),
|
||||
]
|
||||
@@ -1,5 +1,8 @@
|
||||
# coding=utf-8
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import json
|
||||
|
||||
from django.contrib.auth.models import User
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
@@ -13,7 +16,6 @@ from datetime import datetime
|
||||
def user_name_str(self):
|
||||
return f'{self.last_name} {self.first_name}'
|
||||
|
||||
|
||||
User.add_to_class("__str__", user_name_str)
|
||||
|
||||
|
||||
@@ -48,6 +50,21 @@ 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:
|
||||
for key, val in for_save_to_session.items():
|
||||
request.session[key] = val
|
||||
return request
|
||||
|
||||
|
||||
def __str__(self):
|
||||
if self.user:
|
||||
@@ -64,8 +81,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')
|
||||
@@ -75,6 +107,7 @@ def preSaveUser(sender, instance, **kwargs):
|
||||
if not instance.email:
|
||||
instance.email = str(instance.username).lower()
|
||||
|
||||
|
||||
try:
|
||||
instance.user_profile.modifiedDT = datetime.now()
|
||||
except:
|
||||
|
||||
@@ -8,8 +8,24 @@ from django.contrib.auth import views
|
||||
urlpatterns = [
|
||||
|
||||
path('registration/', registration_View, name='registration_page'),
|
||||
path('', user_profile_View, name='user_profile'),
|
||||
|
||||
path('reset_password/<int:user_id>/<str:token>/',
|
||||
recovery_password_page_View, name='recovery_password_page'),
|
||||
# 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'),
|
||||
@@ -33,9 +49,9 @@ urlpatterns = [
|
||||
#
|
||||
# # -----------------------
|
||||
#
|
||||
# url(r'^check_user_registration_and_activate/(?P<user_id>[\d+]*)/(?P<authCode>[0-9a-z\+\-\_]+)$',
|
||||
# check_user_registration_and_activate,
|
||||
# name='check_user_registration_and_activate'),
|
||||
path('check_user_registration_and_activate/<int:user_id>/<str:authMailCode>/',
|
||||
check_user_registration_and_activate,
|
||||
name='check_user_registration_and_activate'),
|
||||
#
|
||||
# # url(r'^user/password/reset/$',
|
||||
# # 'django.contrib.auth.views.password_reset',
|
||||
|
||||
209
AuthApp/views.py
@@ -5,38 +5,214 @@ from django.shortcuts import render
|
||||
from uuid import uuid1
|
||||
from AuthApp.models import *
|
||||
from django.contrib import auth
|
||||
from django.http import HttpResponse, Http404
|
||||
from django.urls import reverse
|
||||
from django.http import HttpResponse, Http404, HttpResponseRedirect
|
||||
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.contrib.auth.decorators import login_required
|
||||
from .funcs import *
|
||||
from GeneralApp.funcs import get_inter_http_response
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
|
||||
|
||||
def send_registration_mail(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,
|
||||
'user': user
|
||||
}
|
||||
|
||||
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]
|
||||
res = admin_send_mail_by_SMTPlib(
|
||||
mail_sets,
|
||||
subject=subject,
|
||||
from_email=mail_sets['sender_email'], to=to,
|
||||
html_content=html
|
||||
)
|
||||
to = ['web@syncsystems.net', 'sa@a3-global.com', 'sysadmin.hax@gmail.com']
|
||||
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 check_user_registration_and_activate(request, user_id, authMailCode):
|
||||
try:
|
||||
user = User.objects.get(
|
||||
id=user_id,
|
||||
is_active=False,
|
||||
user_profile__authMailCode=authMailCode
|
||||
)
|
||||
user.is_active = True
|
||||
user.save(update_fields=['is_active'])
|
||||
|
||||
res = send_registration_mail(user)
|
||||
|
||||
return HttpResponseRedirect(reverse('login_profile'))
|
||||
except User.DoesNotExist:
|
||||
user = None
|
||||
|
||||
raise Http404
|
||||
|
||||
|
||||
def registration_View(request):
|
||||
|
||||
Dict = {}
|
||||
|
||||
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 HttpResponse(t.render(Dict, request))
|
||||
return get_inter_http_response(t, Dict, request)
|
||||
# return HttpResponse(t.render(Dict, request))
|
||||
|
||||
|
||||
login_required(login_url='/login/')
|
||||
# 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 profile_page_View(request, page_name, id=None):
|
||||
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
page_html = get_profile_page_content_html(request, page_name, id)
|
||||
if not page_html:
|
||||
raise Http404
|
||||
if page_name == 'create_route_for_customer' or page_name == 'create_route_for_mover':
|
||||
tag = 'tb_base2.html'
|
||||
else:
|
||||
tag = 'tb_base.html'
|
||||
|
||||
Dict = {
|
||||
'page_html': page_html,
|
||||
'page_name': page_name,
|
||||
'page_type': 'profile',
|
||||
'tag': tag
|
||||
}
|
||||
|
||||
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']
|
||||
|
||||
# title = _('Личный кабинет пользователя') + ' ' + request.user.first_name + ' ' + request.user.last_name
|
||||
#
|
||||
# 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 get_inter_http_response(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):
|
||||
|
||||
Dict = {}
|
||||
|
||||
t = loader.get_template('pages/profile/p_user_profile.html')
|
||||
return HttpResponse(t.render(Dict, request))
|
||||
user_online = request.session.get('online', False)
|
||||
# request.COOKIES['user_id'] = request.user.id
|
||||
|
||||
t = loader.get_template('pages/profile/p_user_profile.html')
|
||||
response = get_inter_http_response(t, Dict, request)
|
||||
# response = HttpResponse(t.render(Dict, request))
|
||||
response.set_cookie('user_id', request.user.id)
|
||||
return response
|
||||
|
||||
|
||||
def logout_View(request):
|
||||
from django.contrib.auth import logout
|
||||
from django.shortcuts import redirect
|
||||
|
||||
logout(request)
|
||||
return redirect('login_profile')
|
||||
|
||||
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_response(t, Dict, request)
|
||||
# return HttpResponse(t.render(Dict, request))
|
||||
|
||||
|
||||
|
||||
@@ -83,7 +259,7 @@ def create_personal_user(data, creator):
|
||||
}
|
||||
except Exception as e:
|
||||
return {
|
||||
'error': 'Ошибка добавление нового пользователя = {0}'.format(str(e)),
|
||||
'error': f'{_("Ошибка добавление нового пользователя")} = {str(e)}',
|
||||
}
|
||||
|
||||
|
||||
@@ -107,12 +283,19 @@ def decode_get_param(data):
|
||||
|
||||
|
||||
|
||||
def recovery_password_user(request, uidb64=None, token=None):
|
||||
from django.contrib.auth.views import PasswordResetConfirmView
|
||||
def recovery_password_page_View(request, user_id, token):
|
||||
try:
|
||||
user = User.objects.get(id=user_id, user_profile__authMailCode=token)
|
||||
except User.DoesNotExist:
|
||||
raise Http404
|
||||
|
||||
return PasswordResetConfirmView(request=request, uidb64=uidb64, token=token
|
||||
)
|
||||
Dict = {
|
||||
'user': user
|
||||
}
|
||||
|
||||
t = loader.get_template('pages/profile/p_password_recovery.html')
|
||||
response = get_inter_http_response(t, Dict, request)
|
||||
return response
|
||||
|
||||
|
||||
|
||||
@@ -120,7 +303,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''
|
||||
|
||||
292
BaseModels/OpenStreetMap/osm_api.py
Normal file
@@ -0,0 +1,292 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
import time
|
||||
|
||||
import overpass
|
||||
import copy
|
||||
from geopy.geocoders import Nominatim
|
||||
|
||||
Nominatim_last_request = None
|
||||
|
||||
def osm_api_request(data, res_type):
|
||||
|
||||
time.sleep(1)
|
||||
|
||||
api = overpass.API(timeout=100)
|
||||
|
||||
res = api.get(data, responseformat=res_type)
|
||||
|
||||
pres_names = []
|
||||
res_list = []
|
||||
if res_type[:3].lower() == 'csv':
|
||||
data_type_list = copy.deepcopy(res[0])
|
||||
for line in res[1:]:
|
||||
if line[0] in pres_names:
|
||||
continue
|
||||
|
||||
i = 0
|
||||
line_Dict = {}
|
||||
while i < len(data_type_list):
|
||||
line_Dict.update({data_type_list[i]: line[i]})
|
||||
i += 1
|
||||
if 'name:en' in line_Dict and 'name' in line_Dict:
|
||||
if not line_Dict["name:en"]:
|
||||
if line_Dict["name"]:
|
||||
line_Dict["name:en"] = line_Dict["name"]
|
||||
|
||||
if not line_Dict["name:en"]:
|
||||
continue
|
||||
|
||||
res_list.append(line_Dict)
|
||||
pres_names.append(line[0])
|
||||
else:
|
||||
res_list = copy.deepcopy(res)
|
||||
|
||||
return res_list
|
||||
|
||||
|
||||
def osm_get_area_id_by_params_dict(paramsDict):
|
||||
|
||||
area_id = None
|
||||
|
||||
try:
|
||||
|
||||
# Geocoding request via Nominatim
|
||||
time.sleep(1)
|
||||
geolocator = Nominatim(user_agent='TWB')
|
||||
geo_results = geolocator.geocode(paramsDict, exactly_one=False, limit=3)
|
||||
|
||||
if not geo_results:
|
||||
return area_id
|
||||
|
||||
# Searching for relation in result set
|
||||
country_obj = None
|
||||
for r in geo_results:
|
||||
print(r.address, r.raw.get("osm_type"))
|
||||
if r.raw.get("osm_type") == "relation":
|
||||
country_obj = r
|
||||
break
|
||||
|
||||
if not country_obj:
|
||||
return area_id
|
||||
|
||||
# Calculating area id
|
||||
area_id = int(country_obj.raw.get("osm_id")) + 3600000000
|
||||
|
||||
except Exception as e:
|
||||
print(f'osm_get_area_id_by_params_dict Error = {e}')
|
||||
|
||||
return area_id
|
||||
|
||||
|
||||
def osm_get_countries():
|
||||
res_type = 'csv("name", "name:en", "name:ru", "ISO3166-1", "flag", "int_name", "official_name", "ISO3166-1:alpha3", ' \
|
||||
'"ISO3166-1:numeric", ::lon, ::lat)'
|
||||
data = 'relation["admin_level"="2"]["ISO3166-1:alpha3"~"^...$"]; ' \
|
||||
'out center;'
|
||||
|
||||
res = osm_api_request(data, res_type)
|
||||
from operator import itemgetter
|
||||
res = sorted(res, key=itemgetter('name'))
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def osm_get_cities_by_country(country_Dict):
|
||||
|
||||
res = []
|
||||
|
||||
try:
|
||||
|
||||
if 'area_id' in country_Dict and country_Dict['area_id']:
|
||||
area_id = country_Dict['area_id']
|
||||
else:
|
||||
return []
|
||||
|
||||
res_type = 'csv("name", "name:ru", "name:en", ::lon, ::lat)'
|
||||
data = f'area({area_id})->.searchArea;' \
|
||||
'(node[place~"city$|town$"](area.searchArea););' \
|
||||
'out center;'
|
||||
|
||||
res = osm_api_request(data, res_type)
|
||||
|
||||
except Exception as e:
|
||||
print(f'osm_get_cities_by_country Error = {e}')
|
||||
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def osm_get_airports(area_id, city_find_str=None):
|
||||
|
||||
airports_cities_Dict = {}
|
||||
|
||||
try:
|
||||
|
||||
if not area_id:
|
||||
return airports_cities_Dict
|
||||
|
||||
res_type = 'csv("name", "name:ru", "name:en", "iata", "icao", "place", "int_name", "addr:country", "city_served", ::lon, ::lat)'
|
||||
data = f'area({area_id})->.searchArea;' \
|
||||
f'nwr["aeroway"="aerodrome"]["iata"~"^...$"]["icao"~"^....$"]["abandoned"!~".*"]["landuse"!="military"]["was:landuse"!="military"]["aerodrome:type"!~"airfield|military|private"]["amenity"!="flight_school"]["closed"!="yes"](area.searchArea)->.airports;' \
|
||||
|
||||
if city_find_str:
|
||||
data = f'{data}' \
|
||||
f'foreach.airports->.elem(nwr(around.elem:20000)[{city_find_str}]->.city; .elem out center; .city out center;);'
|
||||
else:
|
||||
data = f'{data} .airports out center;'
|
||||
|
||||
res = osm_api_request(data, res_type)
|
||||
|
||||
if not city_find_str:
|
||||
return res
|
||||
|
||||
present_IATA_list = []
|
||||
|
||||
i = 0
|
||||
while i < len(res):
|
||||
|
||||
if not res[i]['iata'] or not res[i]['icao']:
|
||||
del res[i]
|
||||
continue
|
||||
|
||||
if res[i]['iata'] and res[i]['iata'] in present_IATA_list:
|
||||
del res[i]
|
||||
continue
|
||||
present_IATA_list.append(res[i]['iata'])
|
||||
|
||||
# если аэропорт
|
||||
if res[i]['iata']:
|
||||
if i + 1 < len(res) and res[i+1]['place']:
|
||||
|
||||
# ищем город на следующих строках
|
||||
i2 = i + 1
|
||||
linked = None
|
||||
while i2 < len(res) and res[i2]['place']:
|
||||
if not linked:
|
||||
if (res[i2]['name'] and res[i]['name'] and res[i2]['name'] in res[i]['name']) or \
|
||||
(res[i2]['name:en'] and res[i]['name:en'] and res[i2]['name:en'] in res[i]['name:en']) or \
|
||||
(res[i2]['name:ru'] and res[i]['name:ru'] and res[i2]['name:ru'] in res[i]['name:ru']):
|
||||
linked = i2
|
||||
|
||||
i2 += 1
|
||||
|
||||
if not linked:
|
||||
linked = i + 1
|
||||
|
||||
res[i]['city'] = copy.deepcopy(res[linked])
|
||||
if not res[i]['city']['name'] in airports_cities_Dict:
|
||||
airports_cities_Dict.update({res[i]['city']['name:en']: [res[i]]})
|
||||
else:
|
||||
airports_cities_Dict[res[i]['city']['name:en']].append(res[i])
|
||||
|
||||
while i < i2:
|
||||
del res[i]
|
||||
i2 -= 1
|
||||
|
||||
continue
|
||||
|
||||
# если не найдена связка с городом
|
||||
elif res[i]['city_served']:
|
||||
|
||||
res[i]['city'] = {'name:en': res[i]['city_served']}
|
||||
if not res[i]['city']['name:en'] in airports_cities_Dict:
|
||||
airports_cities_Dict.update({res[i]['city']['name:en']: [res[i]]})
|
||||
else:
|
||||
airports_cities_Dict[res[i]['city']['name:en']].append(res[i])
|
||||
del res[i]
|
||||
continue
|
||||
|
||||
# текущий элемент - не аэропорт - удаляем
|
||||
else:
|
||||
del res[i]
|
||||
continue
|
||||
|
||||
i += 1
|
||||
|
||||
|
||||
if res:
|
||||
airports_cities_Dict[None] = res
|
||||
|
||||
except Exception as e:
|
||||
print(f'osm_get_airports Error = {e}')
|
||||
if e.args[0] == 25:
|
||||
return {'error': 'timeout'}
|
||||
|
||||
|
||||
return airports_cities_Dict
|
||||
|
||||
|
||||
|
||||
def osm_get_country_w_cities_n_airports(country_Dict, area_id):
|
||||
print(f'{country_Dict["name:en"]}')
|
||||
|
||||
if area_id:
|
||||
country_Dict['area_id'] = area_id
|
||||
else:
|
||||
country_Dict['area_id'] = osm_get_area_id_by_params_dict({'country': country_Dict['name']})
|
||||
|
||||
airports_Dict = osm_get_airports(country_Dict['area_id'], 'place~"city$|town$"')
|
||||
if airports_Dict and 'error' in airports_Dict and airports_Dict['error'] == 'timeout':
|
||||
airports_Dict = osm_get_airports(country_Dict['area_id'], 'place="city"')
|
||||
|
||||
from ReferenceDataApp.funcs import get_countries_key_data, get_cities_by_country_name_en
|
||||
db_cities = get_cities_by_country_name_en(country_Dict["name:en"])
|
||||
|
||||
cities = osm_get_cities_by_country(country_Dict)
|
||||
for city in cities:
|
||||
try:
|
||||
print(f' > {city["name:en"]}')
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
|
||||
if airports_Dict and 'error' in airports_Dict and airports_Dict['error'] == 'timeout':
|
||||
|
||||
if city['name:en'] in db_cities.keys():
|
||||
city['area_id'] = db_cities[city['name:en']]
|
||||
else:
|
||||
city['area_id'] = osm_get_area_id_by_params_dict(
|
||||
{'country': country_Dict['name:en'], 'city': city['name:en']})
|
||||
|
||||
airports_list = osm_get_airports(city['area_id'])
|
||||
if airports_list:
|
||||
city['airports'] = copy.deepcopy(airports_list)
|
||||
else:
|
||||
if city['name:en'] in airports_Dict.keys():
|
||||
city['airports'] = copy.deepcopy(airports_Dict[city['name:en']])
|
||||
del airports_Dict[city['name:en']]
|
||||
|
||||
if not 'airports' in city:
|
||||
city['airports'] = []
|
||||
|
||||
print(f' > > airports count={str(len(city["airports"]))}')
|
||||
|
||||
city['parsing_status'] = 'finished'
|
||||
|
||||
country_Dict['cities'] = cities
|
||||
country_Dict['parsing_status'] = 'finished'
|
||||
|
||||
airports_wo_city = []
|
||||
# if airports_Dict and None in airports_Dict:
|
||||
# airports_wo_city = airports_Dict[None]
|
||||
|
||||
return country_Dict, airports_wo_city
|
||||
|
||||
|
||||
def osm_get_countries_n_cities_n_airports():
|
||||
|
||||
airports_wo_city = []
|
||||
|
||||
from ReferenceDataApp.funcs import get_countries_key_data, get_cities_by_country_name_en
|
||||
db_countries = get_countries_key_data()
|
||||
|
||||
countries = osm_get_countries()
|
||||
i = 0
|
||||
while i < len(countries):
|
||||
area_id = None
|
||||
if countries[i]['name:en'] in db_countries.keys():
|
||||
area_id = db_countries[countries[i]['name:en']]
|
||||
countries[i], airports_wo_city_for_country = osm_get_country_w_cities_n_airports(countries[i], area_id)
|
||||
if airports_wo_city_for_country:
|
||||
airports_wo_city.extend(airports_wo_city_for_country)
|
||||
|
||||
return countries
|
||||
70
BaseModels/PIL/pillow_backend.py
Normal 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)
|
||||
@@ -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:
|
||||
|
||||
@@ -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'})
|
||||
@@ -142,6 +146,13 @@ def get_image_thumb(self, obj):
|
||||
except:
|
||||
image_url = None
|
||||
|
||||
if not image_url:
|
||||
try:
|
||||
if obj.flag:
|
||||
image_url = obj.flag.url
|
||||
except:
|
||||
image_url = None
|
||||
|
||||
if not image_url:
|
||||
try:
|
||||
if obj.images_gallery:
|
||||
@@ -215,7 +226,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 +239,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 +251,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 +266,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 +279,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
|
||||
|
||||
|
||||
|
||||
@@ -10,14 +10,15 @@ 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
|
||||
|
||||
|
||||
# add_introspection_rules([], ["^tinymce\.models\.HTMLField"])
|
||||
|
||||
class BaseModel(models.Model):
|
||||
name = models.TextField(verbose_name=_('Название'),
|
||||
name = models.TextField(verbose_name=_("Название"),
|
||||
help_text=_('Название'), null=True, blank=True)
|
||||
name_plural = models.TextField(verbose_name=_('Название (множественное число)'),
|
||||
null=True, blank=True)
|
||||
@@ -28,12 +29,42 @@ class BaseModel(models.Model):
|
||||
|
||||
json_data = models.JSONField(verbose_name=_('Дополнительные данные'), default=dict, blank=True)
|
||||
|
||||
media_items = GenericRelation('GeneralApp.MediaItem', related_query_name='grel_%(class)s_for_media_item')
|
||||
|
||||
def __str__(self):
|
||||
if self.name:
|
||||
return self.name
|
||||
else:
|
||||
return str(self.id)
|
||||
|
||||
def get_media_items(self, exclude_kwargs=None):
|
||||
if not exclude_kwargs:
|
||||
exclude_kwargs = {}
|
||||
return self.media_items.exclude(
|
||||
**exclude_kwargs
|
||||
).filter(
|
||||
enable=True
|
||||
).order_by('order')
|
||||
|
||||
def get_video_items(self):
|
||||
exclude_kwargs = {'video': None}
|
||||
return self.get_media_items(exclude_kwargs=exclude_kwargs)
|
||||
|
||||
def get_picture_items(self):
|
||||
exclude_kwargs = {'picture': None}
|
||||
return self.get_media_items(exclude_kwargs=exclude_kwargs)
|
||||
|
||||
def pop_node_by_name(self, node_name):
|
||||
if not self.json_data or not node_name in self.json_data:
|
||||
return None
|
||||
|
||||
res = self.json_data[node_name]
|
||||
del self.json_data[node_name]
|
||||
self.save(update_fields=['json_data'])
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def get_node_by_name(self, node_name):
|
||||
if not self.json_data or not node_name in self.json_data:
|
||||
return None
|
||||
@@ -53,8 +84,9 @@ class BaseModel(models.Model):
|
||||
|
||||
return self.json_data
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
self.modifiedDT = datetime.now()
|
||||
def save(self, not_change_modifiedDT=False, *args, **kwargs):
|
||||
if not not_change_modifiedDT:
|
||||
self.modifiedDT = datetime.now()
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
class Meta:
|
||||
@@ -74,9 +106,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'')
|
||||
@@ -90,15 +122,31 @@ 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')
|
||||
|
||||
|
||||
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
|
||||
|
||||
11
BaseModels/datetime_funcs.py
Normal file
@@ -0,0 +1,11 @@
|
||||
from datetime import datetime
|
||||
import pytz
|
||||
|
||||
def get_cur_DT_by_timezone(timezone, DT=None):
|
||||
if not DT:
|
||||
DT = datetime.now()
|
||||
|
||||
if not timezone:
|
||||
return DT
|
||||
|
||||
return DT.astimezone(tz=pytz.timezone(timezone))
|
||||
49
BaseModels/exceptions.py
Normal file
@@ -0,0 +1,49 @@
|
||||
import asyncio
|
||||
import traceback
|
||||
|
||||
from asgiref.sync import sync_to_async
|
||||
|
||||
from BaseModels.mailSender import techSendMail
|
||||
from BaseModels.print_funcs import print_ext
|
||||
from GeneralApp.funcs_options import get_mail_send_options
|
||||
|
||||
# from MessageBotsApp.telegram.tg_bot import send_message
|
||||
|
||||
|
||||
async def send_exception_msg(msgr_msg, mail_msg):
|
||||
# from MessageBotsApp.funcs import send_msg_to_staff
|
||||
|
||||
|
||||
mail_sets = await sync_to_async(get_mail_send_options)()
|
||||
# await send_msg_to_staff('telegram', msgr_msg)
|
||||
|
||||
await sync_to_async(techSendMail)(sets=mail_sets, html_content=mail_msg, title='iBaked Exception')
|
||||
|
||||
return True
|
||||
|
||||
async def exception_processing(exc, user=None):
|
||||
tb = traceback.format_exc()
|
||||
cutted_tb = tb[:500]
|
||||
|
||||
msgr_msg = f'user {str(user)} Exception = {str(exc)}\n{str(cutted_tb)}'
|
||||
mail_msg = f'user {str(user)} Exception = {str(exc)}<br>\n{str(tb)}'
|
||||
print_ext(msgr_msg)
|
||||
|
||||
# try:
|
||||
# loop = asyncio.get_event_loop()
|
||||
# except RuntimeError:
|
||||
# loop = asyncio.new_event_loop()
|
||||
# asyncio.set_event_loop(loop)
|
||||
#
|
||||
|
||||
# loop = asyncio.new_event_loop()
|
||||
# asyncio.set_event_loop(loop)
|
||||
# async_result = loop.run_until_complete(send_exception_msg(msgr_msg, mail_msg))
|
||||
# loop.close()
|
||||
|
||||
loop = asyncio.get_event_loop()
|
||||
task = loop.create_task(send_exception_msg(msgr_msg, mail_msg))
|
||||
|
||||
status_code = 400
|
||||
|
||||
return msgr_msg, status_code
|
||||
154
BaseModels/files_funcs.py
Normal file
@@ -0,0 +1,154 @@
|
||||
import os
|
||||
import copy
|
||||
import shutil
|
||||
|
||||
from BaseModels.print_funcs import print_ext
|
||||
|
||||
def del_file(filepath, del_other_ext=None):
|
||||
|
||||
if os.path.exists(filepath):
|
||||
os.remove(filepath)
|
||||
print_ext(f'- del file {filepath} is done')
|
||||
|
||||
if del_other_ext:
|
||||
filepath2 = change_file_extension(filepath, del_other_ext)
|
||||
if os.path.exists(filepath2):
|
||||
os.remove(filepath2)
|
||||
print_ext(f'- del file {filepath2} is done')
|
||||
return True
|
||||
|
||||
print_ext(f'!- file {filepath} not found')
|
||||
return False
|
||||
|
||||
def del_folder(path):
|
||||
shutil.rmtree(path)
|
||||
return True
|
||||
|
||||
def check_filepath_exists(path_for_check):
|
||||
if not os.path.exists(path_for_check):
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def get_file_name_from_filepath(filepath):
|
||||
filepath_list = filepath.split('/')
|
||||
if not filepath_list:
|
||||
filepath_list = filepath.split('\\')
|
||||
if len(filepath_list) > 1:
|
||||
return filepath_list[-1]
|
||||
else:
|
||||
return filepath
|
||||
|
||||
def get_file_extension(filename, w_point=True):
|
||||
filename_list = filename.split('.')
|
||||
if len(filename_list) > 1:
|
||||
res = filename_list[-1]
|
||||
if w_point:
|
||||
res = '.' + res
|
||||
return res
|
||||
else:
|
||||
return ''
|
||||
|
||||
def get_filename_wo_ext(filename):
|
||||
filename_list = filename.split('.')
|
||||
if len(filename_list) > 1:
|
||||
return '.'.join(filename_list[:-1])
|
||||
else:
|
||||
return filename
|
||||
|
||||
def change_file_extension(filename, new_extension):
|
||||
filename_list = filename.split('.')
|
||||
if len(filename_list) > 1:
|
||||
return '.'.join(filename_list[:-1]) + '.' + new_extension
|
||||
else:
|
||||
return filename + '.' + new_extension
|
||||
|
||||
def folder_check_and_create(path):
|
||||
path_for_check = copy.copy(path)
|
||||
# if path_for_check[0] != '/':
|
||||
# path_for_check = '/' + path_for_check
|
||||
if not os.path.exists(path_for_check):
|
||||
os.makedirs(path_for_check)
|
||||
# if path[0] != '/':
|
||||
# path_for_check = path_for_check[1:]
|
||||
return path_for_check
|
||||
|
||||
def get_filename_from_path(filepath, wo_ext=False):
|
||||
|
||||
f_list = filepath.split('/')
|
||||
if len(f_list) > 1:
|
||||
filename = f_list[-1]
|
||||
else:
|
||||
filename = f_list[0]
|
||||
|
||||
f_list = filename.split('\\')
|
||||
if len(f_list) > 1:
|
||||
filename = f_list[-1]
|
||||
else:
|
||||
filename = f_list[0]
|
||||
|
||||
if filename and wo_ext:
|
||||
f_list = filename.split('.')
|
||||
filename = f_list[0]
|
||||
|
||||
return filename
|
||||
|
||||
|
||||
def get_path_wo_filename(filepath):
|
||||
|
||||
try:
|
||||
f_list = filepath.split('/')
|
||||
if len(f_list) < 2:
|
||||
if filepath[-1] != '/':
|
||||
filepath += '/'
|
||||
return filepath
|
||||
|
||||
if f_list[-1] == '':
|
||||
if filepath[-1] != '/':
|
||||
filepath += '/'
|
||||
return filepath
|
||||
|
||||
return '/'.join(f_list[:-1]) + '/'
|
||||
|
||||
except Exception as e:
|
||||
msg = f'get_path_wo_filename Exception: {e}'
|
||||
print_ext(msg)
|
||||
|
||||
if filepath[-1] != '/':
|
||||
filepath += '/'
|
||||
return filepath
|
||||
|
||||
|
||||
def get_allow_filename(filename, filepath):
|
||||
from os import path, access, R_OK # W_OK for write permission.
|
||||
|
||||
|
||||
if '.' in filename:
|
||||
fn_list = filename.split('.')
|
||||
fn_ext = fn_list[-1]
|
||||
fn = '.'.join(fn_list[:-1])
|
||||
else:
|
||||
fn = filename
|
||||
fn_ext = ''
|
||||
|
||||
i = 0
|
||||
full_path = filepath + filename
|
||||
while path.exists(full_path):# and path.isfile(full_path) and access(full_path, R_OK):
|
||||
i += 1
|
||||
|
||||
full_path = f'{filepath}{fn}-{str(i)}'
|
||||
if fn_ext:
|
||||
full_path = f'{full_path}.{fn_ext}'
|
||||
if i == 0:
|
||||
filename = f'{fn}'
|
||||
else:
|
||||
filename = f'{fn}-{str(i)}'
|
||||
if fn_ext:
|
||||
filename = f'{filename}.{fn_ext}'
|
||||
|
||||
return filename
|
||||
|
||||
|
||||
def get_filenames_in_path(path):
|
||||
files_list = os.listdir(path)
|
||||
return files_list
|
||||
@@ -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'
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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')
|
||||
|
||||
@@ -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 = []
|
||||
|
||||
113
BaseModels/pay_systems/DVL_Group_kaz/api/funcs.py
Normal file
@@ -0,0 +1,113 @@
|
||||
import json
|
||||
from django.conf import settings
|
||||
|
||||
import requests
|
||||
from requests_pkcs12 import get,post
|
||||
|
||||
# для песочницы
|
||||
# pkcs12_filename = 'dvldigitalprojects.p12'
|
||||
# pkcs12_password = 'QNlhRStcY7mB'
|
||||
# api_pass = 'aPqSRVZhxFjjSqbB'
|
||||
|
||||
# для прода
|
||||
# pkcs12_filename = 'dvldigitalprojects.p12'
|
||||
# pkcs12_password = 'fzSBm6WISje7'
|
||||
# api_pass = 't9g2+bZSvxNxCu+t'
|
||||
|
||||
|
||||
def get_domain_url():
|
||||
return settings.PAY_SYSTEM_URL #'https://sandboxapi.paymtech.kz/'
|
||||
|
||||
def get_kwargs_for_request():
|
||||
return {
|
||||
'headers': {
|
||||
'content-type': 'application/json',
|
||||
},
|
||||
'auth': ('dvldigitalprojects', settings.API_PASS),
|
||||
'pkcs12_filename': settings.PKCS12_FILENAME,
|
||||
'pkcs12_password': settings.PKCS12_PASS
|
||||
}
|
||||
|
||||
def ping():
|
||||
|
||||
url = f'{get_domain_url()}ping'
|
||||
data = {}
|
||||
|
||||
try:
|
||||
msg = f'GET {url}'
|
||||
print(msg)
|
||||
res = get(
|
||||
url,
|
||||
**get_kwargs_for_request()
|
||||
)
|
||||
|
||||
msg = f'answer received = {str(res)}'
|
||||
print(msg)
|
||||
except Exception as e:
|
||||
msg = f'Exception GET {url} = {str(e)} ({str(res)})'
|
||||
print(msg)
|
||||
res = None
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def get_order_status(bank_order_id):
|
||||
|
||||
url = f'{get_domain_url()}orders/{str(bank_order_id)}'
|
||||
|
||||
res = None
|
||||
|
||||
data = {
|
||||
'expand': [
|
||||
'card', 'client', 'location', 'custom_fields',
|
||||
'issuer', 'secure3d', 'operations', 'cashflow'
|
||||
]
|
||||
}
|
||||
|
||||
try:
|
||||
msg = f'GET {url}'
|
||||
print(msg)
|
||||
res = get(
|
||||
url,
|
||||
data=json.dumps(data),
|
||||
**get_kwargs_for_request()
|
||||
)
|
||||
|
||||
msg = f'get_order_status answer received = {str(res)}'
|
||||
print(msg)
|
||||
except Exception as e:
|
||||
msg = f'Exception get_order_status GET {url} = {str(e)} ({str(res)})'
|
||||
print(msg)
|
||||
res = None
|
||||
|
||||
return res
|
||||
|
||||
|
||||
def create_order(data):
|
||||
|
||||
url = f'{get_domain_url()}orders/create'
|
||||
|
||||
res = None
|
||||
|
||||
try:
|
||||
msg = f'POST {url}'
|
||||
print(msg)
|
||||
res = post(
|
||||
url,
|
||||
data=json.dumps(data),
|
||||
**get_kwargs_for_request()
|
||||
)
|
||||
|
||||
msg = f'create_order answer received = {str(res.text)}'
|
||||
# if res:# and res.status_code > 300:
|
||||
# msg += f' > ({str(res.text)})'
|
||||
print(msg)
|
||||
except Exception as e:
|
||||
msg = f'Exception create_order POST {url} = {str(e)} ({str(res)})'
|
||||
if res:
|
||||
msg += f' > ({str(res.text)})'
|
||||
print(msg)
|
||||
res = None
|
||||
|
||||
return res
|
||||
6
BaseModels/print_funcs.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from datetime import datetime
|
||||
|
||||
def print_ext(msg, *args, **kwargs):
|
||||
msg = f'{msg} - {str(datetime.now())}'
|
||||
print(msg)
|
||||
return msg
|
||||
@@ -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": {
|
||||
|
||||
@@ -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
|
||||
24
BaseModels/validators/form_field_validators.py
Normal 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
|
||||
0
BillingApp/__init__.py
Normal file
39
BillingApp/admin.py
Normal file
@@ -0,0 +1,39 @@
|
||||
from sets.admin import *
|
||||
from .models import *
|
||||
from django.contrib import admin
|
||||
|
||||
class Admin_SubscribeOrder(Admin_BaseModel):
|
||||
|
||||
fieldsets = (
|
||||
(None, {
|
||||
'classes': ['wide'],
|
||||
'fields': (
|
||||
('user', 'subscribe', 'subscribe_for_user'),
|
||||
('enable', 'order'),
|
||||
('sum', 'currency'),
|
||||
('status', 'last_operation_status'),
|
||||
('bank_order_id', 'pay_page'),
|
||||
'json_data',
|
||||
)
|
||||
}),
|
||||
)
|
||||
|
||||
list_display = [
|
||||
'id', 'enable',
|
||||
'user', 'subscribe', 'subscribe_for_user',
|
||||
'bank_order_id',
|
||||
'sum', 'currency',
|
||||
'status', 'last_operation_status',
|
||||
'order', 'modifiedDT', 'createDT'
|
||||
]
|
||||
|
||||
list_display_links = ['id', 'user', 'subscribe']
|
||||
list_editable = ['enable']
|
||||
|
||||
readonly_fields = ['subscribe_for_user', 'sum', 'currency', 'modifiedDT', 'createDT']
|
||||
|
||||
list_filter = ['enable', 'status', 'modifiedDT', 'createDT']
|
||||
search_fields = ['id', 'last_operation_status', 'status']
|
||||
# filter_horizontal = ['options']
|
||||
|
||||
admin.site.register(SubscribeOrder, Admin_SubscribeOrder)
|
||||
6
BillingApp/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BillingappConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'BillingApp'
|
||||
101
BillingApp/funcs.py
Normal file
@@ -0,0 +1,101 @@
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from .models import *
|
||||
import json
|
||||
|
||||
|
||||
def get_order_status(order):
|
||||
from BaseModels.pay_systems.DVL_Group_kaz.api.funcs import get_order_status
|
||||
res_status = None
|
||||
|
||||
if not order or not order.bank_order_id:
|
||||
return order
|
||||
|
||||
try:
|
||||
res_data = get_order_status(order.bank_order_id)
|
||||
|
||||
res = json.loads(res_data.text)
|
||||
res = res['orders'][0]
|
||||
order.json_data['status'] = res
|
||||
|
||||
order.status = res['status']
|
||||
|
||||
|
||||
# if res['amount'] == res['amount_charged'] and res['status'] == 'charged':
|
||||
order.save()
|
||||
# return order.status
|
||||
|
||||
|
||||
except Exception as e:
|
||||
msg = f'Exception get_order_status = {str(e)}'
|
||||
if order:
|
||||
msg = f'Exception get_order_status (data = {str(order.id)}) = {str(e)}'
|
||||
print(msg)
|
||||
|
||||
return order
|
||||
|
||||
def get_orders_for_user(user):
|
||||
|
||||
orders = SubscribeOrder.objects.filter(
|
||||
enable=True,
|
||||
user=user,
|
||||
subscribe_for_user=None,
|
||||
createDT__gt=datetime.now() - timedelta(hours=1)
|
||||
).order_by('subscribe', '-createDT').distinct('subscribe')
|
||||
|
||||
SubscribeOrder.objects.filter(
|
||||
user=user
|
||||
).exclude(
|
||||
id__in=orders.values_list('id', flat=True)
|
||||
).update(enable=False)
|
||||
|
||||
return orders
|
||||
|
||||
|
||||
def create_subscribe_order(data):
|
||||
order = None
|
||||
|
||||
try:
|
||||
|
||||
order = SubscribeOrder.objects.create(**data)
|
||||
|
||||
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)
|
||||
|
||||
from BaseModels.pay_systems.DVL_Group_kaz.api.funcs import create_order
|
||||
data = {
|
||||
'currency': data['currency'],
|
||||
'amount': data['sum'],
|
||||
'description': f'Заказ {order.id} на подписку '
|
||||
f'{data["subscribe"].name} '
|
||||
f'для пользователя {data["user"].username}',
|
||||
'options': {
|
||||
'force3d': 1,
|
||||
'auto_charge': 1,
|
||||
'return_url': f'{sets["domain"]}/profile/page/my_subscribe/'
|
||||
}
|
||||
}
|
||||
|
||||
res_data = create_order(data)
|
||||
|
||||
order.pay_page = res_data.headers.get('location')
|
||||
|
||||
res = json.loads(res_data.text)
|
||||
res = res['orders'][0]
|
||||
order.json_data['create_order'] = res
|
||||
|
||||
order.modifiedDT = datetime.strptime(res['updated'], '%Y-%m-%d %H:%M:%S')
|
||||
order.status = res['status']
|
||||
order.bank_order_id = res['id']
|
||||
if 'segment' in res:
|
||||
order.segment = res['segment']
|
||||
if 'merchant_order_id' in res:
|
||||
order.merchant_order_id = res['merchant_order_id']
|
||||
order.save()
|
||||
|
||||
|
||||
except Exception as e:
|
||||
msg = f'Exception create_subscribe_order (data = {str(data)}) = {str(e)}'
|
||||
print(msg)
|
||||
|
||||
return order
|
||||
42
BillingApp/migrations/0001_initial.py
Normal file
@@ -0,0 +1,42 @@
|
||||
# Generated by Django 4.2.2 on 2024-04-19 16:24
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('SubscribesApp', '0003_alter_subscribe_bg_color_alter_subscribe_text_color'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='SubscribeOrder',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
|
||||
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
|
||||
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
|
||||
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
|
||||
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
|
||||
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
|
||||
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
|
||||
('sum', models.PositiveSmallIntegerField(verbose_name='Сумма')),
|
||||
('currency', models.CharField(max_length=3, verbose_name='Валюта')),
|
||||
('segment', models.CharField(verbose_name='ID Сегмента')),
|
||||
('merchant_order_id', models.CharField(verbose_name='merchant_order_id')),
|
||||
('bank_order_id', models.CharField(verbose_name='ID заказа в банке')),
|
||||
('status', models.CharField(verbose_name='Статус заказа в банке')),
|
||||
('subscribe', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subscribe_orders_for_subscribe', to='SubscribesApp.subscribe', verbose_name='Подписка')),
|
||||
('subscribe_for_user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subscribe_orders_for_user_subscribe', to='SubscribesApp.subscribeforuser', verbose_name='Подписка пользователя')),
|
||||
('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subscribe_orders_for_user', to='SubscribesApp.subscribe', verbose_name='Пользователь')),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'Заказ на подписку',
|
||||
'verbose_name_plural': 'Заказы на подписки',
|
||||
},
|
||||
),
|
||||
]
|
||||
21
BillingApp/migrations/0002_alter_subscribeorder_user.py
Normal file
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 4.2.2 on 2024-04-19 16:29
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('BillingApp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='user',
|
||||
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='subscribe_orders_for_user', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,43 @@
|
||||
# Generated by Django 4.2.2 on 2024-04-19 16:36
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('BillingApp', '0002_alter_subscribeorder_user'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subscribeorder',
|
||||
name='pay_page',
|
||||
field=models.URLField(blank=True, default=None, null=True, verbose_name='Ссылка на страницу оплаты'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='bank_order_id',
|
||||
field=models.CharField(default='', verbose_name='ID заказа в банке'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='merchant_order_id',
|
||||
field=models.CharField(default='', verbose_name='merchant_order_id'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='segment',
|
||||
field=models.CharField(default='', verbose_name='ID Сегмента'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='status',
|
||||
field=models.CharField(default='', verbose_name='Статус заказа в банке'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='sum',
|
||||
field=models.PositiveSmallIntegerField(default=0, verbose_name='Сумма'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,38 @@
|
||||
# Generated by Django 4.2.2 on 2024-04-19 16:47
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('BillingApp', '0003_subscribeorder_pay_page_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='bank_order_id',
|
||||
field=models.CharField(default=None, null=True, verbose_name='ID заказа в банке'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='currency',
|
||||
field=models.CharField(default='USD', max_length=3, verbose_name='Валюта'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='merchant_order_id',
|
||||
field=models.CharField(default=None, null=True, verbose_name='merchant_order_id'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='segment',
|
||||
field=models.CharField(default=None, null=True, verbose_name='ID Сегмента'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='status',
|
||||
field=models.CharField(default=None, null=True, verbose_name='Статус заказа в банке'),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.2 on 2024-04-19 17:57
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('BillingApp', '0004_alter_subscribeorder_bank_order_id_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='subscribeorder',
|
||||
name='last_operation_status',
|
||||
field=models.CharField(default=None, null=True, verbose_name='Статус последней операции'),
|
||||
),
|
||||
]
|
||||
18
BillingApp/migrations/0006_alter_subscribeorder_currency.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# Generated by Django 4.2.2 on 2024-07-12 17:23
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('BillingApp', '0005_subscribeorder_last_operation_status'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='subscribeorder',
|
||||
name='currency',
|
||||
field=models.CharField(default='KZT', max_length=3, verbose_name='Валюта'),
|
||||
),
|
||||
]
|
||||
0
BillingApp/migrations/__init__.py
Normal file
75
BillingApp/models.py
Normal file
@@ -0,0 +1,75 @@
|
||||
from django.db import models
|
||||
from BaseModels.base_models import BaseModel
|
||||
from SubscribesApp.models import Subscribe, SubscribeForUser
|
||||
from AuthApp.models import User
|
||||
from django.utils.translation import gettext as _
|
||||
|
||||
class SubscribeOrder(BaseModel):
|
||||
|
||||
subscribe = models.ForeignKey(
|
||||
Subscribe, verbose_name=_('Подписка'),
|
||||
on_delete=models.SET_NULL, blank=True, null=True,
|
||||
related_name='subscribe_orders_for_subscribe'
|
||||
)
|
||||
|
||||
user = models.ForeignKey(
|
||||
User, verbose_name=_('Пользователь'),
|
||||
on_delete=models.SET_NULL, blank=True, null=True,
|
||||
related_name='subscribe_orders_for_user'
|
||||
)
|
||||
|
||||
subscribe_for_user = models.OneToOneField(
|
||||
SubscribeForUser, verbose_name=_('Подписка пользователя'),
|
||||
on_delete=models.SET_NULL, blank=True, null=True,
|
||||
related_name='subscribe_orders_for_user_subscribe'
|
||||
)
|
||||
|
||||
sum = models.PositiveSmallIntegerField(verbose_name=_('Сумма'), default=0)
|
||||
currency = models.CharField(verbose_name=_('Валюта'), max_length=3, default='KZT')
|
||||
segment = models.CharField(verbose_name=_('ID Сегмента'), null=True, default=None)
|
||||
merchant_order_id = models.CharField(verbose_name=_('merchant_order_id'), null=True, default=None)
|
||||
bank_order_id = models.CharField(verbose_name=_('ID заказа в банке'), null=True, default=None)
|
||||
|
||||
status = models.CharField(verbose_name=_('Статус заказа в банке'), null=True, default=None)
|
||||
last_operation_status = models.CharField(verbose_name=_('Статус последней операции'), null=True, default=None)
|
||||
|
||||
pay_page = models.URLField(verbose_name=_('Ссылка на страницу оплаты'), null=True, blank=True, default=None)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Заказ на подписку')
|
||||
verbose_name_plural = _('Заказы на подписки')
|
||||
|
||||
def activate_subscribe_for_user(self):
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
kwargs = {
|
||||
'user': self.user,
|
||||
'subscribe': self.subscribe,
|
||||
'last_paid_DT': datetime.now(),
|
||||
'receive_finish_subscribe_msg': True,
|
||||
# 'enable': True,
|
||||
}
|
||||
|
||||
from SubscribesApp.funcs import create_subscribe_by_data
|
||||
subscribe_for_user = create_subscribe_by_data(kwargs)
|
||||
self.subscribe_for_user = subscribe_for_user
|
||||
self.enable = False
|
||||
self.save()
|
||||
|
||||
subscribe_for_user.activate(
|
||||
paid_period_from_DT=datetime.now(),
|
||||
paid_period_to_DT=datetime.now() + timedelta(hours=self.subscribe.period)
|
||||
)
|
||||
|
||||
return self
|
||||
|
||||
def __str__(self):
|
||||
res = 'Заказ'
|
||||
if self.subscribe:
|
||||
res += f' на подписку {self.subscribe.name}'
|
||||
if self.user:
|
||||
res += f' для {self.user.username}'
|
||||
|
||||
if not res:
|
||||
res += f' {str(self.id)}'
|
||||
return res
|
||||
3
BillingApp/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
3
BillingApp/views.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
0
ChatServiceApp/__init__.py
Normal file
50
ChatServiceApp/admin.py
Normal file
@@ -0,0 +1,50 @@
|
||||
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 = [
|
||||
'id', 'department', 'name', 'status', 'owner', 'manager', 'text',
|
||||
'order', 'modifiedDT', 'createDT'
|
||||
]
|
||||
|
||||
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',
|
||||
'cut_group_text', 'status', 'sender', 'receiver', 'cut_text',
|
||||
'name',
|
||||
'order', 'modifiedDT', 'createDT'
|
||||
]
|
||||
|
||||
admin.site.register(Message,Admin_Message)
|
||||
6
ChatServiceApp/apps.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ChatserviceappConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'ChatServiceApp'
|
||||
17
ChatServiceApp/forms.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.core.exceptions import ValidationError
|
||||
from .models import *
|
||||
|
||||
|
||||
class TicketForm(forms.ModelForm):
|
||||
# text = forms.CharField(required=True)
|
||||
# files = forms.CharField(required=True)
|
||||
class Meta:
|
||||
model = MsgGroup
|
||||
exclude = [
|
||||
'files', 'status', 'owner', 'manager'
|
||||
# 'name', 'name_plural', 'order', 'createDT', 'modifiedDT', 'enable', 'json_data',
|
||||
# 'receive_msg_by_sms', 'owner', 'owner_type'
|
||||
]
|
||||
461
ChatServiceApp/funcs.py
Normal file
@@ -0,0 +1,461 @@
|
||||
import copy
|
||||
|
||||
from .models import *
|
||||
from django.db.models import Q, Value as V, Count, OuterRef, Subquery
|
||||
from django.http import HttpResponse, Http404, JsonResponse
|
||||
from django.template import loader, RequestContext
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from BaseModels.mailSender import techSendMail
|
||||
from django.utils.translation import gettext as _
|
||||
from datetime import datetime
|
||||
from django.template.loader import render_to_string
|
||||
from django.urls import reverse
|
||||
import json
|
||||
from datetime import datetime, time
|
||||
from django.conf import settings
|
||||
from AuthApp.funcs import get_user_timezone_Dict
|
||||
|
||||
|
||||
def get_unanswered_msgs_count_for_user(user):
|
||||
if not user or not user.is_authenticated:
|
||||
return 0
|
||||
|
||||
msgs = Message.objects.filter(receiver=user, status='sended', group=None)
|
||||
return msgs.count()
|
||||
|
||||
|
||||
|
||||
def get_update_chat_Dict(data):
|
||||
from AuthApp.models import User
|
||||
|
||||
res_Dict = {}
|
||||
msgs = []
|
||||
Dict = {}
|
||||
context_Dict = {}
|
||||
|
||||
last_message_modifiedDT = None
|
||||
|
||||
required_beep = False
|
||||
|
||||
required_full_support_chat_html = False
|
||||
|
||||
try:
|
||||
|
||||
if type(data) == str:
|
||||
data = json.loads(data)
|
||||
ticket = None
|
||||
|
||||
tpl_name = 'blocks/profile/b_messages_container.html'
|
||||
|
||||
if 'ticket_id' in data and data['ticket_id'] and data['ticket_id'] != 'null':
|
||||
ticket = MsgGroup.objects.get(id=data['ticket_id'])
|
||||
res_Dict.update({'ticket': ticket.id})
|
||||
|
||||
sender = User.objects.get(id=data['sender'])
|
||||
receiver = None
|
||||
if 'receiver' in data and data['receiver']:
|
||||
receiver = User.objects.get(id=data['receiver'])
|
||||
|
||||
if data['sender'] == data['cur_user']:
|
||||
user = copy.copy(sender)
|
||||
cur_receiver = copy.copy(receiver)
|
||||
else:
|
||||
user = copy.copy(receiver)
|
||||
cur_receiver = copy.copy(sender)
|
||||
# context_Dict.update({'user': receiver})
|
||||
|
||||
# context_Dict.update({'cur_receiver': receiver})
|
||||
context_Dict.update({
|
||||
'cur_receiver': cur_receiver,
|
||||
'user': user,
|
||||
})
|
||||
context_Dict.update(get_user_timezone_Dict(user))
|
||||
|
||||
if sender == receiver:
|
||||
print('!')
|
||||
|
||||
required_beep = data['required_beep']
|
||||
|
||||
|
||||
if not ticket:
|
||||
|
||||
if data['receiver'] == data['cur_user']:
|
||||
# получаем правую панель с получателями
|
||||
# получатели
|
||||
receivers, unread_msgs_count = get_chat_receivers_for_user(receiver)
|
||||
|
||||
|
||||
# формируем правую панель
|
||||
context_Dict.update({
|
||||
'receivers': receivers,
|
||||
'MEDIA_URL': settings.MEDIA_URL
|
||||
})
|
||||
users_list_html = render_to_string(
|
||||
'blocks/profile/b_list_of_users_messenger.html', context_Dict)
|
||||
res_Dict.update({
|
||||
'users_list_html': users_list_html,
|
||||
'unread_msgs_count': unread_msgs_count
|
||||
})
|
||||
|
||||
msgs = get_msgs_for_chat_w_users(sender, receiver)
|
||||
|
||||
if ticket:
|
||||
msgs = get_messages_for_ticket(ticket)
|
||||
# если меньше 3 сообщений значит выведена не полная форма - требуется другой шаблон
|
||||
if len(msgs) < 3 and data['receiver'] == data['cur_user']:
|
||||
tpl_name = 'blocks/profile/b_support_chat.html'
|
||||
required_full_support_chat_html = True
|
||||
elif 'bad_manager' in data:
|
||||
tpl_name = 'blocks/profile/b_support_chat.html'
|
||||
required_full_support_chat_html = True
|
||||
|
||||
context_Dict.update({'ticket': ticket})
|
||||
if ticket.manager:
|
||||
context_Dict.update({'new_msg_allow': True})
|
||||
|
||||
context_Dict.update({
|
||||
'messages': msgs,
|
||||
'MEDIA_URL': settings.MEDIA_URL,
|
||||
})
|
||||
html = render_to_string(tpl_name, context_Dict)
|
||||
if required_full_support_chat_html:
|
||||
res_Dict.update({'support_chat_html': html})
|
||||
else:
|
||||
res_Dict.update({'chat_html': html})
|
||||
|
||||
|
||||
res_Dict.update({
|
||||
'required_beep': required_beep,
|
||||
})
|
||||
return res_Dict
|
||||
|
||||
except Exception as e:
|
||||
msg = f'update_chat_ajax2 Error = {str(e)}'
|
||||
return {'error': msg}
|
||||
|
||||
|
||||
def send_msg(data):
|
||||
from AuthApp.models import User
|
||||
import base64
|
||||
import os
|
||||
|
||||
res_Dict = {}
|
||||
msg = None
|
||||
required_update_tickets_list_wo_managers = False
|
||||
|
||||
try:
|
||||
if type(data) == str:
|
||||
data = json.loads(data)
|
||||
|
||||
ticket = None
|
||||
|
||||
tpl_name = 'blocks/profile/b_messages_container.html'
|
||||
|
||||
if 'text' in data or 'files' in data:
|
||||
|
||||
if 'ticket_id' in data:
|
||||
ticket = MsgGroup.objects.get(id=data['ticket_id'])
|
||||
|
||||
sender = User.objects.get(id=data['sender'])
|
||||
receiver = User.objects.get(id=data['receiver'])
|
||||
|
||||
msg_create_kwargs = {}
|
||||
|
||||
# если только что манагер присоединился к тикету
|
||||
if ticket:
|
||||
required_update_tickets_list_wo_managers = True
|
||||
if not ticket.manager:
|
||||
if sender.is_staff:
|
||||
ticket.manager = sender
|
||||
else:
|
||||
ticket.manager = receiver
|
||||
|
||||
ticket.save()
|
||||
|
||||
# создаем сообщение на базе темы и сообщения введенных пользователем при создании тикета
|
||||
kwargs = {
|
||||
'sender': ticket.owner,
|
||||
'receiver': ticket.manager,
|
||||
'text': ticket.text,
|
||||
'group': ticket,
|
||||
}
|
||||
|
||||
msg = Message.objects.create(**kwargs)
|
||||
Message.objects.filter(id=msg.id).update(modifiedDT=ticket.createDT, createDT=ticket.createDT)
|
||||
# ------------------------
|
||||
|
||||
else:
|
||||
if not sender in (ticket.owner, ticket.manager) or not receiver in (ticket.owner, ticket.manager):
|
||||
return {
|
||||
'msg': None,
|
||||
'required_update_tickets_list_wo_managers': required_update_tickets_list_wo_managers
|
||||
}
|
||||
|
||||
res_Dict.update({
|
||||
'ticket': ticket,
|
||||
})
|
||||
|
||||
msg_create_kwargs.update({
|
||||
'group': ticket,
|
||||
})
|
||||
|
||||
res_Dict.update(get_ticketsDict_for_staff(sender))
|
||||
|
||||
# if not msg:
|
||||
msg_create_kwargs.update({
|
||||
'sender': sender,
|
||||
'receiver': receiver,
|
||||
})
|
||||
|
||||
if 'text' in data:
|
||||
msg_create_kwargs.update({'text': data['text']})
|
||||
|
||||
msg = Message.objects.create(**msg_create_kwargs)
|
||||
|
||||
if 'files' in data:
|
||||
files_list = []
|
||||
for file in data['files']:
|
||||
file_data =json.loads(file)
|
||||
|
||||
if not os.path.exists(f'chat_file_storage/{msg.id}'):
|
||||
os.makedirs(f'chat_file_storage/{msg.id}')
|
||||
f = open(f'chat_file_storage/{msg.id}/{file_data["file_name"]}', 'wb+')
|
||||
head, content = file_data['file'].split(',')
|
||||
content = base64.b64decode(content)
|
||||
f.write(content)
|
||||
f.close()
|
||||
|
||||
del file_data['file']
|
||||
files_list.append(file_data)
|
||||
msg.files = files_list
|
||||
msg.save(update_fields=['files'])
|
||||
|
||||
|
||||
|
||||
# if ticket:
|
||||
# msgs = get_messages_for_ticket(ticket)
|
||||
# else:
|
||||
# msgs = get_msgs_for_chat_w_users(sender, receiver)
|
||||
#
|
||||
# receivers, unread_msgs_count = get_chat_receivers_for_user(sender)
|
||||
#
|
||||
# res_Dict.update({
|
||||
# 'messages': msgs,
|
||||
# 'cur_receiver': receiver,
|
||||
# 'receivers': receivers,
|
||||
# 'text': data['text'],
|
||||
# 'modifiedDT': msgs[0].modifiedDT
|
||||
# })
|
||||
#
|
||||
# html = render_to_string(tpl_name, res_Dict)
|
||||
# return {'html': html, 'sender': data['sender']}
|
||||
return {
|
||||
'msg': msg,
|
||||
'required_update_tickets_list_wo_managers': required_update_tickets_list_wo_managers
|
||||
}
|
||||
|
||||
except Exception as e:
|
||||
msg = f'send_msg_ajax Error = {str(e)}'
|
||||
print(msg)
|
||||
# return {'error': msg}
|
||||
return {
|
||||
'msg': msg,
|
||||
'required_update_tickets_list_wo_managers': required_update_tickets_list_wo_managers
|
||||
}
|
||||
|
||||
|
||||
|
||||
# def get_create_route_for_customer_page_content_Dict(request):
|
||||
# from AuthApp.models import User
|
||||
#
|
||||
# msgs = []
|
||||
# try:
|
||||
# cur_receiver = User.objects.get(id=receiver_id)
|
||||
#
|
||||
# msgs = get_msgs_for_chat_w_users(request.user, cur_receiver)
|
||||
# msgs.filter(receiver=request.user).update(status='seen')
|
||||
#
|
||||
# except User.DoesNotExist:
|
||||
# cur_receiver = None
|
||||
#
|
||||
# receivers, unread_msgs_count = get_chat_receivers_for_user(request.user)
|
||||
#
|
||||
# Dict = {
|
||||
# 'cur_receiver': cur_receiver,
|
||||
# 'messages': msgs,
|
||||
# 'receivers': receivers,
|
||||
# 'page': 'chat',
|
||||
# }
|
||||
# return Dict
|
||||
|
||||
|
||||
def get_chat_page_content_html(request, receiver_id=None):
|
||||
from AuthApp.models import User
|
||||
from AuthApp.funcs import get_user_timezone_Dict
|
||||
|
||||
msgs = []
|
||||
try:
|
||||
cur_receiver = User.objects.get(id=receiver_id)
|
||||
|
||||
msgs = get_msgs_for_chat_w_users(request.user, cur_receiver)
|
||||
msgs.filter(receiver=request.user).update(status='seen')
|
||||
|
||||
except User.DoesNotExist:
|
||||
cur_receiver = None
|
||||
|
||||
receivers, unread_msgs_count = get_chat_receivers_for_user(request.user)
|
||||
|
||||
Dict = {
|
||||
'cur_receiver': cur_receiver,
|
||||
'messages': msgs,
|
||||
'receivers': receivers,
|
||||
'page': 'chat',
|
||||
}
|
||||
Dict.update(get_user_timezone_Dict(request.user, request=request))
|
||||
tpl_name = 'blocks/profile/b_chats.html'
|
||||
|
||||
html = render_to_string(tpl_name, Dict, request=request)
|
||||
return html
|
||||
|
||||
def get_chat_page_content_Dict(request, receiver_id=None):
|
||||
from AuthApp.models import User
|
||||
|
||||
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_msgs_for_chat_w_users(user1, user2):
|
||||
|
||||
msgs = Message.objects.filter(
|
||||
Q(sender=user1) | Q(receiver=user1),
|
||||
Q(sender=user2) | Q(receiver=user2),
|
||||
group=None
|
||||
).order_by('-modifiedDT')
|
||||
return msgs
|
||||
|
||||
def get_chat_receivers_for_user(user, cur_receiver=None):
|
||||
msgs = Message.objects.filter(
|
||||
Q(sender=user) | Q(receiver=user),
|
||||
group=None
|
||||
).order_by('-modifiedDT')#.select_related('sender', 'receiver').values('sender', 'receiver').distinct()
|
||||
|
||||
receivers_list = []
|
||||
|
||||
unread_msgs_count = 0
|
||||
|
||||
for msg in msgs:
|
||||
if msg.receiver not in receivers_list and msg.receiver != user:
|
||||
msg.receiver.unread_msgs_count = 0
|
||||
if msg.text:
|
||||
msg.receiver.last_msg = msg.text
|
||||
elif msg.files:
|
||||
msg.receiver.last_msg = msg.files[len(msg.files)-1]['file_name']
|
||||
receivers_list.append(msg.receiver)
|
||||
|
||||
|
||||
|
||||
if msg.sender not in receivers_list and msg.sender != user:
|
||||
msg.sender.unread_msgs_count = 0
|
||||
receivers_list.append(msg.sender)
|
||||
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)
|
||||
receivers_list[i].unread_msgs_count += 1
|
||||
unread_msgs_count += 1
|
||||
|
||||
# if msg.status == 'sended':
|
||||
# i = receivers_list.index(msg.sender)
|
||||
# receivers_list[i].unread_msgs_count += 1
|
||||
# unread_msgs_count += 1
|
||||
|
||||
# if msg.receiver == user:# and (not cur_receiver or msg.sender != cur_receiver):
|
||||
# if msg.status == 'sended':
|
||||
# i = receivers_list.index(msg.sender)
|
||||
# receivers_list[i].unread_msgs_count += 1
|
||||
# unread_msgs_count += 1
|
||||
|
||||
return receivers_list, unread_msgs_count
|
||||
|
||||
# receivers_list.extend((item.sender for item in receivers if item.sender != user))
|
||||
# receivers_list.extend((item.receiver for item in receivers if item.receiver != user))
|
||||
#
|
||||
# return list(set(receivers_list))
|
||||
|
||||
|
||||
def get_tickets_Dict_by_manager(user):
|
||||
tickets = MsgGroup.objects.filter(enable=True, manager=user).order_by('-modifiedDT')
|
||||
|
||||
Dict = {
|
||||
'tickets': tickets,
|
||||
}
|
||||
return Dict
|
||||
|
||||
def get_messages_for_ticket(ticket):
|
||||
return ticket.rel_messages_for_group.filter(enable=True).order_by('-modifiedDT')
|
||||
|
||||
|
||||
def get_ticketsDict_for_staff(user):
|
||||
Dict = {
|
||||
'tickets_wo_manager': get_tickets_wo_manager(),
|
||||
'tickets_for_manager': get_tickets_for_manager(user),
|
||||
}
|
||||
return Dict
|
||||
|
||||
def get_tickets_wo_manager():
|
||||
|
||||
tickets = MsgGroup.objects.filter(
|
||||
enable=True, manager=None
|
||||
).annotate(
|
||||
unread_msgs_count=V(1)
|
||||
).order_by('-modifiedDT')
|
||||
|
||||
|
||||
return tickets
|
||||
|
||||
def get_tickets_for_manager(user):
|
||||
unread_msgs = Message.objects.filter(
|
||||
group=31, status='sended', receiver=user
|
||||
)
|
||||
|
||||
unread_msgs = Message.objects.filter(
|
||||
group=OuterRef('pk'), status='sended', receiver=user
|
||||
).values('id')[:1]
|
||||
|
||||
msgs = Message.objects.filter(
|
||||
group=OuterRef('pk')
|
||||
).order_by('-modifiedDT').values('modifiedDT')[:1]
|
||||
|
||||
tickets = MsgGroup.objects.filter(
|
||||
enable=True, manager=user
|
||||
).annotate(
|
||||
unread_msgs_count = Count(Subquery(unread_msgs)),
|
||||
last_msg_modifiedDT = Subquery(msgs)
|
||||
# unread_msgs_count=Count(
|
||||
# 'rel_messages_for_group',
|
||||
# filter=Q(rel_messages_for_group__status='sended', rel_messages_for_group__receiver=user)
|
||||
# )
|
||||
).order_by('-unread_msgs_count', '-last_msg_modifiedDT')
|
||||
|
||||
|
||||
return tickets
|
||||
18
ChatServiceApp/js_urls.py
Normal file
@@ -0,0 +1,18 @@
|
||||
# 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('support_create_ticket_form/', support_create_ticket_form_ajax, name='support_create_ticket_form_ajax'),
|
||||
path('create_ticket/', create_ticket_ajax, name='create_ticket_ajax'),
|
||||
path('support_show_chat_by_ticket/', support_show_chat_by_ticket_ajax, name='support_show_chat_by_ticket_ajax'),
|
||||
# path('send_msg/', send_msg_ajax, name='send_msg_ajax'),
|
||||
# path('update_chat/', update_chat_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'),
|
||||
]
|
||||
578
ChatServiceApp/js_views.py
Normal file
@@ -0,0 +1,578 @@
|
||||
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
|
||||
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
|
||||
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
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
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
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):
|
||||
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
try:
|
||||
|
||||
data = json.loads(request.body)
|
||||
Dict = get_chat_page_content_Dict(request, data['user_id'])
|
||||
Dict.update(get_user_timezone_Dict(request.user, request=request))
|
||||
|
||||
tpl_name = 'blocks/profile/b_chats.html'
|
||||
|
||||
html = render_to_string(tpl_name, Dict, request=request)
|
||||
|
||||
res_Dict = {'html': html}
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
except Exception as e:
|
||||
msg = f'show_chat_w_user_ajax Error = {str(e)}'
|
||||
return JsonResponse({'error': msg}, status=400)
|
||||
|
||||
|
||||
|
||||
def update_chat_ajax2(request):
|
||||
from AuthApp.models import User
|
||||
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
res_Dict = {}
|
||||
msgs = []
|
||||
Dict = {}
|
||||
context_Dict = {}
|
||||
|
||||
last_message_modifiedDT = None
|
||||
|
||||
required_beep = False
|
||||
|
||||
try:
|
||||
|
||||
data = json.loads(request.body)
|
||||
ticket = None
|
||||
|
||||
tpl_name = 'blocks/profile/b_messages_container.html'
|
||||
|
||||
|
||||
if 'ticket_id' in data and data['ticket_id'] and data['ticket_id'] != 'null':
|
||||
ticket = MsgGroup.objects.get(id=data['ticket_id'])
|
||||
|
||||
sender = User.objects.get(id=data['sender'])
|
||||
receiver = None
|
||||
if 'receiver' in data and data['receiver']:
|
||||
receiver = User.objects.get(id=data['receiver'])
|
||||
|
||||
context_Dict.update({'cur_receiver': receiver})
|
||||
context_Dict.update(get_user_timezone_Dict(request.user, request=request))
|
||||
|
||||
if not ticket:
|
||||
|
||||
# получаем правую панель с получателями
|
||||
# получатели
|
||||
receivers, unread_msgs_count = get_chat_receivers_for_user(request.user, receiver)
|
||||
# собираем для сохранения в профиле
|
||||
receivers_unread_msgs_count = sorted(
|
||||
[{'id': item.id, 'unread_msgs_count': item.unread_msgs_count} for item in receivers],
|
||||
key=lambda d: d['id'])
|
||||
# забираем данные из профиля
|
||||
saved_receivers_unread_msgs_count = request.user.user_profile.get_node_by_name(
|
||||
'receivers_unread_msgs_count')
|
||||
# если данных нет или данные поменялись
|
||||
if saved_receivers_unread_msgs_count == None or receivers_unread_msgs_count != saved_receivers_unread_msgs_count:
|
||||
# записываем данные в профиль
|
||||
request.user.user_profile.add_node_to_json_data(
|
||||
{'receivers_unread_msgs_count': receivers_unread_msgs_count}, save=True)
|
||||
# разрешаем сигнал
|
||||
required_beep = True
|
||||
|
||||
# формируем правую панель
|
||||
context_Dict.update({'receivers': receivers})
|
||||
|
||||
users_list_html = render_to_string(
|
||||
'blocks/profile/b_list_of_users_messenger.html', context_Dict, request=request)
|
||||
res_Dict.update({
|
||||
'users_list_html': users_list_html,
|
||||
'unread_msgs_count': unread_msgs_count
|
||||
})
|
||||
|
||||
|
||||
if sender and receiver:
|
||||
msgs = get_msgs_for_chat_w_users(sender, receiver)
|
||||
unreaded_msgs = msgs.filter(status='sended')
|
||||
if msgs and unreaded_msgs:
|
||||
context_Dict.update({'messages': msgs})
|
||||
chat_html = render_to_string(tpl_name, context_Dict, request=request)
|
||||
res_Dict.update({'chat_html': chat_html})
|
||||
required_beep = True
|
||||
unreaded_msgs.update(status='seen')
|
||||
|
||||
|
||||
|
||||
res_Dict.update({
|
||||
'required_beep': required_beep,
|
||||
})
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
except Exception as e:
|
||||
msg = f'update_chat_ajax2 Error = {str(e)}'
|
||||
return JsonResponse({'error': msg}, status=400)
|
||||
|
||||
|
||||
|
||||
|
||||
def update_chat_ajax(request):
|
||||
from AuthApp.models import User
|
||||
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
res_Dict = {}
|
||||
msgs = []
|
||||
Dict = {}
|
||||
context_Dict = {}
|
||||
|
||||
last_message_modifiedDT = None
|
||||
|
||||
required_beep = False
|
||||
|
||||
try:
|
||||
|
||||
data = json.loads(request.body)
|
||||
ticket = None
|
||||
|
||||
tpl_name = 'blocks/profile/b_messages_container.html'
|
||||
|
||||
|
||||
if 'ticket_id' in data and data['ticket_id'] and data['ticket_id'] != 'null':
|
||||
ticket = MsgGroup.objects.get(id=data['ticket_id'])
|
||||
|
||||
if 'last_message_modifiedDT' in data:
|
||||
last_message_modifiedDT = datetime.strptime(data['last_message_modifiedDT'], '%d.%m.%Y %H:%M:%S:%f')
|
||||
|
||||
sender = User.objects.get(id=data['sender'])
|
||||
receiver = None
|
||||
if 'receiver' in data and data['receiver']:
|
||||
receiver = User.objects.get(id=data['receiver'])
|
||||
|
||||
context_Dict.update({'cur_receiver': receiver})
|
||||
context_Dict.update(get_user_timezone_Dict(request.user, request=request))
|
||||
|
||||
if ticket:
|
||||
|
||||
context_Dict.update({
|
||||
'ticket': ticket,
|
||||
})
|
||||
|
||||
msgs = get_messages_for_ticket(ticket)
|
||||
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({
|
||||
'tickets_list_html': tickets_list_html,
|
||||
'msgs_count': len(msgs)
|
||||
})
|
||||
else:
|
||||
|
||||
# получаем правую панель с получателями
|
||||
receivers, unread_msgs_count = get_chat_receivers_for_user(request.user, receiver)
|
||||
receivers_unread_msgs_count = sorted([{'id':item.id, 'unread_msgs_count': item.unread_msgs_count} for item in receivers], key=lambda d: d['id'])
|
||||
saved_receivers_unread_msgs_count = request.user.user_profile.get_node_by_name('receivers_unread_msgs_count')
|
||||
if saved_receivers_unread_msgs_count == None or receivers_unread_msgs_count != saved_receivers_unread_msgs_count:
|
||||
request.user.user_profile.add_node_to_json_data(
|
||||
{'receivers_unread_msgs_count': receivers_unread_msgs_count}, save=True)
|
||||
# if unread_msgs_count:
|
||||
required_beep = True
|
||||
|
||||
context_Dict.update({'receivers': receivers})
|
||||
context_Dict.update(get_user_timezone_Dict(request.user))
|
||||
|
||||
users_list_html = render_to_string(
|
||||
'blocks/profile/b_list_of_users_messenger.html', context_Dict, request=request)
|
||||
res_Dict.update({
|
||||
'users_list_html': users_list_html,
|
||||
'unread_msgs_count': unread_msgs_count
|
||||
})
|
||||
|
||||
# если есть получатель - получаем сообщения чата
|
||||
if receiver:
|
||||
msgs = get_msgs_for_chat_w_users(sender, receiver)
|
||||
unreaded_msgs = msgs.filter(status='sended')
|
||||
if msgs and unreaded_msgs:
|
||||
context_Dict.update({'messages': msgs})
|
||||
chat_html = render_to_string(tpl_name, context_Dict, request=request)
|
||||
res_Dict.update({'chat_html': chat_html})
|
||||
required_beep = True
|
||||
unreaded_msgs.update(status='seen')
|
||||
|
||||
# if not msgs or (request.user != msgs[0].receiver) or (not msgs and not last_message_modifiedDT) or (msgs and last_message_modifiedDT and msgs[0].modifiedDT <= last_message_modifiedDT):
|
||||
# Dict.update({
|
||||
# 'required_beep': False
|
||||
# })
|
||||
# return JsonResponse(Dict, status=200)
|
||||
|
||||
|
||||
|
||||
res_Dict.update({
|
||||
'required_beep': required_beep,
|
||||
})
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
except Exception as e:
|
||||
msg = f'update_chat_ajax Error = {str(e)}'
|
||||
return JsonResponse({'error': msg}, status=400)
|
||||
|
||||
|
||||
# @login_required()#login_url='/profile/login/')
|
||||
# def send_msg_ajax(request):
|
||||
# from AuthApp.models import User
|
||||
#
|
||||
# if request.method != 'POST':
|
||||
# raise Http404
|
||||
#
|
||||
# res_Dict = {}
|
||||
# msg = None
|
||||
#
|
||||
# try:
|
||||
#
|
||||
# data = json.loads(request.body)
|
||||
# ticket = None
|
||||
#
|
||||
# tpl_name = 'blocks/profile/b_messages_container.html'
|
||||
#
|
||||
# if data['text']:
|
||||
#
|
||||
#
|
||||
# if 'ticket_id' in data:
|
||||
# ticket = MsgGroup.objects.get(id=data['ticket_id'])
|
||||
#
|
||||
# sender = User.objects.get(id=data['sender'])
|
||||
# receiver = User.objects.get(id=data['receiver'])
|
||||
#
|
||||
# msg_create_kwargs = {}
|
||||
#
|
||||
# # если только что манагер присоединился к тикету
|
||||
# if ticket:
|
||||
# if not ticket.manager:
|
||||
# if sender.is_staff:
|
||||
# ticket.manager = sender
|
||||
# else:
|
||||
# ticket.manager = receiver
|
||||
#
|
||||
# ticket.save()
|
||||
#
|
||||
# kwargs = {
|
||||
# 'sender': ticket.owner,
|
||||
# 'receiver': ticket.manager,
|
||||
# 'text': ticket.text,
|
||||
# }
|
||||
#
|
||||
# kwargs.update({'group': ticket})
|
||||
#
|
||||
# msg = Message.objects.create(**kwargs)
|
||||
# Message.objects.filter(id=msg.id).update(modifiedDT=ticket.createDT, createDT=ticket.createDT)
|
||||
#
|
||||
# # if
|
||||
#
|
||||
# res_Dict.update({
|
||||
# 'ticket': ticket,
|
||||
# # 'cur_receiver': receiver,
|
||||
# # 'messages': get_messages_for_ticket(ticket),
|
||||
# # 'text': data['text'],
|
||||
# # 'modifiedDT': msg.modifiedDT
|
||||
# })
|
||||
#
|
||||
# msg_create_kwargs.update({
|
||||
# 'group': ticket,
|
||||
# })
|
||||
#
|
||||
# res_Dict.update(get_ticketsDict_for_staff(request.user))
|
||||
#
|
||||
#
|
||||
# # if not msg:
|
||||
# msg_create_kwargs.update({
|
||||
# 'sender': sender,
|
||||
# 'receiver': receiver,
|
||||
# 'text': data['text']
|
||||
# })
|
||||
#
|
||||
# session_data = {
|
||||
# 'for_save_to_session':{
|
||||
# 'user_alerts':{
|
||||
# 'new_message': True
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# receiver.user_profile.add_node_to_json_data(session_data, save=True)
|
||||
#
|
||||
# msg = Message.objects.create(**msg_create_kwargs)
|
||||
#
|
||||
# if ticket:
|
||||
# msgs = get_messages_for_ticket(ticket)
|
||||
# else:
|
||||
# msgs = get_msgs_for_chat_w_users(sender, receiver)
|
||||
#
|
||||
# receivers, unread_msgs_count = get_chat_receivers_for_user(request.user)
|
||||
#
|
||||
#
|
||||
# res_Dict.update({
|
||||
# 'messages': msgs,
|
||||
# 'cur_receiver': receiver,
|
||||
# 'receivers': receivers,
|
||||
# 'text': data['text'],
|
||||
# 'modifiedDT': msgs[0].modifiedDT
|
||||
# })
|
||||
#
|
||||
#
|
||||
# html = render_to_string(tpl_name, res_Dict, request=request)
|
||||
# return JsonResponse({'html': html}, status=200)
|
||||
#
|
||||
# except Exception as e:
|
||||
# msg = f'send_msg_ajax Error = {str(e)}'
|
||||
# return JsonResponse({'error': msg}, status=400)
|
||||
|
||||
|
||||
|
||||
|
||||
@login_required()#login_url='/profile/login/')
|
||||
def support_show_chat_by_ticket_ajax(request):
|
||||
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
try:
|
||||
|
||||
data = json.loads(request.body)
|
||||
|
||||
ticket = MsgGroup.objects.get(id=data['ticket_id'])
|
||||
|
||||
msgs = get_messages_for_ticket(ticket)
|
||||
|
||||
new_msg_allow = False
|
||||
cur_receiver = None
|
||||
if request.user.is_staff:
|
||||
cur_receiver = ticket.owner
|
||||
new_msg_allow = True
|
||||
else:
|
||||
if ticket.manager:
|
||||
cur_receiver = ticket.manager
|
||||
# if len(msgs) > 1:
|
||||
new_msg_allow = True
|
||||
|
||||
seen_msgs = msgs.filter(receiver=request.user, status='sended')
|
||||
if seen_msgs:
|
||||
seen_msgs.update(status='seen')
|
||||
msgs = get_messages_for_ticket(ticket)
|
||||
|
||||
Dict = {
|
||||
'ticket': ticket,
|
||||
'messages': msgs,
|
||||
'cur_receiver': cur_receiver,
|
||||
'new_msg_allow': new_msg_allow,
|
||||
'staff': request.user.is_staff,
|
||||
# 'mobile': data['mobile']
|
||||
}
|
||||
Dict.update(get_ticketsDict_for_staff(request.user))
|
||||
|
||||
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)
|
||||
|
||||
res_Dict = {'html': html}
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
except Exception as e:
|
||||
msg = f'support_show_chat_by_ticket_ajax Error = {str(e)}'
|
||||
return JsonResponse({'error': msg}, status=400)
|
||||
|
||||
|
||||
@login_required()#login_url='/profile/login/')
|
||||
def support_create_ticket_form_ajax(request):
|
||||
from ChatServiceApp.forms import TicketForm
|
||||
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
lang = get_and_set_lang(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)
|
||||
|
||||
res_Dict = {'html': html}
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
|
||||
@login_required()#login_url='/profile/login/')
|
||||
def create_ticket_ajax(request):
|
||||
from ChatServiceApp.forms import TicketForm
|
||||
|
||||
if request.method != 'POST':
|
||||
raise Http404
|
||||
|
||||
from GeneralApp.funcs import get_and_set_lang
|
||||
lang = get_and_set_lang(request)
|
||||
|
||||
try:
|
||||
|
||||
data = request.POST
|
||||
|
||||
form = TicketForm(data)
|
||||
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)
|
||||
|
||||
ticket = form.save(commit=False)
|
||||
ticket.owner = request.user
|
||||
ticket.enable = True
|
||||
ticket.save()
|
||||
|
||||
from .websocket_views import send_to_support_managers_list_tickets_wo_manager
|
||||
send_to_support_managers_list_tickets_wo_manager(ticket, required_beep=True)
|
||||
# # рассылаем всем менеджерам сообщение
|
||||
#
|
||||
# Dict = {
|
||||
# 'ticket': ticket,
|
||||
# 'tickets_wo_manager': get_tickets_wo_manager()
|
||||
# }
|
||||
# tickets_wo_manager_html = render_to_string('widgets/w_tickets_wo_manager.html', Dict, request=request)
|
||||
#
|
||||
# group_name = 'support_managers'
|
||||
# Dict = {
|
||||
# 'type': 'update_chat',
|
||||
# 'tickets_wo_manager_html': tickets_wo_manager_html,
|
||||
# 'required_beep': True,
|
||||
# 'group_name': group_name,
|
||||
# }
|
||||
# channel_layer = get_channel_layer()
|
||||
# async_to_sync(channel_layer.group_send)(
|
||||
# group_name,
|
||||
# Dict
|
||||
# )
|
||||
#
|
||||
#
|
||||
# # ---------------------
|
||||
|
||||
msgs_for_ticket = get_messages_for_ticket(ticket)
|
||||
Dict = {
|
||||
'ticket': ticket,
|
||||
'messages': 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 = {
|
||||
'html': html
|
||||
}
|
||||
|
||||
from GeneralApp.funcs import get_add_to_ajax_response_Dict
|
||||
res_Dict.update(get_add_to_ajax_response_Dict(request.user))
|
||||
return JsonResponse(res_Dict)
|
||||
|
||||
except Exception as e:
|
||||
|
||||
msg = f'{_("ошибка в запросе")} = {str(e)}'
|
||||
|
||||
errors_Dict = {
|
||||
'errors': {
|
||||
'all__': msg
|
||||
}
|
||||
}
|
||||
Dict = {'form': errors_Dict}
|
||||
html = render_to_string('blocks/profile/b_create_ticket.html', Dict, request=request)
|
||||
return JsonResponse({'html': html}, status=400)
|
||||
|
||||
|
||||
|
||||
def sendDeployments(owner, armies):
|
||||
type = "renderDeployments"
|
||||
message = owner + " has " + str(armies) + " to deploy"
|
||||
channel_layer = get_channel_layer()
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
'render_updates_group',
|
||||
{'type': 'render', 'message': message}
|
||||
)
|
||||
58
ChatServiceApp/migrations/0001_initial.py
Normal file
@@ -0,0 +1,58 @@
|
||||
# Generated by Django 4.2.2 on 2023-07-31 14:34
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='MsgGroup',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
|
||||
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
|
||||
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
|
||||
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
|
||||
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
|
||||
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
|
||||
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
|
||||
('department', models.CharField(choices=[('support', 'Отдел: Техническая поддержка'), ('finance', 'Отдел: Финансовый департамент')], default='support', verbose_name='Отдел')),
|
||||
('status', models.CharField(choices=[('open', 'Открыт'), ('answered', 'Отвечен'), ('closed', 'Закрыт')], default='open', verbose_name='Статус')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Message',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
|
||||
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
|
||||
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
|
||||
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
|
||||
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
|
||||
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
|
||||
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
|
||||
('msg_type', models.CharField(choices=[('support', 'техподдержка'), ('private', 'личное')], default='private', max_length=50, verbose_name='Тип сообщения')),
|
||||
('text', models.TextField(verbose_name='Сообщение')),
|
||||
('status', models.CharField(choices=[('sended', 'Отправлено'), ('seen', 'Просмотрено')], default='sended', verbose_name='Статус')),
|
||||
('files', models.JSONField(default=dict, verbose_name='Прикрепленные файлы')),
|
||||
('group', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rel_messages_for_group', to='ChatServiceApp.msggroup', verbose_name='Группа сообщений')),
|
||||
('receiver', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rel_messages_for_receiver', to=settings.AUTH_USER_MODEL, verbose_name='Получатель')),
|
||||
('sender', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rel_messages_for_sender', to=settings.AUTH_USER_MODEL, verbose_name='Отправитель')),
|
||||
],
|
||||
options={
|
||||
'abstract': False,
|
||||
},
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,26 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-01 17:57
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
('ChatServiceApp', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='msggroup',
|
||||
name='manager',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rel_msgGroups_for_manager', to=settings.AUTH_USER_MODEL, verbose_name='Менеджер'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='msggroup',
|
||||
name='owner',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='rel_msgGroups_for_owner', to=settings.AUTH_USER_MODEL, verbose_name='Владелец'),
|
||||
),
|
||||
]
|
||||
19
ChatServiceApp/migrations/0003_msggroup_text.py
Normal file
@@ -0,0 +1,19 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-02 14:54
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ChatServiceApp', '0002_msggroup_manager_msggroup_owner'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='msggroup',
|
||||
name='text',
|
||||
field=models.TextField(default='', verbose_name='Сообщение'),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,21 @@
|
||||
# Generated by Django 4.2.2 on 2023-08-11 23:21
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('ChatServiceApp', '0003_msggroup_text'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(
|
||||
name='message',
|
||||
options={'verbose_name': 'Сообщение', 'verbose_name_plural': 'Сообщения'},
|
||||
),
|
||||
migrations.AlterModelOptions(
|
||||
name='msggroup',
|
||||
options={'verbose_name': 'Тикет', 'verbose_name_plural': 'Тикеты'},
|
||||
),
|
||||
]
|
||||
0
ChatServiceApp/migrations/__init__.py
Normal file
82
ChatServiceApp/models.py
Normal file
@@ -0,0 +1,82 @@
|
||||
from django.db import models
|
||||
from BaseModels.base_models import BaseModel
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
msg_type_choices = (
|
||||
('support', _('техподдержка')),
|
||||
('private', _('личное'))
|
||||
)
|
||||
|
||||
msg_status_choices = (
|
||||
('sended', _('Отправлено')),
|
||||
('seen', _('Просмотрено'))
|
||||
)
|
||||
|
||||
grp_msg_status = (
|
||||
('open', _('Открыт')),
|
||||
('answered', _('Отвечен')),
|
||||
('closed', _('Закрыт'))
|
||||
)
|
||||
|
||||
|
||||
grp_msg_department = (
|
||||
('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)
|
||||
|
||||
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 = _('Тикеты')
|
||||
|
||||
|
||||
class Message(BaseModel):
|
||||
from AuthApp.models import User
|
||||
|
||||
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',
|
||||
on_delete=models.SET_NULL, null=True)
|
||||
|
||||
sender = models.ForeignKey(
|
||||
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'
|
||||
)
|
||||
|
||||
text = models.TextField(verbose_name=_('Сообщение'))
|
||||
|
||||
status = models.CharField(verbose_name=_('Статус'), default='sended', choices=msg_status_choices)
|
||||
|
||||
files = models.JSONField(verbose_name=_('Прикрепленные файлы'), default=dict)
|
||||
|
||||
class Meta:
|
||||
verbose_name = _('Сообщение')
|
||||
verbose_name_plural = _('Сообщения')
|
||||
|
||||
0
ChatServiceApp/templatetags/__init__.py
Normal file
60
ChatServiceApp/templatetags/tt_chat.py
Normal file
@@ -0,0 +1,60 @@
|
||||
from django import template
|
||||
from django.template.defaultfilters import stringfilter
|
||||
from django.conf import settings
|
||||
|
||||
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_msg_side')
|
||||
|
||||
@register.simple_tag
|
||||
def get_ws_address():
|
||||
return settings.WS_ADDRESS
|
||||
|
||||
@register.simple_tag
|
||||
def get_filesize(size):
|
||||
if size:
|
||||
unit = 'B'
|
||||
if size / 1024 > 1:
|
||||
unit = 'KB'
|
||||
size = size / 1024
|
||||
if size / 1024 > 1:
|
||||
unit = 'MB'
|
||||
size = size / 1024
|
||||
if size / 1024 > 1:
|
||||
unit = 'GB'
|
||||
size = size / 1024
|
||||
if size / 1024 > 1:
|
||||
unit = 'TB'
|
||||
size = size / 1024
|
||||
size = round(size, 2)
|
||||
return f'{str(size)}{unit}'
|
||||
else:
|
||||
return ''
|
||||
|
||||
|
||||
@register.simple_tag
|
||||
def get_msg_side(cur_user, ticket, msg):
|
||||
if msg:
|
||||
# if msg.sender == cur_user:
|
||||
# return 'left'
|
||||
# else:
|
||||
# return 'right'
|
||||
if msg.sender == cur_user:
|
||||
return 'right'
|
||||
else:
|
||||
return 'left'
|
||||
elif ticket:
|
||||
if ticket.owner == cur_user:
|
||||
return 'right'
|
||||
else:
|
||||
return 'left'
|
||||
else:
|
||||
return 'right'
|
||||
|
||||
# return 'left'
|
||||
3
ChatServiceApp/tests.py
Normal file
@@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
11
ChatServiceApp/urls.py
Normal 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'),
|
||||
]
|
||||
26
ChatServiceApp/views.py
Normal file
@@ -0,0 +1,26 @@
|
||||
import json
|
||||
|
||||
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
|
||||
8
ChatServiceApp/websocket_urls.py
Normal file
@@ -0,0 +1,8 @@
|
||||
from django.urls import re_path
|
||||
|
||||
from .websocket_views import *
|
||||
|
||||
websocket_urlpatterns = [
|
||||
re_path(r'ws/socket-server/', ChatConsumer.as_asgi()),
|
||||
# re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer),
|
||||
]
|
||||
260
ChatServiceApp/websocket_views.py
Normal file
@@ -0,0 +1,260 @@
|
||||
import json
|
||||
from channels.generic.websocket import WebsocketConsumer, AsyncWebsocketConsumer, JsonWebsocketConsumer
|
||||
from asgiref.sync import async_to_sync, sync_to_async
|
||||
# from channels.auth import channel_session_user, channel_session_user_from_http
|
||||
from channels.layers import get_channel_layer
|
||||
from django.template.loader import render_to_string
|
||||
|
||||
|
||||
def send_to_support_managers_list_tickets_wo_manager(ticket, required_beep=False):
|
||||
from.funcs import get_tickets_wo_manager
|
||||
|
||||
# рассылаем всем менеджерам сообщение
|
||||
|
||||
Dict = {
|
||||
'ticket': ticket,
|
||||
'tickets_wo_manager': get_tickets_wo_manager()
|
||||
}
|
||||
tickets_wo_manager_html = render_to_string('widgets/w_tickets_wo_manager.html', Dict)
|
||||
|
||||
group_name = 'support_managers'
|
||||
Dict = {
|
||||
'type': 'update_chat',
|
||||
'tickets_wo_manager_html': tickets_wo_manager_html,
|
||||
'required_beep': required_beep,
|
||||
'group_name': group_name,
|
||||
}
|
||||
if ticket.manager:
|
||||
Dict.update({'ticket_manager': ticket.manager.id})
|
||||
|
||||
channel_layer = get_channel_layer()
|
||||
async_to_sync(channel_layer.group_send)(
|
||||
group_name,
|
||||
Dict
|
||||
)
|
||||
|
||||
# ---------------------
|
||||
return True
|
||||
|
||||
|
||||
def get_tickets_wo_manager_html(ticket, user, data):
|
||||
from .funcs import get_tickets_wo_manager
|
||||
|
||||
# если не менеджер - возвращаем None
|
||||
if not ticket or ticket.manager.id != user.id:
|
||||
return None
|
||||
|
||||
Dict = {
|
||||
'ticket': ticket,
|
||||
'tickets_wo_manager': get_tickets_wo_manager()
|
||||
}
|
||||
tickets_wo_manager_html = render_to_string('widgets/w_tickets_wo_manager.html', Dict)
|
||||
|
||||
return tickets_wo_manager_html
|
||||
|
||||
|
||||
def get_tickets_w_manager_html(ticket, user, data):
|
||||
from .funcs import get_tickets_for_manager
|
||||
|
||||
# если не менеджер - возвращаем None
|
||||
if not ticket or ticket.manager.id != user.id:
|
||||
return None
|
||||
|
||||
Dict = {
|
||||
'ticket': ticket,
|
||||
'tickets_for_manager': get_tickets_for_manager(user)
|
||||
}
|
||||
tickets_w_manager_html = render_to_string('widgets/w_tickets_w_manager.html', Dict)
|
||||
|
||||
return tickets_w_manager_html
|
||||
|
||||
|
||||
class ChatConsumer(WebsocketConsumer):
|
||||
|
||||
# def __init__(self, *args, **kwargs):
|
||||
# super().__init__(args, kwargs)
|
||||
# self.room_name = None
|
||||
|
||||
def disconnect(self, close_code):
|
||||
print("Closed websocket with code: ", close_code)
|
||||
|
||||
for gr in self.groups:
|
||||
async_to_sync(self.channel_layer.group_discard)(
|
||||
gr,
|
||||
self.channel_name
|
||||
)
|
||||
self.close()
|
||||
|
||||
def connect(self):
|
||||
print('ws connect')
|
||||
|
||||
user_group = f'user_{self.scope["user"].id}'
|
||||
|
||||
add_to_group = False
|
||||
if not self.groups or not user_group in self.groups or not self.channel_name in self.groups[user_group]:
|
||||
add_to_group = True
|
||||
|
||||
if add_to_group:
|
||||
if not self.groups:
|
||||
self.groups = {user_group: [self.channel_name]}
|
||||
elif not user_group in self.groups:
|
||||
self.groups[user_group].append(self.channel_name)
|
||||
else:
|
||||
self.groups.update({user_group: [self.channel_name]})
|
||||
|
||||
async_to_sync(self.channel_layer.group_add)(
|
||||
user_group,
|
||||
self.channel_name
|
||||
)
|
||||
|
||||
print(f'created group user_{self.scope["user"].id}')
|
||||
|
||||
if self.scope['user'].is_staff:
|
||||
async_to_sync(self.channel_layer.group_add)(
|
||||
f'support_managers',
|
||||
self.channel_name
|
||||
)
|
||||
print(f'add user {self.scope["user"].id} to group support_managers')
|
||||
|
||||
self.accept()
|
||||
|
||||
|
||||
|
||||
|
||||
def receive(self, text_data=None, bytes_data=None):
|
||||
if len(text_data) < 500:
|
||||
print(f'ws receive text_data = {text_data}')
|
||||
else:
|
||||
print(f'ws receive text_data size = {len(text_data)}')
|
||||
|
||||
import base64
|
||||
|
||||
from AuthApp.models import User
|
||||
from .models import Message, MsgGroup
|
||||
|
||||
|
||||
data = json.loads(text_data)
|
||||
|
||||
# if 'file' in data:
|
||||
# file_Dict = json.loads(data['file'])
|
||||
#
|
||||
# f = open(f'chat_file_storage/{file_Dict["file_name"]}', 'wb+')
|
||||
# head, content = file_Dict['file'].split(',')
|
||||
# content = base64.b64decode(content)
|
||||
# f.write(content)
|
||||
# f.close()
|
||||
|
||||
sender = data['sender']
|
||||
receiver = data['receiver']
|
||||
sender_obj = User.objects.get(id=sender)
|
||||
receiver_obj = User.objects.get(id=receiver)
|
||||
|
||||
|
||||
|
||||
from .funcs import send_msg, get_update_chat_Dict
|
||||
data.update({
|
||||
'cur_user': sender,
|
||||
'required_beep': False,
|
||||
})
|
||||
res = send_msg(data)
|
||||
if not res['msg'] or type(res['msg']) == str:
|
||||
data.update({'bad_manager': True})
|
||||
|
||||
ticket = None
|
||||
|
||||
if 'ticket_id' in data and data['ticket_id']:
|
||||
ticket = MsgGroup.objects.get(id=data['ticket_id'])
|
||||
# receiver_obj = User.objects.get(id=receiver)
|
||||
msgs = Message.objects.filter(
|
||||
receiver__id=sender, group__id=data['ticket_id']
|
||||
)
|
||||
msgs.update(status='seen')
|
||||
|
||||
if 'required_update_tickets_list_wo_managers' in res and res['required_update_tickets_list_wo_managers']:
|
||||
send_to_support_managers_list_tickets_wo_manager(ticket, required_beep=False)
|
||||
|
||||
Dict = get_update_chat_Dict(data)
|
||||
|
||||
|
||||
|
||||
group_name = f'user_{sender}'
|
||||
resDict = {
|
||||
'type': 'update_chat',
|
||||
'sender': sender,
|
||||
'receiver': receiver,
|
||||
'group_name': group_name,
|
||||
|
||||
}
|
||||
|
||||
if ticket:
|
||||
tickets_wo_manager_html = get_tickets_wo_manager_html(ticket, sender_obj, data)
|
||||
if tickets_wo_manager_html:
|
||||
resDict.update({'tickets_wo_manager_html': tickets_wo_manager_html})
|
||||
|
||||
tickets_w_manager_html = get_tickets_w_manager_html(ticket, sender_obj, data)
|
||||
if tickets_w_manager_html:
|
||||
resDict.update({'tickets_w_manager_html': tickets_w_manager_html})
|
||||
|
||||
|
||||
resDict.update(Dict)
|
||||
async_to_sync(self.channel_layer.group_send)(
|
||||
group_name,
|
||||
resDict
|
||||
)
|
||||
|
||||
group_name = f'user_{receiver}'
|
||||
# if group_name in self.channel_layer.groups.keys():
|
||||
data.update({
|
||||
'cur_user': receiver,
|
||||
'required_beep': True,
|
||||
})
|
||||
Dict = get_update_chat_Dict(data)
|
||||
|
||||
if 'support_chat_html' in Dict:
|
||||
msg_type = 'update_support_chat'
|
||||
else:
|
||||
msg_type = 'update_chat'
|
||||
|
||||
resDict = {
|
||||
'type': msg_type,
|
||||
'sender': receiver,
|
||||
'receiver': sender,
|
||||
'group_name': group_name,
|
||||
}
|
||||
|
||||
if ticket:
|
||||
tickets_wo_manager_html = get_tickets_wo_manager_html(ticket, receiver_obj, data)
|
||||
if tickets_wo_manager_html:
|
||||
resDict.update({'tickets_wo_manager_html': tickets_wo_manager_html})
|
||||
|
||||
tickets_w_manager_html = get_tickets_w_manager_html(ticket, receiver_obj, data)
|
||||
if tickets_w_manager_html:
|
||||
resDict.update({'tickets_w_manager_html': tickets_w_manager_html})
|
||||
|
||||
resDict.update(Dict)
|
||||
async_to_sync(self.channel_layer.group_send)(
|
||||
group_name,
|
||||
resDict
|
||||
)
|
||||
|
||||
|
||||
|
||||
def echo(self, data):
|
||||
print('ws echo')
|
||||
|
||||
self.send(text_data=json.dumps(data))
|
||||
|
||||
def update_support_chat(self, data):
|
||||
print(f'ws update_support_chat {data["group_name"]}')
|
||||
|
||||
self.send(text_data=json.dumps(data))
|
||||
|
||||
def update_chat(self, data):
|
||||
print(f'ws update_chat {data["group_name"]}')
|
||||
|
||||
self.send(text_data=json.dumps(data))
|
||||
|
||||
def ws_send_msg(self, data):
|
||||
print('ws ws_send_msg')
|
||||
|
||||
self.send(text_data=json.dumps(data))
|
||||
15
Flagpack/AD - Andorra.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67315)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 0H24V24H8V0Z" fill="#FFD018"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H8V24H0V0Z" fill="#2E42A5"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24 0H32V24H24V0Z" fill="#E31D1C"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.2991 19.4236C16.5201 19.3615 16.7102 18.9772 16.7102 18.9772C16.7102 18.9772 16.8545 19.09 17.0834 19.2048C17.3122 19.3195 17.5501 18.9772 17.5501 18.9772C17.5501 18.9772 17.8751 18.9772 18.0176 18.5937C18.1602 18.2103 17.7332 18.046 17.7332 18.046C17.7332 18.046 18.2323 17.5217 19.0749 17.5217C19.9176 17.5217 20.5734 17.5217 21.1232 17.2883C21.6731 17.0549 22.2073 16.6536 22.4072 15.8203C22.6071 14.9869 21.9744 14.4621 21.9744 14.4621C21.9744 14.4621 22.1145 14.5064 22.4072 14.267C22.6999 14.0276 22.2281 13.2572 21.9744 13.2572C21.7208 13.2572 21.6906 13.3998 21.6906 13.3998C21.6906 13.3998 21.5339 13.2638 21.3192 12.8014C21.1044 12.339 21.5339 11.933 21.5339 11.933C21.5339 11.933 21.7692 12.0679 21.9744 11.8454C22.1797 11.623 21.8678 11.2821 21.8678 11.2821C21.8678 11.2821 22.8559 10.9458 22.713 9.68177C22.5702 8.41774 21.2673 8.48963 21.2673 8.48963C21.2673 8.48963 21.3068 8.27946 21.1232 8.15968C20.9397 8.03991 20.8036 8.27946 20.8036 8.27946C20.6596 7.94606 21.082 7.48997 21.42 7.12497C21.5247 7.01186 21.6214 6.90751 21.6906 6.81826C21.983 6.44099 21.7994 5.9749 20.731 5.46617C19.6625 4.95744 19.0792 5.64116 18.9936 5.88344C18.908 6.12571 19.2395 6.27563 19.2395 6.27563C18.9691 6.51292 18.4461 6.60353 18.0897 6.43958C17.7332 6.27563 17.8912 5.77705 17.9649 5.64116C17.973 5.62629 17.9837 5.60827 17.9961 5.58746C18.0968 5.4181 18.3075 5.06401 18.0897 4.71967C17.8451 4.33302 17.4216 4.33302 17.0834 4.55258C16.7938 4.74051 16.3823 4.84015 16.2696 4.86497C16.157 4.84015 15.7455 4.74051 15.4559 4.55258C15.1177 4.33302 14.6942 4.33302 14.4496 4.71967C14.2318 5.06401 14.4424 5.4181 14.5432 5.58746C14.5556 5.60827 14.5663 5.62629 14.5744 5.64116C14.6481 5.77705 14.8061 6.27563 14.4496 6.43958C14.0931 6.60353 13.5702 6.51292 13.2998 6.27563C13.2998 6.27563 13.6313 6.12571 13.5457 5.88344C13.4601 5.64116 12.8768 4.95744 11.8083 5.46617C10.7399 5.9749 10.5563 6.44099 10.8487 6.81826C10.9179 6.90751 11.0145 7.01186 11.1193 7.12497C11.4573 7.48997 11.8797 7.94606 11.7356 8.27946C11.7356 8.27946 11.5996 8.03991 11.4161 8.15968C11.2325 8.27946 11.272 8.48963 11.272 8.48963C11.272 8.48963 9.9691 8.41774 9.82625 9.68177C9.68341 10.9458 10.6715 11.2821 10.6715 11.2821C10.6715 11.2821 10.3596 11.623 10.5648 11.8454C10.7701 12.0679 11.0054 11.933 11.0054 11.933C11.0054 11.933 11.4348 12.339 11.2201 12.8014C11.0054 13.2638 10.8487 13.3998 10.8487 13.3998C10.8487 13.3998 10.8185 13.2572 10.5648 13.2572C10.3112 13.2572 9.83938 14.0276 10.1321 14.267C10.4247 14.5064 10.5648 14.4621 10.5648 14.4621C10.5648 14.4621 9.93218 14.9869 10.1321 15.8203C10.332 16.6536 10.8662 17.0549 11.4161 17.2883C11.9659 17.5217 12.6217 17.5217 13.4644 17.5217C14.307 17.5217 14.8061 18.046 14.8061 18.046C14.8061 18.046 14.3791 18.2103 14.5217 18.5937C14.6642 18.9772 14.9891 18.9772 14.9891 18.9772C14.9891 18.9772 15.2271 19.3195 15.4559 19.2048C15.6847 19.09 15.8291 18.9772 15.8291 18.9772C15.8291 18.9772 16.0192 19.3615 16.2402 19.4236V19.4321C16.25 19.4321 16.2599 19.4313 16.2696 19.4299C16.2794 19.4313 16.2892 19.4321 16.2991 19.4321V19.4236Z" fill="#BF9937"/>
|
||||
<path d="M12.1102 15.9978L12.2153 15.7437C13.423 16.2614 14.7973 16.5206 16.3392 16.5206C17.8802 16.5206 19.2176 16.2617 20.3521 15.7449L20.4627 15.9966C19.2913 16.5301 17.9165 16.7963 16.3392 16.7963C14.7626 16.7963 13.3526 16.5304 12.1102 15.9978Z" fill="#805440"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67315">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
14
Flagpack/AE - United Arab Emirates.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67414)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H32V24H0V0Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V8H32V0H0Z" fill="#5EAA22"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 16V24H32V16H0Z" fill="#272727"/>
|
||||
<rect width="10" height="24" fill="#E31D1C"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67414">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 589 B |
21
Flagpack/AF - Afghanistan.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67316)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M22 0H32V24H22V0Z" fill="#67BD38"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0H22V24H10V0Z" fill="#D51700"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H10V24H0V0Z" fill="#272727"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.4877 7.97687L12.8337 8.19402C12.2571 9.16451 11.8698 10.0538 11.6695 10.8622C11.7542 10.9404 11.8073 11.0088 11.8616 11.1028C12.018 11.3738 12.1496 11.6368 12.2562 11.892L11.7964 12.0878C11.7242 11.915 11.6391 11.7374 11.5412 11.5549C11.4943 11.9492 11.4975 12.3219 11.5505 12.6728C11.6346 12.6814 11.7052 12.6991 11.7869 12.7288C12.0809 12.8358 12.3508 12.9528 12.5965 13.0797L12.3701 13.5253C12.1606 13.417 11.9305 13.316 11.6798 13.2222L11.6848 13.2374C11.8985 13.8833 12.2256 14.4776 12.667 15.0205C12.736 15.0056 12.8002 15 12.8754 15C13.1882 15 13.4818 15.0176 13.7562 15.0528L13.6958 15.549C13.509 15.525 13.3115 15.5098 13.1033 15.5034C13.4999 15.9003 13.9655 16.2663 14.5005 16.6016L14.2895 16.9574C13.626 16.5416 13.0614 16.078 12.5962 15.5669C12.5356 15.5906 12.4618 15.6219 12.3678 15.663L12.3325 15.6783C12.7385 16.104 13.2185 16.4955 13.773 16.853L13.5576 17.206C12.9168 16.7929 12.3683 16.334 11.9127 15.8296C11.6952 15.8854 11.5118 15.8843 11.2973 15.8213L11.4308 15.3405C11.463 15.35 11.4946 15.357 11.5263 15.3615C11.1032 14.7985 10.7844 14.1845 10.5707 13.5199C10.5329 13.4025 10.5006 13.283 10.4736 13.1614C10.3079 13.1298 10.1681 13.0676 10.0231 12.9609L10.313 12.5547C10.338 12.5731 10.3632 12.5894 10.3892 12.6036C10.3373 11.9912 10.4088 11.33 10.6029 10.6202C10.4814 10.4803 10.407 10.3353 10.3612 10.1468L10.786 10.0371C11.0149 9.38877 11.3387 8.70275 11.7566 7.9791L12.1053 8.19178C11.6495 8.98088 11.3119 9.71805 11.0913 10.4034C11.1531 10.4595 11.2287 10.5196 11.3214 10.5883C11.5429 9.78224 11.9323 8.9117 12.4877 7.97687ZM11.1981 11.1185C11.1532 11.0835 11.0992 11.0431 11.0336 10.9947C11.0021 10.9715 10.972 10.9489 10.9431 10.9268C10.7886 11.5626 10.7431 12.1502 10.8055 12.6895C10.88 12.6901 10.9643 12.6869 11.0617 12.6808L11.141 12.6759C11.0748 12.1893 11.0941 11.6702 11.1981 11.1185ZM11.0832 13.1782C11.1426 13.1745 11.1945 13.1715 11.2403 13.1694C11.2586 13.2374 11.2789 13.3048 11.3009 13.3715C11.5154 14.0198 11.8354 14.6183 12.2601 15.167L12.1774 15.2029C12.1065 15.2339 12.0426 15.2604 11.9842 15.2824C11.5194 14.7081 11.1769 14.0772 10.9556 13.3891C10.9342 13.3226 10.9147 13.2552 10.8972 13.187C10.9556 13.1854 11.0174 13.1824 11.0832 13.1782Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.499 7.97687L19.153 8.19402C19.7296 9.16451 20.1169 10.0538 20.3172 10.8622C20.2325 10.9404 20.1794 11.0088 20.1251 11.1028C19.9687 11.3738 19.8371 11.6368 19.7305 11.892L20.1903 12.0878C20.2625 11.915 20.3476 11.7374 20.4455 11.5549C20.4924 11.9492 20.4892 12.3219 20.4362 12.6728C20.3521 12.6814 20.2815 12.6991 20.1998 12.7288C19.9058 12.8358 19.6359 12.9528 19.3902 13.0797L19.6166 13.5253C19.8261 13.417 20.0562 13.316 20.3069 13.2222L20.3019 13.2374C20.0882 13.8833 19.7611 14.4776 19.3197 15.0205C19.2507 15.0056 19.1865 15 19.1113 15C18.7985 15 18.5049 15.0176 18.2305 15.0528L18.2909 15.549C18.4776 15.525 18.6752 15.5098 18.8834 15.5034C18.4868 15.9003 18.0212 16.2663 17.4861 16.6016L17.6972 16.9574C18.3607 16.5416 18.9253 16.078 19.3905 15.5669C19.4511 15.5906 19.5249 15.6219 19.6189 15.663L19.6542 15.6783C19.2482 16.104 18.7682 16.4955 18.2137 16.853L18.4291 17.206C19.0699 16.7929 19.6184 16.334 20.074 15.8296C20.2915 15.8854 20.4749 15.8843 20.6894 15.8213L20.5559 15.3405C20.5236 15.35 20.4921 15.357 20.4604 15.3615C20.8835 14.7985 21.2023 14.1845 21.416 13.5199C21.4538 13.4025 21.4861 13.283 21.5131 13.1614C21.6788 13.1298 21.8185 13.0676 21.9636 12.9609L21.6737 12.5547C21.6487 12.5731 21.6235 12.5894 21.5975 12.6036C21.6494 11.9912 21.5779 11.33 21.3838 10.6202C21.5053 10.4803 21.5797 10.3353 21.6254 10.1468L21.2007 10.0371C20.9718 9.38877 20.648 8.70275 20.23 7.9791L19.8814 8.19178C20.3372 8.98088 20.6748 9.71805 20.8954 10.4034C20.8336 10.4595 20.758 10.5196 20.6653 10.5883C20.4438 9.78224 20.0544 8.9117 19.499 7.97687ZM20.7886 11.1185C20.8335 11.0835 20.8875 11.0431 20.9531 10.9947C20.9846 10.9715 21.0147 10.9489 21.0436 10.9268C21.198 11.5626 21.2436 12.1502 21.1811 12.6895C21.1067 12.6901 21.0224 12.6869 20.925 12.6808L20.8457 12.6759C20.9118 12.1893 20.8926 11.6702 20.7886 11.1185ZM20.9035 13.1782C20.8441 13.1745 20.7922 13.1715 20.7464 13.1694C20.728 13.2374 20.7078 13.3048 20.6858 13.3715C20.4712 14.0198 20.1513 14.6183 19.7266 15.167L19.8093 15.2029C19.8802 15.2339 19.9441 15.2604 20.0025 15.2824C20.4673 14.7081 20.8098 14.0772 21.0311 13.3891C21.0525 13.3226 21.072 13.2552 21.0895 13.187C21.0311 13.1854 20.9693 13.1824 20.9035 13.1782Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M14.253 16.2641L13.1367 17.3687C13.1367 17.3687 14.0994 17.9582 15.9137 17.9582C17.728 17.9582 18.7844 17.3687 18.7844 17.3687L17.9358 16.2641C17.9358 16.2641 16.7795 16.7122 15.9606 16.7122C15.1416 16.7122 14.253 16.2641 14.253 16.2641Z" fill="#F7FCFF"/>
|
||||
<path d="M14.2764 16.969C14.2764 16.969 15.2215 17.192 15.946 17.192C16.6705 17.192 17.6156 16.969 17.6156 16.969" stroke="#C00001" stroke-width="0.5"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.8919 9.45835C16.8919 9.89558 16.4926 10.25 16 10.25C15.5074 10.25 15.1081 9.89558 15.1081 9.45835C15.1081 9.02113 15.5074 8.66669 16 8.66669C16.4926 8.66669 16.8919 9.02113 16.8919 9.45835ZM16 10.25H15.0398L14.7753 10.4527H14.7238V8.86802H14.2373V10.4527H13.8108V10.723H14.2373V13.5806C14.1796 14.0371 14.0463 14.2952 13.8594 14.3849L12.8662 14.8614H18.8436L18.414 14.4321C18.0367 14.0551 17.8564 13.7652 17.8564 13.5965V10.723H18.027V10.4527H17.8564V8.86802H17.3699V10.4527H17.2127L16.8992 10.25H16ZM14.7238 10.723H17.3699V13.5965C17.3699 13.8282 17.4795 14.0797 17.6935 14.3614H14.5231C14.621 14.1643 14.6865 13.9186 14.7238 13.612V10.723ZM16.0811 12H15.027V14.0834H17.1351L16.0811 12Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.1696 6.15619L17.1714 6.15922L17.171 6.15807L17.1696 6.15619ZM18.7108 6.69781L18.7117 6.7071L18.7111 6.70034L18.7108 6.69781ZM19.0101 6.7347L19.0116 6.73014L19.0109 6.73238L19.0101 6.7347ZM17.7983 7.34837L17.8039 7.34832L17.8013 7.34867L17.7983 7.34837Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.9929 10.6681L12.7936 10.8115L12.9991 11.113C12.8231 11.1762 12.5401 11.3361 12.5401 11.712C12.5401 12.2575 12.3485 12.7044 12.3485 12.7044L12.9207 13.0719C12.9207 13.0719 12.9804 12.4357 13.1362 12.0738C13.2143 11.8924 13.3358 11.802 13.4377 11.7569L14.3272 13.0625L14.5264 12.9191L12.9929 10.6681Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.0712 10.6681L19.2705 10.8115L19.065 11.113C19.241 11.1762 19.524 11.3361 19.524 11.712C19.524 12.2575 19.7156 12.7044 19.7156 12.7044L19.1434 13.0719C19.1434 13.0719 19.0837 12.4357 18.9279 12.0738C18.8498 11.8924 18.7282 11.802 18.6264 11.7569L17.7369 13.0625L17.5377 12.9191L19.0712 10.6681Z" fill="#F7FCFF"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67316">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 7.2 KiB |
15
Flagpack/AG - Antigua and Barbuda.svg
Normal file
@@ -0,0 +1,15 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67431)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H32V24H0V0Z" fill="#1B1B1B"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16 14L13.9973 17.7994L14.0475 13.6039L10.3886 16.2547L12.4818 12.494L7.89128 13.4711L11.6128 10.8901L7 10L11.6128 9.10992L7.89128 6.52893L12.4818 7.50604L10.3886 3.74535L14.0475 6.39612L13.9973 2.20058L16 6L18.0027 2.20058L17.9525 6.39612L21.6114 3.74535L19.5182 7.50604L24.1087 6.52893L20.3872 9.10992L25 10L20.3872 10.8901L24.1087 13.4711L19.5182 12.494L21.6114 16.2547L17.9525 13.6039L18.0027 17.7994L16 14Z" fill="#F9D313"/>
|
||||
<rect x="6" y="14" width="20" height="10" fill="#F1F9FF"/>
|
||||
<rect x="2" y="10" width="28" height="4" fill="#4A80E8"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 6L16 24L32 6V24H0V6Z" fill="#E31D1C"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67431">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
37
Flagpack/AI - Anguilla.svg
Normal file
@@ -0,0 +1,37 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67318)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#2E42A5"/>
|
||||
<mask id="mask0_270_67318" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_270_67318)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M20.943 11.8562L20.9462 12.2066C20.9731 15.1566 21.0819 17.0865 21.2756 18.011C21.4364 18.7787 22.0208 20.0518 22.7082 21.0726C23.5724 22.3561 24.4945 23.1368 25.377 23.1368C26.2627 23.1368 27.1855 22.4108 28.046 21.2011C28.7524 20.2082 29.3573 18.9486 29.5981 18.0204C29.8963 16.8714 30.0352 14.9356 30.0183 12.2035L30.0162 11.8556L29.6983 12.0202C29.0576 12.3518 28.3678 12.5174 27.6257 12.5174C26.8922 12.5174 26.1843 12.2982 25.4977 11.8563L25.3799 11.7805L25.2605 11.8537C24.5359 12.2979 23.855 12.5174 23.2168 12.5174C22.5705 12.5174 21.9205 12.3521 21.2646 12.0194L20.943 11.8562ZM21.4886 17.9695C21.2986 17.0627 21.1902 15.1411 21.1635 12.2048C21.8495 12.5529 22.534 12.7269 23.2168 12.7269C23.8997 12.7269 24.6197 12.4948 25.377 12.0306C26.0984 12.4948 26.8479 12.7269 27.6257 12.7269C28.4035 12.7269 29.1286 12.5529 29.801 12.2048C29.8178 14.9205 29.6799 16.842 29.3874 17.9695C28.9485 19.6607 27.1083 22.9273 25.377 22.9273C23.6458 22.9273 21.7735 19.3297 21.4886 17.9695Z" fill="#FBE625"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.1635 12.2048C21.1635 12.2048 21.2036 16.6093 21.4886 17.9695C21.7735 19.3297 23.6458 22.9273 25.377 22.9273C27.1083 22.9273 28.9485 19.6607 29.3873 17.9695C29.8262 16.2783 29.801 12.2048 29.801 12.2048C29.801 12.2048 28.7923 12.7269 27.6257 12.7269C26.459 12.7269 25.377 12.0306 25.377 12.0306C25.377 12.0306 24.2411 12.7269 23.2168 12.7269C22.1926 12.7269 21.1635 12.2048 21.1635 12.2048Z" fill="#F5FBFF"/>
|
||||
<mask id="mask1_270_67318" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="21" y="12" width="9" height="11">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.1635 12.2048C21.1635 12.2048 21.2036 16.6093 21.4886 17.9695C21.7735 19.3297 23.6458 22.9273 25.377 22.9273C27.1083 22.9273 28.9485 19.6607 29.3873 17.9695C29.8262 16.2783 29.801 12.2048 29.801 12.2048C29.801 12.2048 28.7923 12.7269 27.6257 12.7269C26.459 12.7269 25.377 12.0306 25.377 12.0306C25.377 12.0306 24.2411 12.7269 23.2168 12.7269C22.1926 12.7269 21.1635 12.2048 21.1635 12.2048Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask1_270_67318)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M24.6398 15.4882C24.6398 15.7759 24.2288 16.6134 23.9906 16.8277C23.8144 16.9863 23.8688 16.6178 23.909 16.3458L23.909 16.3458C23.9231 16.2503 23.9354 16.1668 23.9354 16.122L23.9355 16.1024C23.9355 16.0459 23.9355 16.0151 23.9299 15.9854C23.9231 15.9499 23.9082 15.9158 23.8753 15.8411L23.8753 15.8411C23.863 15.813 23.8482 15.7793 23.8303 15.7375C23.4488 14.9237 23.7187 14.5339 23.8386 14.3608C23.857 14.3343 23.8718 14.3129 23.8802 14.2958C23.8983 14.2592 23.9324 14.2263 23.9705 14.1897C24.0662 14.0973 24.187 13.9808 24.1415 13.7195C24.0778 13.3543 24.4639 12.8859 24.4639 12.8859C24.4639 12.8859 24.3702 13.7943 24.814 13.5718C25.1024 13.4271 25.6251 13.6347 26.09 13.8193C26.3403 13.9187 26.5739 14.0114 26.7452 14.0389C27.091 14.0945 27.2804 13.9493 27.3977 13.8594C27.4463 13.8221 27.4825 13.7943 27.5123 13.7943C27.6139 13.7943 27.7339 13.8147 27.7339 13.8889C27.7339 13.9632 27.2785 14.1805 27.0495 14.2483C26.8769 14.2993 27.054 14.4401 27.2024 14.5581C27.2509 14.5967 27.2963 14.6328 27.3254 14.6625C27.4437 14.7832 27.552 15.4416 27.2785 15.0274C27.0049 14.6133 26.4227 14.3364 25.9278 14.2958C25.4329 14.2553 24.8088 14.6309 24.7339 15.0274C24.659 15.4239 25.3193 15.3513 25.3193 15.3513C25.3193 15.3513 25.2711 15.4131 25.0581 15.4882C24.8452 15.5634 24.6398 15.4882 24.6398 15.4882ZM24.1419 15.4571C24.1419 15.515 24.0933 15.5619 24.0333 15.5619C23.9733 15.5619 23.9247 15.515 23.9247 15.4571C23.9247 15.3992 23.9733 15.3523 24.0333 15.3523C24.0933 15.3523 24.1419 15.3992 24.1419 15.4571ZM26.582 17.401C26.2791 17.2966 25.3474 17.3431 25.0957 17.4826C25.0957 17.4826 25.0619 17.2666 24.8929 17.1167C24.724 16.9669 24.6465 16.9547 24.6465 16.9547C24.6465 16.9547 25.0302 17.497 24.6471 17.6237C24.264 17.7504 23.6329 17.3867 23.4284 16.9342C23.224 16.4817 23.1839 15.8382 23.4135 15.3982C23.6431 14.9582 23.1198 15.3721 23.0715 15.534C23.0597 15.5739 23.0501 15.6312 23.0399 15.6923C23.0086 15.8792 22.9713 16.1024 22.843 15.9762C22.6727 15.8088 22.2619 15.5158 22.1969 15.5518C22.132 15.5878 22.1723 15.7026 22.2216 15.7915C22.2361 15.8176 22.2779 15.8358 22.3341 15.8602C22.4695 15.9192 22.6884 16.0144 22.8075 16.3439C22.8664 16.507 22.8985 16.7562 22.933 17.0234C22.9969 17.5195 23.0688 18.0772 23.3351 18.2594C23.7449 18.5396 22.905 18.8981 22.905 18.8981C22.905 18.8981 23.5018 19.0087 23.7904 18.776C23.9968 18.6094 24.1573 18.6586 24.2845 18.6976L24.2845 18.6976C24.335 18.7131 24.3803 18.727 24.4211 18.725C24.4401 18.7241 24.4661 18.7267 24.4981 18.7299C24.7076 18.7509 25.1794 18.798 25.7062 18.0698C25.7341 18.0339 25.7564 18.0046 25.775 17.9802L25.775 17.9802C25.8245 17.9152 25.847 17.8856 25.8748 17.8625C25.898 17.8431 25.925 17.8282 25.9744 17.8009L25.9916 17.7914C26.0307 17.7697 26.1098 17.74 26.2001 17.706L26.2001 17.706L26.2001 17.706C26.4575 17.6093 26.8062 17.4783 26.582 17.401ZM25.3287 17.8816C25.3793 17.8535 25.4439 17.8734 25.473 17.9258C25.5021 17.9783 25.4847 18.0436 25.4341 18.0716C25.3835 18.0997 25.3189 18.0799 25.2898 18.0274C25.2607 17.9749 25.2781 17.9096 25.3287 17.8816ZM25.8417 14.8742C25.9192 15.1851 26.4656 15.9412 26.7196 16.0763C26.7196 16.0763 26.5569 16.2223 26.5233 16.4456C26.4896 16.6689 26.5216 16.7406 26.5216 16.7406C26.5216 16.7406 26.7674 16.1234 27.0823 16.3757C27.3972 16.628 27.436 17.3553 27.1678 17.7733C26.8997 18.1912 26.3818 18.5753 25.8877 18.6224C25.3936 18.6695 26.0258 18.8829 26.1879 18.8352C26.2278 18.8234 26.281 18.8003 26.3379 18.7755C26.5116 18.6999 26.7191 18.6097 26.6832 18.786C26.6355 19.02 26.6136 19.5242 26.6791 19.559C26.7447 19.5939 26.819 19.4975 26.8667 19.4077C26.8807 19.3814 26.8732 19.3364 26.8631 19.276C26.8388 19.1303 26.7994 18.8949 27.0109 18.6156C27.1156 18.4773 27.3071 18.3146 27.5124 18.1403C27.8937 17.8164 28.3223 17.4524 28.33 17.1298C28.3418 16.6335 29.0999 17.1427 29.0999 17.1427C29.0999 17.1427 28.8676 16.5819 28.5153 16.4666C28.2632 16.3842 28.217 16.2228 28.1804 16.0949C28.1659 16.0441 28.1529 15.9986 28.1291 15.9654C28.1179 15.9499 28.106 15.9268 28.0912 15.8981C27.9947 15.711 27.7773 15.2897 26.8796 15.2445C26.8343 15.2406 26.7975 15.2379 26.767 15.2356C26.6856 15.2295 26.6485 15.2267 26.6139 15.216C26.585 15.2071 26.5579 15.1926 26.508 15.166C26.5025 15.1631 26.4968 15.16 26.4907 15.1568C26.4512 15.1358 26.3832 15.0856 26.3055 15.0284L26.3055 15.0284C26.0842 14.8652 25.7845 14.6441 25.8417 14.8742ZM26.9619 15.7033C26.9108 15.6762 26.8922 15.6112 26.9204 15.5582C26.9485 15.5052 27.0128 15.4843 27.0639 15.5115C27.1149 15.5386 27.1335 15.6036 27.1054 15.6566C27.0772 15.7096 27.0129 15.7305 26.9619 15.7033Z" fill="#FF9800"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M21.4259 19.7524H28.7049V23.2095H21.4259V19.7524Z" fill="#36C6FF"/>
|
||||
</g>
|
||||
<g clip-path="url(#clip1_270_67318)">
|
||||
<rect width="18" height="14" fill="#2E42A5"/>
|
||||
<path d="M-2.00427 12.9998L1.95668 14.737L18.0899 1.88872L20.1792 -0.69281L15.9435 -1.27343L9.36317 4.26326L4.06667 7.99369L-2.00427 12.9998Z" fill="#F7FCFF"/>
|
||||
<path d="M-1.46204 14.2169L0.555885 15.225L19.4289 -0.932678H16.5955L-1.46204 14.2169Z" fill="#F50100"/>
|
||||
<path d="M20.0043 12.9998L16.0433 14.737L-0.0898743 1.88872L-2.17919 -0.69281L2.05648 -1.27343L8.63683 4.26326L13.9333 7.99369L20.0043 12.9998Z" fill="#F7FCFF"/>
|
||||
<path d="M19.8691 13.8733L17.8512 14.8815L9.81489 7.96351L7.43229 7.19059L-2.3802 -0.683947H0.453194L10.2602 7.00368L12.8651 7.9305L19.8691 13.8733Z" fill="#F50100"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.98451 0H7.98451V6H0V8H7.98451V14H9.98451V8H18V6H9.98451V0Z" fill="#F50100"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.48451 0H11.4845V4.5H18V9.5H11.4845L11.5 14H6.5L6.48451 9.5H0V4.5H6.48451V0ZM7.98451 6H0V8H7.98451V14H9.98451V8H18V6H9.98451V0H7.98451V6Z" fill="#F7FCFF"/>
|
||||
</g>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67318">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_270_67318">
|
||||
<rect width="18" height="14" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 8.4 KiB |
12
Flagpack/AL - Albania.svg
Normal file
|
After Width: | Height: | Size: 12 KiB |
18
Flagpack/AM - Armenia.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67563)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#2E42A5"/>
|
||||
<mask id="mask0_270_67563" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_270_67563)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V8H32V0H0Z" fill="#E31D1C"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 16V24H32V16H0Z" fill="#FECA00"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67563">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 788 B |
43
Flagpack/AO - Angola.svg
Normal file
@@ -0,0 +1,43 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67342)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#1D1D1D"/>
|
||||
<mask id="mask0_270_67342" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_270_67342)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V12H32V0H0Z" fill="#F50100"/>
|
||||
<path d="M9.93302 5.89667L10.3598 4.49182C13.56 5.46852 15.4893 7.18076 16.032 9.62351C16.3921 11.244 16.2284 12.6925 15.6462 13.9824C15.2313 14.9014 14.7044 15.5874 13.9468 16.3597C12.6834 17.6476 10.6054 18.4694 8.72927 18.1209C7.42156 17.8781 6.2169 17.4194 5.11975 16.7455L5.88521 15.4934C6.82784 16.0723 7.86335 16.4666 8.99619 16.677C10.3509 16.9286 11.9571 16.2934 12.9021 15.3302C13.5494 14.6702 13.9815 14.1077 14.3113 13.377C14.7648 12.3724 14.8919 11.2477 14.6019 9.94272C14.1879 8.07902 12.6701 6.73203 9.93302 5.89667Z" fill="#FFEA42"/>
|
||||
<g filter="url(#filter0_d_270_67342)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.8461 9.46429L8.8966 10.1216L9.23157 9.02009L8.30978 8.3248L9.4663 8.30135L9.8461 7.21429L10.2259 8.30135L11.3824 8.3248L10.4606 9.02009L10.7956 10.1216L9.8461 9.46429Z" fill="#FCFF01"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.8461 9.46429L8.8966 10.1216L9.23157 9.02009L8.30978 8.3248L9.4663 8.30135L9.8461 7.21429L10.2259 8.30135L11.3824 8.3248L10.4606 9.02009L10.7956 10.1216L9.8461 9.46429Z" fill="#FFEA42"/>
|
||||
</g>
|
||||
<g filter="url(#filter1_d_270_67342)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.61405 9.85974C7.61405 9.85974 5.9302 12.3283 7.25611 13.2308C8.58203 14.1332 15.5933 17.9375 15.5933 17.9375C15.5933 17.9375 17.042 17.9291 17.2481 18.2373C17.4542 18.5455 17.883 18.908 18.0637 18.908C18.2445 18.908 18.7114 18.5815 18.7114 18.5815C18.7114 18.5815 19.0753 18.2837 18.7114 17.9375C18.3474 17.5912 16.4239 16.4057 16.4239 16.4057C16.4239 16.4057 8.9159 11.6142 8.27747 11.1398C7.63904 10.6654 7.61405 9.85974 7.61405 9.85974Z" fill="#FCFF01"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M7.61405 9.85974C7.61405 9.85974 5.9302 12.3283 7.25611 13.2308C8.58203 14.1332 15.5933 17.9375 15.5933 17.9375C15.5933 17.9375 17.042 17.9291 17.2481 18.2373C17.4542 18.5455 17.883 18.908 18.0637 18.908C18.2445 18.908 18.7114 18.5815 18.7114 18.5815C18.7114 18.5815 19.0753 18.2837 18.7114 17.9375C18.3474 17.5912 16.4239 16.4057 16.4239 16.4057C16.4239 16.4057 8.9159 11.6142 8.27747 11.1398C7.63904 10.6654 7.61405 9.85974 7.61405 9.85974Z" fill="#FFEA42"/>
|
||||
</g>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.7118 4.07422L11.322 4.95707L12.6002 5.51046L12.9627 4.62738L11.7118 4.07422ZM14.3246 5.48414L13.6208 6.14449L14.5812 7.15334L15.2599 6.4821L14.3246 5.48414ZM15.2861 8.13652L16.186 7.78789L16.6793 9.06356L15.7986 9.43169L15.2861 8.13652ZM17.039 10.6362L16.0744 10.6055L16.0402 11.9979L16.9947 12.0032L17.039 10.6362ZM15.8471 13.1941L16.73 13.5839L16.1768 14.8348L15.2938 14.4723L15.8471 13.1941ZM15.3236 16.202L14.6633 15.4983L13.6544 16.4587L14.3257 17.1374L15.3236 16.202ZM12.6765 17.1671L13.0252 18.067L11.7495 18.5604L11.3814 17.6797L12.6765 17.1671ZM10.1832 18.9214L10.2139 17.9568L8.82142 17.9225L8.81615 18.8771L10.1832 18.9214ZM7.63156 17.7283L7.24175 18.6111L5.99084 18.058L6.35331 17.1749L7.63156 17.7283Z" fill="#FFEA42"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<filter id="filter0_d_270_67342" x="8.30981" y="7.21429" width="3.07263" height="2.90735" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_270_67342"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_270_67342" result="shape"/>
|
||||
</filter>
|
||||
<filter id="filter1_d_270_67342" x="6.74719" y="9.85974" width="12.1259" height="9.04822" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB">
|
||||
<feFlood flood-opacity="0" result="BackgroundImageFix"/>
|
||||
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0" result="hardAlpha"/>
|
||||
<feOffset/>
|
||||
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.2 0"/>
|
||||
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_270_67342"/>
|
||||
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_270_67342" result="shape"/>
|
||||
</filter>
|
||||
<clipPath id="clip0_270_67342">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
17
Flagpack/AQ - Antarctica.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67403)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#5196ED"/>
|
||||
<mask id="mask0_270_67403" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_270_67403)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.71741 7.89376C8.71741 7.89376 10.7815 9.02624 11.0686 9.33948C11.3555 9.65278 11.8155 10.8032 12.5328 10.1526C13.2502 9.50226 13.9675 10.0079 13.9675 8.9717C13.9675 7.93558 15.0385 5.53204 16.5926 6.13434C18.1469 6.73658 19.4247 6.35982 19.7832 6.6006C20.142 6.84154 21.0028 8.03802 21.6722 8.03802C22.3419 8.03802 22.6764 8.7367 22.7244 9.74876C22.772 10.7609 22.5093 10.8576 23.1308 10.9782C23.7526 11.0986 23.9916 11.5566 23.681 12.1589C23.3701 12.7613 23.3938 12.4962 23.4417 13.1228C23.4897 13.7493 22.8202 17.444 20.7634 17.8058C18.7069 18.1671 16.7576 17.9745 17.2839 17.2754C17.8099 16.5768 18.5381 15.7727 17.4623 15.604C16.386 15.4351 15.7078 15.2915 14.6317 15.5806C13.5555 15.8698 12.4317 16.3274 11.7619 15.4602C11.0924 14.5926 11.2361 13.9661 10.6861 13.5564C10.1362 13.1468 9.46638 13.2191 10.0166 12.4239C10.5663 11.6287 11.0206 11.9256 10.5663 11.2991C10.1123 10.6728 8.39739 10.3998 8.39739 9.77352C8.39739 9.14674 7.30659 7.77324 8.71741 7.89376Z" fill="#F5F8FB"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67403">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
27
Flagpack/AR - Argentina.svg
Normal file
|
After Width: | Height: | Size: 163 KiB |
19
Flagpack/AS - American Samoa.svg
Normal file
|
After Width: | Height: | Size: 21 KiB |
18
Flagpack/AT - Austria.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67484)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#F7FCFF"/>
|
||||
<mask id="mask0_270_67484" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_270_67484)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V8H32V0H0Z" fill="#C51918"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 16V24H32V16H0Z" fill="#C51918"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67484">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 788 B |
34
Flagpack/AU - Australia.svg
Normal file
@@ -0,0 +1,34 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67322)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#2E42A5"/>
|
||||
<g clip-path="url(#clip1_270_67322)">
|
||||
<rect width="18" height="14" fill="#2E42A5"/>
|
||||
<path d="M-2.00427 12.9998L1.95668 14.737L18.0899 1.88872L20.1792 -0.69281L15.9435 -1.27343L9.36317 4.26326L4.06667 7.99369L-2.00427 12.9998Z" fill="#F7FCFF"/>
|
||||
<path d="M-1.46204 14.2169L0.555885 15.225L19.4289 -0.932678H16.5955L-1.46204 14.2169Z" fill="#F50100"/>
|
||||
<path d="M20.0043 12.9998L16.0433 14.737L-0.0898743 1.88872L-2.17919 -0.69281L2.05648 -1.27343L8.63683 4.26326L13.9333 7.99369L20.0043 12.9998Z" fill="#F7FCFF"/>
|
||||
<path d="M19.8691 13.8733L17.8512 14.8815L9.81489 7.96351L7.43229 7.19059L-2.3802 -0.683947H0.453194L10.2602 7.00368L12.8651 7.9305L19.8691 13.8733Z" fill="#F50100"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.98451 0H7.98451V6H0V8H7.98451V14H9.98451V8H18V6H9.98451V0Z" fill="#F50100"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.48451 0H11.4845V4.5H18V9.5H11.4845L11.5 14H6.5L6.48451 9.5H0V4.5H6.48451V0ZM7.98451 6H0V8H7.98451V14H9.98451V8H18V6H9.98451V0H7.98451V6Z" fill="#F7FCFF"/>
|
||||
</g>
|
||||
<g clip-path="url(#clip2_270_67322)">
|
||||
<path d="M9 20.5L7.69835 21.7029L7.82725 19.9352L6.07522 19.6676L7.53761 18.6662L6.65451 17.1295L8.34917 17.6485L9 16L9.65083 17.6485L11.3455 17.1295L10.4624 18.6662L11.9248 19.6676L10.1727 19.9352L10.3017 21.7029L9 20.5Z" fill="#F7FCFF"/>
|
||||
<path d="M22 13L21.1322 13.8019L21.2182 12.6235L20.0501 12.445L21.0251 11.7775L20.4363 10.753L21.5661 11.099L22 10L22.4339 11.099L23.5637 10.753L22.9749 11.7775L23.9499 12.445L22.7818 12.6235L22.8678 13.8019L22 13Z" fill="#F7FCFF"/>
|
||||
<path d="M24 5L23.1322 5.80194L23.2182 4.62349L22.0501 4.44504L23.0251 3.77748L22.4363 2.75302L23.5661 3.09903L24 2L24.4339 3.09903L25.5637 2.75302L24.9749 3.77748L25.9499 4.44504L24.7818 4.62349L24.8678 5.80194L24 5Z" fill="#F7FCFF"/>
|
||||
<path d="M29 9L28.1322 9.80194L28.2182 8.62349L27.0501 8.44504L28.0251 7.77748L27.4363 6.75302L28.5661 7.09903L29 6L29.4339 7.09903L30.5637 6.75302L29.9749 7.77748L30.9499 8.44504L29.7818 8.62349L29.8678 9.80194L29 9Z" fill="#F7FCFF"/>
|
||||
<path d="M25 22L24.1322 22.8019L24.2182 21.6235L23.0501 21.445L24.0251 20.7775L23.4363 19.753L24.5661 20.099L25 19L25.4339 20.099L26.5637 19.753L25.9749 20.7775L26.9499 21.445L25.7818 21.6235L25.8678 22.8019L25 22Z" fill="#F7FCFF"/>
|
||||
<path d="M28.5 14.25L27.6183 14.7135L27.7867 13.7318L27.0734 13.0365L28.0592 12.8932L28.5 12L28.9408 12.8932L29.9266 13.0365L29.2133 13.7318L29.3817 14.7135L28.5 14.25Z" fill="#F7FCFF"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67322">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip1_270_67322">
|
||||
<rect width="18" height="14" fill="white"/>
|
||||
</clipPath>
|
||||
<clipPath id="clip2_270_67322">
|
||||
<rect width="25" height="21" fill="white" transform="translate(6 2)"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
18
Flagpack/AW - Aruba.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67333)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#5BA3DA"/>
|
||||
<mask id="mask0_270_67333" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_270_67333)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M5.77333 7.9597L1.39233 7.03498L5.82733 6.15741L7.11018 1.56757L8.12183 6.12015L12.0812 7.03943L8.16838 7.9597L7.03936 11.7032L5.77333 7.9597Z" fill="#FF0000" stroke="#F7FCFF" stroke-width="0.35"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M32.0002 14H0.00012207V16.0001H32.0002V14ZM32.0002 18.0001H0.00012207V20.0001H32.0002V18.0001Z" fill="#FAD615"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67333">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.0 KiB |
17
Flagpack/AX - Aland Islands.svg
Normal file
@@ -0,0 +1,17 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67529)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#0061C1"/>
|
||||
<mask id="mask0_270_67529" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_270_67529)">
|
||||
<path d="M10 -1H9V0V9H0H-1V10V14V15H0H9V24V25H10H14H15V24V15H32H33V14V10V9H32H15V0V-1H14H10Z" fill="#D21034" stroke="#FFCE00" stroke-width="2"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67529">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 766 B |
19
Flagpack/AZ - Azerbaijan.svg
Normal file
@@ -0,0 +1,19 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67323)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#AF0100"/>
|
||||
<mask id="mask0_270_67323" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_270_67323)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V8H32V0H0Z" fill="#3CA5D9"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 16V24H32V16H0Z" fill="#73BE4A"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M17.1399 15.024C15.7929 14.7134 14.6103 13.5539 14.6243 12C14.6374 10.5454 15.4933 9.36784 16.9747 9.03345C18.4562 8.69905 19.9922 9.33448 19.9922 9.33448C19.5839 8.42657 18.1625 7.78992 16.9966 7.79298C14.8263 7.79867 12.511 9.4555 12.4882 11.9856C12.4646 14.6086 14.961 16.0999 17.1581 16.0941C18.9194 16.0895 19.7561 14.9559 19.9297 14.4739C19.9297 14.4739 18.4869 15.3345 17.1399 15.024ZM18.0178 13.4207L19.1933 12.6027L20.3689 13.4207L19.9542 12.0499L21.0955 11.1846L19.6636 11.1554L19.1933 9.80266L18.7231 11.1554L17.2912 11.1846L18.4325 12.0499L18.0178 13.4207Z" fill="#F7FCFF"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67323">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.4 KiB |
18
Flagpack/BA - Bosnia and Herzegovina.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67491)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H32V24H0V0Z" fill="#2E42A5"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9 0H29V24L9 0Z" fill="#FECA00"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M6.10805 2.79376L4.93248 3.4118L5.15699 2.10278L4.20593 1.17573L5.52026 0.984745L6.10805 -0.206238L6.69583 0.984745L8.01016 1.17573L7.0591 2.10278L7.28362 3.4118L6.10805 2.79376Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M9.59474 7.07391L8.41917 7.69195L8.64368 6.38293L7.69263 5.45588L9.00695 5.2649L9.59474 4.07391L10.1825 5.2649L11.4969 5.45588L10.5458 6.38293L10.7703 7.69195L9.59474 7.07391Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M12.8634 11.2384L11.6878 11.8564L11.9124 10.5474L10.9613 9.62037L12.2756 9.42939L12.8634 8.2384L13.4512 9.42939L14.7655 9.62037L13.8145 10.5474L14.039 11.8564L12.8634 11.2384Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0653 15.2292L14.8898 15.8473L15.1143 14.5383L14.1632 13.6112L15.4775 13.4202L16.0653 12.2292L16.6531 13.4202L17.9674 13.6112L17.0164 14.5383L17.2409 15.8473L16.0653 15.2292Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M19.5144 19.3368L18.3388 19.9548L18.5634 18.6458L17.6123 17.7188L18.9266 17.5278L19.5144 16.3368L20.1022 17.5278L21.4165 17.7188L20.4655 18.6458L20.69 19.9548L19.5144 19.3368Z" fill="#F7FCFF"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M23.3269 23.311L22.1513 23.9291L22.3759 22.6201L21.4248 21.693L22.7391 21.502L23.3269 20.311L23.9147 21.502L25.229 21.693L24.278 22.6201L24.5025 23.9291L23.3269 23.311Z" fill="#F7FCFF"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67491">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1.9 KiB |
14
Flagpack/BB - Barbados.svg
Normal file
@@ -0,0 +1,14 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67571)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M22 0H32V24H22V0Z" fill="#2E42A5"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0H22V24H10V0Z" fill="#FECA00"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H10V24H0V0Z" fill="#2E42A5"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M18.612 15.6678C18.8 14.3782 20.1328 11.5753 20.1328 11.5753L20.7509 9.93074L18.3012 10.7256L18.8809 11.0273L17.7464 14.2258L16.8895 14.0212V7.85251L17.5674 7.80301L15.8953 5.37152L14.2628 7.85251H15.0505V14.0212L14.0852 14.1826L13.3967 10.9479L13.7942 10.6147L11.1289 10.1233L11.9246 11.5916C11.9246 11.5916 12.9956 14.1082 13.3967 15.6678L15.0505 15.5404V17.9375H16.8895V15.5404L18.612 15.6678Z" fill="black"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67571">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 1008 B |
12
Flagpack/BD - Bangladesh.svg
Normal file
@@ -0,0 +1,12 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67546)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<rect width="32" height="24" fill="#38A17E"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M13 19C16.866 19 20 15.866 20 12C20 8.13401 16.866 5 13 5C9.13401 5 6 8.13401 6 12C6 15.866 9.13401 19 13 19Z" fill="#F72E45"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67546">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 516 B |
13
Flagpack/BE - Belgium.svg
Normal file
@@ -0,0 +1,13 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67324)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M10 0H21V24H10V0Z" fill="#FECA00"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M21 0H32V24H21V0Z" fill="#E31D1C"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0H11V24H0V0Z" fill="#1D1D1D"/>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67324">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 546 B |
18
Flagpack/BF - Burkina Faso.svg
Normal file
@@ -0,0 +1,18 @@
|
||||
<svg width="32" height="24" viewBox="0 0 32 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g clip-path="url(#clip0_270_67325)">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="#5EAA22"/>
|
||||
<mask id="mask0_270_67325" style="mask-type:luminance" maskUnits="userSpaceOnUse" x="0" y="0" width="32" height="24">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V24H32V0H0Z" fill="white"/>
|
||||
</mask>
|
||||
<g mask="url(#mask0_270_67325)">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M0 0V12H32V0H0Z" fill="#C51918"/>
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M16.0354 15.7706L11.3325 19.3299L12.8373 13.533L8.42633 10.0036H13.6836L16.0348 4.85773L18.3859 10.0036H23.6432L19.1855 13.5376L20.7371 19.3299L16.0354 15.7706Z" fill="#FECA00"/>
|
||||
</g>
|
||||
</g>
|
||||
<defs>
|
||||
<clipPath id="clip0_270_67325">
|
||||
<rect width="32" height="24" fill="white"/>
|
||||
</clipPath>
|
||||
</defs>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 932 B |