This commit is contained in:
SDE
2023-05-16 17:14:16 +03:00
commit c17da7eaab
157 changed files with 14503 additions and 0 deletions

415
.gitignore vendored Normal file
View File

@@ -0,0 +1,415 @@
### JetBrains+all template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
.idea/
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Django template
*.log
*.pot
*.pyc
__pycache__/
local_settings.py
db.sqlite3
db.sqlite3-journal
media
# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/
# in your Git repository. Update and uncomment the following line accordingly.
# <django-project-name>/staticfiles/
### JetBrains+iml template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# AWS User-specific
.idea/**/aws.xml
# Generated files
.idea/**/contentModel.xml
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/artifacts
# .idea/compiler.xml
# .idea/jarRepositories.xml
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# *.iml
# *.ipr
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# SonarLint plugin
.idea/sonarlint/
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
# Android studio 3.1+ serialized cache file
.idea/caches/build_file_checksums.ser
### Python template
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/

0
AuthApp/__init__.py Normal file
View File

253
AuthApp/admin.py Normal file
View File

@@ -0,0 +1,253 @@
# coding=utf-8
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from BaseModels.admin_utils import *
from django.contrib.auth.models import User
from django.utils.translation import gettext_lazy as _
from AuthApp.models import *
from django.contrib.auth.models import Group
from django.db import models
from django.contrib.admin.models import LogEntry
from django.db.models import F, Value as V
from django.db.models.functions import Concat
from django.contrib.auth.admin import GroupAdmin as BaseGroupAdmin
from django.contrib.auth.models import Group
from django.contrib.admin import SimpleListFilter
class LogEntryAdmin(admin.ModelAdmin):
# pass
list_display = (
'__str__', 'action_time', 'user', 'content_type', 'object_id', 'object_repr', 'action_flag', 'change_message')
list_filter = ('content_type', 'action_flag')
search_fields = ['user__username', 'change_message', 'object_id', 'object_repr']
date_hierarchy = 'action_time'
def has_delete_permission(self, request, obj=None):
return False
admin.site.register(LogEntry, LogEntryAdmin)
# class Admin_ProfileInline(admin.StackedInline):
# fieldsets = (
# (None, {
# 'classes': ['wide'],
# 'fields': (
# ('enable',),
# ('regions',),
# ('company_obj', 'office', 'company_position', 'departament'),
# ('delivery_address'),
# ('discount',),
# ('work_start_D', 'work_finish_D'),
# ('days_to_order_cancellation_default', 'days_to_pay_default', 'pay_terms'),
# ('authMailCode', 'document_sign_person'),
# ('birthdate'),
# 'comment', 'creator'
# )
# }),
# ('Дополнительно', {
# 'classes': ['wide'],
# 'fields': (
# ('connected_mailings',),
# ('mailing_sets', 'json_data', 'sync_data')
# )
# }),
# ('1С', {
# 'classes': ['wide'],
# 'fields': (
# ('id_1s', 'name',),
# )
# }),
# )
#
# model = UserProfile
# can_delete = False
# extra = 1
# fk_name = 'user'
#
# filter_horizontal = ['regions', 'connected_mailings']
# raw_id_fields = ("company_obj", 'office')
# verbose_name_plural = _(u'Профиль пользователя')
#
# list_display = ['company_obj', 'office', 'company_position', 'departament', 'creator']
# readonly_fields = ['creator', ]
# Define a new User admin
class Admin_User(UserAdmin):
pass
# def user_groups(self, obj):
# return ' \ '.join(obj.groups.all().values_list('name', flat=True))
#
# user_groups.short_description = u'Группы'
#
# def last_web_request(self, obj):
# return obj.user_profile.last_web_request
#
# last_web_request.short_description = u'Последний запрос'
# def profile_enable(self, obj):
# if obj.user_profile.enable:
# return '+'
# else:
# return '-'
#
# profile_enable.short_description = u'Включен'
# fieldsets = (
# (None, {
# 'classes': ['wide'],
# 'fields': (
# ('username', 'password'),
# ('first_name', 'last_name', 'email'),
# ('is_active', 'is_staff', 'is_superuser'),
# ('groups', 'user_permissions'),
# ('last_login', 'date_joined'),
# # ('username', 'first_name', 'last_name'),
# # ('password'),
# # ('email', 'is_active'),
# # ('is_staff')
# )
# }),
#
# )
save_on_top = True
# list_display = ['id', 'profile_enable', 'last_name', 'first_name', 'email', 'last_web_request', 'is_staff',
# 'is_active', 'user_groups']
# list_editable = ['is_staff', 'is_active']
# list_display_links = ['first_name', 'last_name', 'email']
# search_fields = ['first_name', 'last_name', 'email']
#
# inlines = (Admin_ProfileInline,)
# actions = ['del_all_temp_users', ]
#
# ordering = ['is_staff', 'last_name', 'first_name']
#
# def del_all_temp_users(modeladmin, request, queryset):
# queryset.filter(mipp_user__temporary_user=True).delete()
#
# del_all_temp_users.short_description = _(u'Удалить всех временных пользователей')
# Re-register UserAdmin
admin.site.unregister(User)
admin.site.register(User, Admin_User)
class Admin_UserProfile(Admin_BaseIconModel):
pass
# def get_list_filter(self, request):
# res = super(Admin_UserProfile, self).get_list_filter(request)
# user_groups = request.user.groups.all().values_list('name', flat=True)
# if request.user.is_superuser or 'Отдел продаж: Начальник отдела продаж' in user_groups or 'Маркетинг: Маркетолог' in user_groups:
# return res
#
# return []
#
# def get_queryset(self, request):
# user_groups = request.user.groups.all().values_list('name', flat=True)
# if request.user.is_superuser or 'Отдел продаж: Начальник отдела продаж' in user_groups or 'Маркетинг: Маркетолог' in user_groups:
# return UserProfile.objects.all()
#
# companies_ids = request.user.companies_for_manager.all().values_list('id', flat=True)
# queryset = UserProfile.objects.filter(
# company_obj__id__in=companies_ids
# # ).annotate(
# # lead_source = F('company_obj__lead_source')
# )
# return queryset
#
# def get_list_display_links(self, request, list_display):
# res = super(Admin_UserProfile, self).get_list_display_links(request, list_display)
# if not request.user.is_superuser: # and not request.user.has_perm('AuthApp.change_userprofile'):
# return None
# return res
#
# def get_changelist_instance(self, request):
# if not request.user.is_superuser: # and not request.user.has_perm('AuthApp.change_userprofile'):
# self.list_editable = ['birthdate']
# return super(Admin_UserProfile, self).get_changelist_instance(request)
#
# def user_name(self, obj):
# return '{0} {1}'.format(obj.user.last_name, obj.user.first_name)
#
# user_name.short_description = u'Имя'
#
# def lead_source(self, obj):
# res = None
# if obj.company_obj:
# res = obj.company_obj.lead_source
# if not res:
# res = '-'
# else:
# res = obj.company_obj.get_lead_source_display()
# return res
#
# lead_source.short_description = 'Источник'
# lead_source.admin_order_field = 'company_obj__lead_source'
#
# def manager(self, obj):
# if not obj.company_obj or not obj.company_obj.manager_obj:
# return '-'
#
# return '{0}'.format(obj.company_obj.manager_obj.get_full_name())
#
# manager.short_description = u'Менеджер'
#
# fieldsets = (
# (None, {
# 'classes': ['wide'],
# 'fields': (
# 'user', 'enable', 'account_type',
# ('discount',),
# ('work_start_D', 'work_finish_D'),
# ('company_obj', 'company', 'office', 'company_position', 'departament'),
# ('delivery_address'),
# ('days_to_order_cancellation_default', 'days_to_pay_default', 'pay_terms'),
# ('authMailCode', 'document_sign_person'),
# ('birthdate'),
# ('connected_mailings',),
# 'creator'
# )
# }),
# ('1С', {
# 'classes': ['wide'],
# 'fields': (
# ('id_1s', 'name',),
# )
# }),
# )
#
# save_on_top = True
#
# list_display = [
# # 'user__last_name', 'user__first_name', 'user__email', 'user__is_staff', 'user__is_active',
# 'id', 'user_name', 'user', 'enable', 'birthdate', 'lead_source', 'manager',
# 'company_obj', 'office', 'company_position', 'departament', 'account_type', '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', 'company_obj__name', 'company_position',
# 'departament',
# 'company_obj', 'company_obj__manager_obj'
# ] # 'user__last_name', 'user__first_name', 'user__email']
#
# list_filter = ['company_obj__lead_source', ManagersFilter, 'account_type']
#
# filter_horizontal = ['connected_mailings']
# # raw_id_fields = ("favourites",)
# verbose_name_plural = _(u'Профиль пользователя')
admin.site.register(UserProfile, Admin_UserProfile)

0
AuthApp/api/__init__.py Normal file
View File

View File

@@ -0,0 +1,88 @@
# coding=utf-8
from BaseModels.api.base_api_views import *
from ..models import *
from rest_framework.response import Response
from BaseModels.mailSender import techSendMail
import json
from BaseModels.api.base_api_permissions import *
from datetime import datetime
import re
from rest_framework import status
from BaseModels.api.base_api_serializers import Import_Pocket_Srializer
class Managers_1C_get_timestamp(APIBaseSimplaClass):
permission_classes = (api_1C_perm,)
serializer_class = Import_Pocket_Srializer
def get(self, request):
from GeneralApp.views import get_timestamp_by_property_item_name
property_item_name = u'1S_managers'
last_timestamp = get_timestamp_by_property_item_name(property_item_name)
return Response({
'property_item_name' : property_item_name,
'timestamp' : last_timestamp
})
class Managers_1C_import(APIBaseSimplaClass):
# authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (api_1C_perm,)
serializer_class = Import_Pocket_Srializer
def post(self, request, format=None):
"""
import 1C companies data
"""
log = ''
res = ''
try:
data = request.data
if not data:
msg = 'нет данных в пакете'
res_Dict = {
'status': 'error',
'error': msg
}
return Response(res_Dict, status=status.HTTP_400_BAD_REQUEST)
from .import_1C_data import import_1C_pocket
res = import_1C_pocket(data)
res_Dict = {
'status': 'finished',
'log': log
}
res_Dict.update(res)
return Response(res_Dict)
except Exception as e:
len_data = 0
if request.data:
len_data = len(request.data)
title = 'ОШИБКА tE Managers_1C_import'
msg = 'Managers_1C_import Error = {0}({1})<br>lenght data = {2}<br>log...<br>{3}'.format(
str(e), str(e.args), str(len_data), str(res)
)
from BaseModels.mailSender import techSendMail_for_specified_email_list
from tEDataProj.inter import problem_solvers_personal_1S
techSendMail_for_specified_email_list(msg, problem_solvers_personal_1S, title=title)
res_Dict = {
'status': 'error',
'error': str(e)
}
return Response(res_Dict, status=status.HTTP_400_BAD_REQUEST)

View File

@@ -0,0 +1,76 @@
# coding=utf-8
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import PermissionDenied
from tEDataProj.inter import check_user_key_inter
class Auth_API_perms(BasePermission):
"""
Allows access only users w full access.
"""
def has_permission(self, request, view):
if not request.user or request.user.is_anonymous or not request.user.is_active:
return False
# auth_data = request.query_params
# if not check_user_key_inter(auth_data):
# raise PermissionDenied(code=403)
user = request.user
groups = user.groups.all()
groups_name_list = groups.values_list('name', flat=True)
if u'API 1С импорт' in groups_name_list:
return False
if view.basename == u'user':
# if view.action in ('get_subordinate_staff',):
# return True
if view.action in (
'get_sales_department_staff',) and u'Отдел продаж: Начальник отдела продаж' in groups_name_list:
return True
if view.action in ('create',):
perm = user.has_perm('AuthApp.UI_managers_create')
return perm
if view.action in ('update', 'partial_update', 'add_communication_item'):
perm = user.has_perm('AuthApp.UI_managers_modify')
return perm
if view.action in ('destroy',):
perm = user.has_perm('AuthApp.UI_managers_delete')
return perm
if view.action in (
'retrieve', 'list', 'list_by_company_id', 'list_by_office_id', 'get_subordinate_staff',
'get_all_staff'):
perm = user.has_perm('AuthApp.UI_managers_retrieve')
return perm
if view.action in ('get_sales_stat_by_productid', 'stat_list'):
perm = user.has_perm('AuthApp.UI_managers_all_stat') or user.has_perm('AuthApp.UI_managers_self_stat')
return perm
# if view.basename == u'userprofile':
#
# if view.action in ('create',):
# perm = user.has_perm('AuthApp.add_userprofile')
# return perm
#
# if view.action in ('update', 'partial_update'):
# perm = user.has_perm('AuthApp.change_userprofile')
# return perm
#
# if view.action in ('destroy',):
# perm = user.has_perm('AuthApp.delete_userprofile')
# return perm
#
# if view.action in ('retrieve', 'list'):
# return True
return False

17
AuthApp/api/api_urls.py Normal file
View File

@@ -0,0 +1,17 @@
from django.conf.urls import url, include
# from .api_views import *
from rest_framework import routers
from .api_1C_views import *
from .v1.user.user_api_views import *
router = routers.SimpleRouter()
# router.register(r'user', UserProfile_ViewSet)
# router.register(r'client_person', Client_Person_ViewSet)
router.register(r'v1/managers', v1_Managers_ViewSet)
router.register(r'v1/personal', v1_Personal_ViewSet)
urlpatterns = router.urls + [
url(r'^1c/import_managers_data$', Managers_1C_import.as_view()),
# url(r'^1c/managers_import_1C_pocket_from_file$', ),
url(r'^1c/import_managers/get_last_timestamp$', Managers_1C_get_timestamp.as_view()),
]

94
AuthApp/api/api_views.py Normal file
View File

@@ -0,0 +1,94 @@
# coding=utf-8
from BaseModels.api.base_api_views import *
from AuthApp.models import *
from .serializers import *
from .api_permissions import *
from rest_framework.decorators import action
def get_buttons_states_Dict(user):
Dict = {
'managers_menu': user.has_perm('AuthApp.UI_managers_show'),
'managers_add_but': user.has_perm('AuthApp.UI_managers_create'),
'managers_change_but': user.has_perm('AuthApp.UI_managers_modify'),
'managers_delete_but': user.has_perm('AuthApp.UI_managers_delete'),
'admin_group_menu': user.has_perm('AuthApp.UI_adminGroup_show'), #показываем в меню группу Администрирование
'personal_show': user.has_perm('AuthApp.UI_personal_show'), #показываем в группе Администрирование пункт Персонал
}
return Dict
class Client_Person_ViewSet(APIViewSet_ModelClass):
queryset = UserProfile.objects.filter(user__is_staff=False)
serializer_class = UserProfile_Serializer
permission_classes = (Auth_API_perms,)
def get_serializer_class(self):
try:
if self.action == 'retrieve':
return UserProfile_Serializer
except (KeyError, AttributeError):
pass
return super(Client_Person_ViewSet, self).get_serializer_class()
class Staff_Person_ViewSet(APIViewSet_ModelClass):
queryset = UserProfile.objects.filter(user__is_staff=True)
serializer_class = UserProfile_Serializer
permission_classes = (Auth_API_perms,)
def get_serializer_class(self):
try:
if self.action == 'retrieve':
return UserProfile_Serializer
except (KeyError, AttributeError):
pass
return super(Staff_Person_ViewSet, self).get_serializer_class()
class UserProfile_ViewSet(APIViewSet_ModelClass):
queryset = UserProfile.objects.all()
serializer_class = UserProfile_Serializer
permission_classes = (Auth_API_perms,)
def get_serializer_class(self):
try:
if self.action == 'retrieve':
return UserProfile_Serializer
except (KeyError, AttributeError):
pass
return super(UserProfile_ViewSet, self).get_serializer_class()
# @action(methods=['GET'], detail=True)
# def get_current_order(self, request, pk):
# from B2BApp.models import Order
# from B2BApp.api.serializers import B2B_Order_serializer
#
# try:
# from B2BApp.views import get_waiting_order_or_create_new
# order = get_waiting_order_or_create_new(request, pk)
# except Order.DoesNotExist:
# raise serializers.ValidationError(
# u'Ошибка, функция недоступна'
# )
#
# order_data = B2B_Order_serializer(order)
#
# return Response(order_data.data)

View File

@@ -0,0 +1,194 @@
# coding=utf-8
from BaseModels.mailSender import techSendMail
from ..models import *
from datetime import date, datetime
from GeneralApp.views import get_timestamp_by_property_item_name, set_timestamp_by_propertiy_item_name
from BaseModels.error_processing import *
from uuid import uuid1
from GeneralApp.temp_data_funcs import add_tmp_data, del_tmp_data_by_obj
def avg(val):
"""uses floating-point division."""
return sum(val) / float(len(val))
from django.http import HttpResponse
import json
def import_1C_pocket_from_file(request):
if not request.user.is_superuser:
return HttpResponse(u'import_1C_pocket_from_file PERMISSION FAIL')
try:
f = open('companies_1s.txt')
data = f.read()
except:
return HttpResponse(u'import_1C_pocket_from_file READ FILE FAIL')
import re
data = re.sub(r'[\r\n\t]', ' ', data)
data = re.sub(r'\s+', ' ', data)
request_data = data
data = json.loads(request_data)
import_1C_pocket(data)
return HttpResponse(u'import_1C_pocket_from_file Accept')
def import_1C_pocket(json_data):
log = ''
log_begin_DT = datetime.now()
msg = 'import_1C_pocket MANAGERS start - {0}<br>---------------<br><br>'.format(str(log_begin_DT))
log = '{0}<br>{1}'.format(log, msg)
try:
data = json_data['data_list']
timestamp = json_data['timestamp']
msg = str(timestamp)
log = '{0}<br>{1}'.format(log, msg)
# dt = datetime.fromtimestamp(timestamp)
saved_timestamp = get_timestamp_by_property_item_name('1S_managers')
if saved_timestamp and saved_timestamp >= timestamp:
# generate_error(f, u'import_1S_companies', u'1S_companies пакет устарел, импорт не был произведен', u'')
msg = '<b style="color : red;">!!!!! --- 1S_managers пакет устарел, импорт не был произведен</b>'
print(msg)
return {u'result': u'1S_managers пакет устарел, импорт не был произведен', 'error': 304}
set_timestamp_by_propertiy_item_name('1S_managers', timestamp)
except:
data = json_data
# сохраняем данные для импорта временно в БД
tmp_data = add_tmp_data(data_type='import_proc', data_target='managers_1s_import', data=data)
for item in data:
# print(str(item))
# break
json_item = json.dumps(item, ensure_ascii=False)
try:
if not u'id' in item:
# generate_error(f, u'import_1S_companies', u'1S_companies ID отсутствует в экзепляре данных', json_item)
msg = '<b style="color : red;">!!!!! --- 1S_managers ID отсутствует в экзепляре данных</b>'
log = '{0}<br>{1}'.format(log, msg)
return {
u'result': msg,
u'error': 400,
}
msg = '{0} - {1}'.format(item[u'id'], item[u'name'])
log = '{0}<br>{1}'.format(log, msg)
user_profiles = UserProfile.objects.filter(id_1s=item[u'id'])
kwargs = {
'name' : item[u'name'].replace(u"'", '"'),
'id_1s' : item[u'id'],
'company_position' : item[u'position'],
'departament': str(item[u'subdiv']),
}
work_start_D = item[u'datein'].replace(u" ", '')
if len(work_start_D) > 9:
work_start_D = datetime.strptime(work_start_D, "%d.%m.%Y")
kwargs.update({'work_start_D' : work_start_D})
work_finish_D = item[u'dateout'].replace(u" ", '')
if len(work_finish_D) > 9:
work_finish_D = datetime.strptime(work_finish_D, "%d.%m.%Y")
kwargs.update({'work_finish_D': work_finish_D})
birthday = item[u'birthday'].replace(u" ", '')
if len(birthday) > 9:
birthday = datetime.strptime(birthday, "%d.%m.%Y")
kwargs.update({'birthdate': birthday})
user = None
u_profile = None
if user_profiles:
user_profiles.update(**kwargs)
u_profile = user_profiles[0]
msg = ' - ОБНОВЛЕНИЕ данных МЕНЕДЖЕРА - {0}'.format(str(u_profile.__dict__))
user = u_profile.user
if not user:
username = str(item[u'id'])
mail = '{0}@truenergy.by'.format(str(item[u'id']))
password = user_id = str(uuid1().hex)[:10]
user = User.objects.create_user(username=username, email=mail, password=password)
user.is_staff = True
user.is_active = True
user.is_superuser = False
user.set_password(password)
user.save()
kwargs.update({'user': user})
user_profiles = UserProfile.objects.filter(user=user)
user_profiles.update(**kwargs)
u_profile = user_profiles[0]
msg = ' - СОЗДАНИЕ МЕНЕДЖЕРА - {0}'.format(str(u_profile.__dict__))
log = '{0}<br>{1}'.format(log, msg)
msg = ''
if 'work_finish_D' in kwargs and user.is_active:
user.is_active = False
msg = ' - отключен доступ<br>'
name_list = item[u'name'].split(' ')
if len(name_list) > 1 and user.first_name != ' '.join(name_list[1:]):
user.first_name = ' '.join(name_list[1:])
msg = ' - изменено имя<br>'
if len(name_list) > 0 and user.last_name != name_list[0]:
user.last_name = name_list[0]
msg = ' - изменена фамилия'
if msg:
user.save()
log = '{0}<br>{1}'.format(log, msg)
except Exception as e:
# generate_error(f, u'import_1S_companies', str(e), json_item)
msg = '<b style="color : red;">!!!!! --- import_1C_pocket MANAGERS error={0}</b>'.format(str(e))
print(msg)
log = '{0}<br>{1}'.format(log, msg)
# close_log_file(f, u'END import_1S_companies')
# удаляем временные данные для импорта из БД
if tmp_data:
del_tmp_data_by_obj(tmp_data)
msg = 'import_1C_package MANAGERS finish - {0} (processing time = {1}<br>---------------<br><br>'.format(
str(datetime.now()),
str(datetime.now() - log_begin_DT)
)
log = '{0}<br>{1}'.format(log, msg)
title = 'import_1C_pocket MANAGERS'
techSendMail(log, title)
return {
u'result': log
}

26
AuthApp/api/init_api.py Normal file
View File

@@ -0,0 +1,26 @@
from ..models import mail_list_types
from django.http import JsonResponse, Http404
def init_API(request, get_Dict=False):
# auth_data = request.GET
# if not check_user_key_inter(auth_data):
# raise exceptions.PermissionDenied()
Dict = {
}
mail_list_types_Dict = {}
for item in mail_list_types:
mail_list_types_Dict.update({
item[0] : item[1]
})
Dict.update({'mail_list_types' : mail_list_types_Dict})
if get_Dict:
return Dict
return JsonResponse({'data': Dict})

View File

@@ -0,0 +1,49 @@
from rest_framework import serializers
from AuthApp.models import *
# Person
class client_UserProfile_Serializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = (
'id',
# 'name', 'company', 'departament', 'company_position', 'phone', 'email', 'document_sign_person',
# 'days_to_order_cancellation_default', 'days_to_pay_default', 'pay_terms', 'discount', 'birthdate',
)
class staff_UserProfile_Serializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = (
'id',
# 'name', 'company', 'departament', 'company_position', 'phone', 'email',
# 'birthdate',
)
class UserProfile_Serializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = (
'id',
# 'name', 'company', 'departament', 'company_position', 'phone', 'email', 'document_sign_person',
# 'days_to_order_cancellation_default', 'days_to_pay_default', 'pay_terms', 'discount', 'birthdate',
)
class UserProfile_list_Serializer(serializers.ModelSerializer):
class Meta:
model = UserProfile
fields = (
'id',
# 'name'
)
# ----------------------------------------

View File

@@ -0,0 +1,116 @@
# coding=utf-8
from rest_framework.permissions import BasePermission
from rest_framework.exceptions import PermissionDenied
from tEDataProj.inter import check_user_key_inter
from AuthApp.models import User
def check_of_user_is_manager_of_company(user, view):
if not 'pk' in view.kwargs:
return False
try:
objs = User.objects.get(
user_profile__company_obj__manager_obj=user,
id=view.kwargs['pk']
)
except:
return False
return objs
def check_of_user_is_company_staff(user, view):
if not 'pk' in view.kwargs:
return False
try:
objs = User.objects.get(
user_profile__company_obj=user.user_profile.company_obj,
id=view.kwargs['pk']
)
except:
return False
return objs
class Personal_API_perms(BasePermission):
"""
Allows access only users w full access.
"""
def has_permission(self, request, view):
if not request.user or request.user.is_anonymous or not request.user.is_active:
return False
# auth_data = request.query_params
# if not check_user_key_inter(auth_data):
# raise PermissionDenied(code=403)
user = request.user
groups = user.groups.all()
groups_name_list = groups.values_list('name', flat=True)
if u'API 1С импорт' in groups_name_list:
return False
if view.basename == u'user':
if view.action in ('create',):
perm = user.has_perm('AuthApp.UI_company_staff_create')
return perm
if view.action in (
'update', 'partial_update', 'add_communication_item', 'get_connected_mailings', 'possible_mailings',
'change_mailing_status'):
# perm = user.has_perm('AuthApp.UI_managers_modify')
# return perm
if not user.is_staff:
# персонал компании
if user.has_perm('AuthApp.UI_company_staff_modify_if_staff_company'):
return check_of_user_is_company_staff(user, view)
return False
# если персонал
else:
if check_of_user_is_manager_of_company(user, view) and user.has_perm(
'AuthApp.UI_company_staff_modify_if_manager'):
return True
elif user.has_perm('AuthApp.UI_company_staff_modify_any'):
return True
if view.action in ('destroy',):
perm = user.has_perm('AuthApp.UI_company_staff_delete')
return perm
if view.action in (
'retrieve', 'list', 'list_by_company_id', 'list_by_office_id',
'possible_departaments_list', 'possible_company_positions_list'
):
perm = user.has_perm('AuthApp.UI_company_staff_retrieve_any_no_staff')
if not perm:
perm = user.has_perm('AuthApp.UI_company_staff_retrieve')
return perm
# if view.basename == u'userprofile':
#
# if view.action in ('create',):
# perm = user.has_perm('AuthApp.add_userprofile')
# return perm
#
# if view.action in ('update', 'partial_update'):
# perm = user.has_perm('AuthApp.change_userprofile')
# return perm
#
# if view.action in ('destroy',):
# perm = user.has_perm('AuthApp.delete_userprofile')
# return perm
#
# if view.action in ('retrieve', 'list'):
# return True
return False

View File

@@ -0,0 +1,124 @@
from rest_framework import serializers
from ....models import *
from ....funcs import fullname_for_user
class Personal_change_mailing_status_Serializer(serializers.Serializer):
mailing_ID = serializers.IntegerField()
mailing_status = serializers.BooleanField()
class User_sync_Serializer(serializers.ModelSerializer):
id_1s = serializers.SerializerMethodField()
def get_id_1s(self, obj):
return obj.user_profile.id_1s
class Meta:
model = User
fields = (
'id_1s',
)
class Profile_list_Serializer(serializers.ModelSerializer):
from GeneralApp.api.v1.communications.communications_api_serializers import Communications_create_Serializer
office_name = serializers.SerializerMethodField(required=False)
company_name = serializers.SerializerMethodField(required=False)
manager_name = serializers.SerializerMethodField(required=False)
company_client_type = serializers.SerializerMethodField(required=False)
communications = Communications_create_Serializer(many=True)
def get_company_client_type(self, obj):
try:
if obj and obj.company_obj:
return obj.company_obj.client_type
else:
return None
except:
return None
def get_office_name(self, obj):
try:
if obj and obj.office:
return obj.office.name
except:
return None
return None
def get_company_name(self, obj):
try:
if obj and obj.company_obj:
return obj.company_obj.name
except:
return None
return None
def get_manager_name(self, obj):
try:
if obj and obj.company_obj and obj.company_obj.manager_obj:
return '{0} {1}'.format(obj.company_obj.manager_obj.last_name, obj.company_obj.manager_obj.first_name)
except:
return None
return None
class Meta:
model = UserProfile
fields = (
'id',
'enable',
'company_obj', 'company_name',
'company_position', 'company_client_type',
'phone',
'delivery_address',
'office', 'office_name',
'departament',
'document_sign_person',
'work_start_D',
'work_finish_D',
'birthdate',
'comment',
'communications',
'priority_connect_type',
'modifiedDT',
'mailing_sets',
'manager_name'
)
class User_list_Serializer(serializers.ModelSerializer):
full_name = serializers.SerializerMethodField('get_full_name_user', required=False)
# user_profile = Profile_list_Serializer()
def get_full_name_user(self, obj):
name = fullname_for_user(obj)
if not name:
name = obj.email
return name
class Meta:
model = User
fields = (
'id', 'full_name'
)
class Personal_list_Serializer(User_list_Serializer):
user_profile = Profile_list_Serializer()
class Meta:
model = User
fields = (
'id', 'full_name', 'first_name', 'last_name', 'is_active', 'is_staff', 'user_profile', 'email'
)
extra_kwargs = {
'email': {'required': 'False'},
'first_name': {'required': 'False'},
'last_name': {'required': 'False'},
}

File diff suppressed because it is too large Load Diff

70
AuthApp/forms.py Normal file
View File

@@ -0,0 +1,70 @@
# coding=utf-8
from django import forms
from django.contrib.auth.forms import AuthenticationForm
from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from .models import *
# from djng.styling.bootstrap3.forms import Bootstrap3ModelForm
# from djng.forms import fields, NgModelFormMixin, NgFormValidationMixin, NgModelForm
# from datetimepicker.widgets import DateTimePicker
# from datetimepicker.helpers import js_loader_url
# class PersonForm(NgModelFormMixin, NgFormValidationMixin, NgModelForm, Bootstrap3ModelForm):
#
# form_name = 'person_form'
# scope_prefix = 'person_data'
#
# class Meta:
# model = UserProfile
# fields = ['name', 'departament', 'company', 'company_position',
# 'days_to_order_cancellation_default', 'days_to_pay_default',
# 'pay_terms', 'birthdate',
# 'phone', 'email', 'discount', 'document_sign_person']
def emailValid(value):
if User.objects.filter(username=value, is_active=True):
raise ValidationError(_(u'пользователь с таким e-mail уже существует, воспользуйтесь восстановлением пароля'))
def check_authorizationBy_cleaned_data(cleaned_data):
from django.contrib.auth import authenticate
print('check_authorizationBy_cleaned_data')
username = cleaned_data.get('username')
password = cleaned_data.get('password')
user = authenticate(username=username, password=password)
# print(user)
if user:
# if user.is_active:
return user
def check_activate_by_user(reg_user):
print('check_activate_by_user')
if reg_user:
if reg_user.is_active:
return True
return False
class LoginForm(AuthenticationForm):
username = forms.EmailField(label=_('Email'), widget=forms.TextInput())
password = forms.CharField(min_length=8, label=_('Пароль'), widget=forms.PasswordInput(render_value=False))
def clean(self):
# print('check')
cleaned_data = super(LoginForm, self).clean()
reg_user = check_authorizationBy_cleaned_data(cleaned_data)
# print(reg_user)
if not reg_user:
raise ValidationError(_(u'Пользователь с введенными регистрационными данными не зарегистрирован. Проверьте правильность ввода e-mail и пароля.'))
else:
if not check_activate_by_user(reg_user):
raise ValidationError(_(u'Указанная учетная запись не была Активирована'))
return cleaned_data
class ResetPassword_byEmail_Form(AuthenticationForm):
email = forms.EmailField(label=_('Email'), widget=forms.TextInput())

392
AuthApp/funcs.py Normal file
View File

@@ -0,0 +1,392 @@
# -*- coding: utf-8 -*-
from .models import *
from datetime import datetime, timedelta, date
from django.db.models import Q, F, Value as V
from django.db.models.functions import ExtractYear, Concat, Coalesce
from functools import reduce
from operator import or_
sales_department_groups = [
'Отдел продаж: Начальник отдела продаж',
'Отдел продаж: Менеджер отдела продаж',
'Отдел продаж: Региональный руководитель отдела продаж',
'Отдел продаж: Стажер отдела продаж'
]
heads_of_sales_groups = [
'Отдел продаж: Начальник отдела продаж',
'Отдел продаж: Региональный руководитель отдела продаж',
]
def get_personal_companies_by_managers_list(managers_list, filter_kwargs={}, exclude_kwargs={}, only_emails=False):
try:
users = User.objects.filter(
user_profile__company_obj__manager_obj__in=managers_list,
**filter_kwargs
).exclude(
**exclude_kwargs
).order_by('-is_active', 'last_name', 'first_name')
if only_emails:
users = list(users.values_list('email', flat=True))
except Exception as e:
msg = f'get_personal_companies_by_managers_list Error = {str(e)}'
users = []
return users
def get_head_staffs_by_user_email(user_email, only_emails=False):
try:
user = User.objects.get(email=user_email)
heads = get_head_staffs(user.user_profile, only_emails=True)
except User.DoesNotExist as e:
heads = None
return heads
def get_head_staffs(user_profile_or_user_ID, only_emails=False):
if type(user_profile_or_user_ID) == str:
user = User.objects.get(id=int(user_profile_or_user_ID))
user_profile = user.user_profile
else:
user_profile = user_profile_or_user_ID
user = user_profile.user
groups = user.groups.all()
groups_name_list = groups.values_list('name', flat=True)
Q_list = []
if user_profile.is_sales_department_staff():
if 'Отдел продаж: Менеджер отдела продаж' in groups_name_list:
groups_list = ['Отдел продаж: Региональный руководитель отдела продаж']
kwargs = {
'groups__name__in': groups_list,
'user_profile__regions__in': user.user_profile.regions.all(),
}
Q_list.append(Q(**kwargs))
if not 'Отдел продаж: Начальник отдела продаж' in groups_name_list:
groups_list = ['Отдел продаж: Начальник отдела продаж']
kwargs = {'groups__name__in': groups_list}
Q_list.append(Q(**kwargs))
# groups_list = ['Управляющий']
# kwargs.update({'groups__name__in': groups_list})
if not 'Управляющий' in groups_name_list:
kwargs = {'is_superuser': True}
Q_list.append(Q(**kwargs))
Q_obj = reduce(lambda p1, p2: (p1 | p2), Q_list)
heads = User.objects.filter(
Q_obj,
is_active=True,
is_staff=True
)
heads = heads.distinct().order_by('-is_active', 'last_name', 'first_name')
if only_emails:
heads = list(heads.values_list('email', flat=True))
return heads
def get_bosses_and_subordinate_staff_ids(user, only_active=False, include_cur_user=False, only_emails=False):
users = []
kwargs = {
'is_staff': True,
}
if only_active:
kwargs.update({'is_active': True})
groups = user.groups.all()
groups_name_list = groups.values_list('name', flat=True)
Q_obj = Q()
if 'Отдел продаж: Начальник отдела продаж' in groups_name_list:
Q_obj.add(Q(is_superuser=True), Q.OR)
elif 'Отдел продаж: Региональный руководитель отдела продаж' in groups_name_list:
Q_obj.add(Q(is_superuser=True), Q.OR)
Q_obj.add(Q(groups__name='Отдел продаж: Начальник отдела продаж'), Q.OR)
elif 'Отдел продаж: Менеджер отдела продаж' in groups_name_list:
Q_obj.add(Q(is_superuser=True), Q.OR)
Q_obj.add(Q(groups__name='Отдел продаж: Начальник отдела продаж'), Q.OR)
Q_obj.add(Q(
groups__name='Отдел продаж: Региональный руководитель отдела продаж',
user_profile__regions__in=user.user_profile.regions.all()), Q.OR)
subordinate_users = get_subordinate_staff(
user, only_active=only_active, include_cur_user=include_cur_user, only_emails=only_emails
)
res_val = 'id'
if only_emails:
res_val = 'user_profile__email'
subordinate_users = subordinate_users.values_list(res_val, flat=True)
if user.is_superuser:
users = subordinate_users
else:
bosses = User.objects.filter(Q_obj, **kwargs).values_list(res_val, flat=True)
users = set(subordinate_users) | set(bosses)
return users
def get_subordinate_sales_staff(user, groups, only_active=False, only_emails=False, include_cur_user=False):
kwargs = {
'is_staff': True,
}
if only_active:
kwargs.update({'is_active': True})
Q_obj = Q()
if 'Отдел продаж: Начальник отдела продаж' in groups:
kwargs_groups = [
'Отдел продаж: Менеджер отдела продаж',
'Отдел продаж: Региональный руководитель отдела продаж',
'Отдел продаж: Стажер отдела продаж'
]
Q_obj.add(Q(groups__name__in=kwargs_groups), Q.OR)
elif u'Отдел продаж: Региональный руководитель отдела продаж' in groups:
kwargs_groups = [
'Отдел продаж: Менеджер отдела продаж',
'Отдел продаж: Стажер отдела продаж'
]
Q_obj.add(Q(groups__name__in=kwargs_groups, user_profile__regions__in=user.user_profile.regions.all()), Q.OR)
elif u'Отдел продаж: Менеджер отдела продаж' in groups:
kwargs_groups = [
'Отдел продаж: Стажер отдела продаж'
]
Q_obj.add(Q(groups__name__in=kwargs_groups, user_profile__regions__in=user.user_profile.regions.all()), Q.OR)
elif 'Отдел закупок: Начальник отдела закупок' in groups:
kwargs_groups = [
'Отдел закупок: Менеджер отдела закупок',
]
Q_obj.add(Q(groups__name__in=kwargs_groups), Q.OR)
if include_cur_user:
Q_obj.add(Q(id=user.id), Q.OR)
users = User.objects.filter(Q_obj, **kwargs).exclude(is_superuser=True)
users = users.distinct().order_by('-is_active', 'last_name', 'first_name')
# if kwargs:
# other_users = User.objects.filter(
# **kwargs
# ).exclude(
# is_superuser=True
# )
# users = users.union(other_users)
# users = users.distinct().order_by('-is_active', 'last_name', 'first_name')
if only_emails:
users = list(users.values_list('email', flat=True))
return users
def get_subordinate_staff(user, only_active=False, include_cur_user=False, only_emails=False):
users = []
groups = user.groups.all()
groups_name_list = groups.values_list('name', flat=True)
# отдел продаж
if set(groups_name_list):
users = get_subordinate_sales_staff(user, groups_name_list, include_cur_user=include_cur_user)
if not users and user.is_superuser:
users = get_all_staff()
if not users and include_cur_user:
users = User.objects.filter(id=user.id)
if users and only_active:
users = users.filter(is_active=True)
if only_emails:
users = list(users.values_list('email', flat=True))
return users
def get_all_staff(only_active=False, only_emails=False):
kwargs = {
'is_staff': True,
}
if only_active:
kwargs.update({'is_active': True})
users = User.objects.filter(**kwargs).order_by('-is_active', 'last_name', 'first_name')
if only_emails:
users = list(users.values_list('email', flat=True))
return users
def get_managers_wo_work():
managers = get_sales_managers()
managers = managers.filter(
user_profile__last_web_request__lt=datetime.now() - timedelta(minutes=20),
user_profile__last_web_request__contains=date.today()
)
return managers
def get_birthdays(manager=None, for_next_count_days=None, only_first=False):
from_D = datetime.now()
Q_obj = Q()
Q_obj.add(Q(Q(birthdate__day=from_D.day) & Q(birthdate__month=from_D.month)), Q.OR)
# в for_next_count_days количество будущих дней для которых собираем дни рождения
if for_next_count_days:
for i in range(1, for_next_count_days):
Q_obj.add(Q(Q(birthdate__day=(from_D + timedelta(days=i)).day) & Q(
birthdate__month=(from_D + timedelta(days=i)).month)), Q.OR)
kwargs = {}
if manager:
kwargs.update({
'company_obj__manager_obj': manager,
})
from .models import UserProfile
user_profiles = UserProfile.objects.filter(
Q_obj,
# birthdate__lte=(datetime.now() + timedelta(days=3)).date(),
# birthdate__gte=datetime.now().date()
**kwargs
).annotate(
age=datetime.now().year - ExtractYear('birthdate')
).order_by('company_obj__manager_obj', 'birthdate')
if user_profiles and only_first:
return user_profiles[0]
return user_profiles
def get_sales_managers(only_active=False, only_emails=False):
from CompaniesApp.models import Company
kwargs = {
'is_staff': True,
'companies_for_manager__company_type': 'client',
}
if only_active:
kwargs.update({'is_active': True})
managers = User.objects.filter(**kwargs).exclude(companies_for_manager=None).distinct().order_by('-is_active',
'last_name',
'first_name')
# managers = Company.objects.filter(
# company_type='client'
# ).values_list('manager_obj', flat=True).distinct()
if only_emails:
managers = list(managers.values_list('email', flat=True))
return managers
def get_full_names_by_id(ids):
names = User.objects.filter(
id__in=ids
).annotate(
fullname=Concat(F('last_name'), V(' '), F('first_name'))
).values('id', 'fullname')
names = {item['id']: item['fullname'] for item in names}
return names
def fullname_for_user(user):
full_name = '%s %s' % (user.last_name, user.first_name)
return full_name.strip()
def get_heads_of_sales(only_active=False, only_emails=False):
kwargs = {
'is_staff': True,
}
Q_obj = Q(groups__name__in=heads_of_sales_groups) | Q(is_superuser=True)
if only_active:
kwargs.update({'is_active': True})
users = User.objects.filter(
Q_obj,
**kwargs
).order_by('-is_active', 'last_name', 'first_name')
if only_emails:
users = list(users.values_list('email', flat=True))
return users
def get_sales_department_staff(regions=None, only_active=False, only_emails=False):
kwargs = {
'is_staff': True,
}
if not regions:
sales_department_groups = [
'Отдел продаж: Менеджер отдела продаж', 'Отдел продаж: Начальник отдела продаж',
'Отдел продаж: Региональный руководитель отдела продаж',
'Отдел продаж: Стажер отдела продаж'
]
kwargs.update({
'groups__name__in': sales_department_groups,
})
else:
sales_department_groups = [
'Отдел продаж: Менеджер отдела продаж',
'Отдел продаж: Региональный руководитель отдела продаж',
'Отдел продаж: Стажер отдела продаж'
]
kwargs.update({
'groups__name__in': sales_department_groups,
})
if only_active:
kwargs.update({'is_active': True})
users = User.objects.filter(**kwargs).exclude(is_superuser=True).order_by('-is_active', 'last_name', 'first_name')
if only_emails:
users = list(users.values_list('email', flat=True))
return users
def get_marketing_departament_persons(only_active=False, only_emails=False):
kwargs = {
'is_staff': True,
'groups__name': 'Маркетинг: Маркетолог',
}
if only_active:
kwargs.update({'is_active': True})
users = User.objects.filter(**kwargs).order_by('-is_active', 'last_name', 'first_name')
if only_emails:
users = list(users.values_list('email', flat=True))
return users

View File

@@ -0,0 +1,44 @@
# Generated by Django 4.2.1 on 2023-05-16 09:47
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='UserProfile',
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='Дополнительные данные')),
('UI_lang', models.CharField(choices=[('ru', 'Russian'), ('en', 'English')], max_length=2, verbose_name='Язык интерфейса')),
('nick_name', models.CharField(blank=True, max_length=250, null=True, verbose_name='Псевдоним')),
('authCode', models.CharField(blank=True, max_length=32, null=True)),
('phone', models.CharField(blank=True, max_length=100, null=True, verbose_name='Телефон')),
('birthdate', models.DateField(blank=True, null=True, verbose_name='Дата рождения')),
('comment', models.TextField(blank=True, null=True, verbose_name='Дополнительные сведения')),
('referal_link', models.TextField(verbose_name='Реферальная ссылка')),
('user', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_profile', to=settings.AUTH_USER_MODEL, verbose_name='id пользователя')),
],
options={
'verbose_name': 'Профиль',
'verbose_name_plural': 'Профили',
'ordering': ('user__last_name', 'user__first_name'),
'permissions': (),
},
),
]

View File

@@ -0,0 +1,41 @@
# Generated by Django 4.2.1 on 2023-05-16 14:01
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),
('AuthApp', '0001_initial'),
]
operations = [
migrations.AddField(
model_name='userprofile',
name='answer_success_count',
field=models.IntegerField(default=0, verbose_name='Успешных ответов'),
),
migrations.AddField(
model_name='userprofile',
name='balance',
field=models.FloatField(default=0, verbose_name='Баланс'),
),
migrations.AddField(
model_name='userprofile',
name='questions_count',
field=models.IntegerField(default=0, verbose_name='Задано вопросов'),
),
migrations.AlterField(
model_name='userprofile',
name='comment',
field=models.TextField(blank=True, null=True, verbose_name='Комментарий'),
),
migrations.AlterField(
model_name='userprofile',
name='user',
field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='user_profile', to=settings.AUTH_USER_MODEL, verbose_name='Пользователь'),
),
]

View File

83
AuthApp/models.py Normal file
View File

@@ -0,0 +1,83 @@
# coding=utf-8
from __future__ import unicode_literals
from django.contrib.auth.models import User
from django.db import models
from django.utils.translation import gettext_lazy as _
from django.db.models.signals import post_save, pre_save
from django.contrib.contenttypes.fields import GenericRelation
from BaseModels.base_models import BaseModel
from datetime import datetime
from django.conf import settings
def user_name_str(self):
return f'{self.last_name} {self.first_name}'
User.add_to_class("__str__", user_name_str)
class UserProfile(BaseModel):
user = models.OneToOneField(User, verbose_name=_('Пользователь'), related_name=u'user_profile',
null=True, blank=True, on_delete=models.CASCADE)
UI_lang = models.CharField(max_length=2, verbose_name=_('Язык интерфейса'), choices=settings.LANGUAGES)
nick_name = models.CharField(max_length=250, verbose_name=_('Псевдоним'), null=True, blank=True)
authCode = models.CharField(max_length=32, null=True, blank=True)
phone = models.CharField(max_length=100, verbose_name=_('Телефон'), null=True, blank=True)
birthdate = models.DateField(verbose_name=_(u'Дата рождения'), null=True, blank=True)
comment = models.TextField(verbose_name=_('Комментарий'), null=True, blank=True)
referal_link = models.TextField(verbose_name=_('Реферальная ссылка'))
balance = models.FloatField(verbose_name=_('Баланс'), default=0)
questions_count = models.IntegerField(verbose_name=_('Задано вопросов'), default=0)
answer_success_count = models.IntegerField(verbose_name=_('Успешных ответов'), default=0)
def __str__(self):
if self.user:
return '{0} {1}'.format(self.user.last_name, self.user.first_name)
else:
return str(self.id)
class Meta:
permissions = (
)
verbose_name = _('Профиль')
verbose_name_plural = _('Профили')
ordering = ('user__last_name', 'user__first_name')
# @receiver(post_save, sender=User)
def create_user_profile(sender, instance, created, **kwargs):
if created:
UserProfile.objects.create(user=instance)
post_save.connect(create_user_profile, sender=User, dispatch_uid='post_save_connect')
# @receiver(pre_save, sender=User)
def preSaveUser(sender, instance, **kwargs):
if not instance.email:
instance.email = str(instance.username).lower()
try:
instance.user_profile.modifiedDT = datetime.now()
except:
pass
pre_save.connect(preSaveUser, sender=User, dispatch_uid='pre_save_connect')

194
AuthApp/stat_funcs.py Normal file
View File

@@ -0,0 +1,194 @@
from BaseModels.mailSender import techSendMail
from django.db.models import Sum, FloatField, F, Value as V, Count, OuterRef, Subquery, Q, Max, Exists
from django.db.models.functions import Concat, Length
from operator import itemgetter
from datetime import datetime
import copy
from .models import User
from datetime import datetime, date, timedelta
from collections import OrderedDict
def managers_sales_by_period_n_regions(period_from=None, period_to=None, region_ids=None, kwargs=None, sum_w_prev_data=None):
from CompaniesApp.models import Company
log_begin_DT = datetime.now()
msg = 'managers_sales_by_period_n_regions start - {0}<br>---------------<br><br>'.format(str(log_begin_DT))
print(msg)
stat_kwargs = {}
queryset_kwargs = {}
all_managers = False
separate_managers_sales_by_link_in_order = False
if kwargs:
if 'stat_kwargs' in kwargs:
stat_kwargs.update(kwargs['stat_kwargs'])
if 'all_managers' in kwargs:
all_managers = kwargs['all_managers']
if 'queryset_kwargs' in kwargs:
queryset_kwargs.update(kwargs['queryset_kwargs'])
if 'separate_managers_sales_by_link_in_order' in kwargs:
separate_managers_sales_by_link_in_order = kwargs['separate_managers_sales_by_link_in_order']
from B2BApp.stat_funcs import get_sales_by_period_n_regions
orders, receipts, pays = get_sales_by_period_n_regions(period_from=period_from, period_to=period_to, region_ids=region_ids, kwargs=kwargs)
ids = []
if not all_managers:
ord_manager_ids = set(item['manager_obj__id'] for item in orders)
receipt_manager_ids = set(item['manager_obj__id'] for item in receipts)
pay_manager_ids = set(item['manager__id'] for item in pays)
ids = set.union(ord_manager_ids, receipt_manager_ids, pay_manager_ids)
queryset_kwargs.update({'id__in': ids})
if not sum_w_prev_data:
managers = User.objects.filter(
**queryset_kwargs
).annotate(
full_name=Concat(F('last_name'), V(' '), F('first_name')),
).order_by('full_name').values(
'id', 'full_name'
)
objs = list(managers)
objs.sort(key=itemgetter('full_name'), reverse=False)
if None in ids:
objs.append({'id': None, 'full_name': 'не назначен менеджер'})
else:
objs = copy.deepcopy(sum_w_prev_data)
from_date = None
to_date = None
if orders:
orders.sort(key=itemgetter('delivery_DT'), reverse=False)
from_date = orders[0]['delivery_DT'].date()
to_date = orders[-1]['delivery_DT'].date()
if from_date:
from_DT = from_date
else:
from_DT = date(year=2000, month=1, day=1)
if to_date:
to_DT = to_date + timedelta(days=1)
else:
to_DT = datetime.now().date() + timedelta(days=1)
chart_data_Dict = OrderedDict({
str(from_DT + timedelta(n)): {
'sales_count': 0,
'sales_sum': 0,
'paid_sum': 0,
'paid_cash': 0,
'paid_invoice': 0
} for n in range(int((to_DT - from_DT).days) + 1)
})
objs_i = 0
while objs_i < len(objs):
obj_i = objs[objs_i]
# if separate_managers_sales_by_link_in_order:
if not 'sales_count' in obj_i:
obj_i['sales_count'] = 0
if not 'sales_sum' in obj_i:
obj_i['sales_sum'] = 0
if not 'paid_sum' in obj_i:
obj_i['paid_sum'] = 0
if not 'receipts_sum' in obj_i:
obj_i['receipts_sum'] = 0
if not 'receipts_count' in obj_i:
obj_i['receipts_count'] = 0
# # if not 'top_sales_80' in obj_i:
# # обнуляем все при каждой итерации
# obj_i['top_sales_80'] = ''
# obj_i['manager_top_sales_80'] = ''
# if obj_i['id'] == 657:
# print('!')
obj_orders = list(filter(lambda item: item['manager_obj__id'] == obj_i['id'], orders))
cash_orders = list(filter(lambda item: item['pay_type'] == 'cash', obj_orders))
obj_pays = list(filter(lambda item: item['manager__id'] == obj_i['id'], pays))
obj_receipts = list(filter(lambda item: item['manager_obj__id'] == obj_i['id'], receipts))
cash_receipts = list(filter(lambda item: item['pay_type'] == 'cash' and item['receipts_sum'], obj_receipts))
if obj_orders:
obj_i['sales_sum'] += round(sum(item['sales_sum'] for item in obj_orders if item['sales_sum']), 2)
obj_i['sales_count'] += round(sum(item['sales_count'] for item in obj_orders if item['sales_count']), 2)
if cash_orders:
# добавляем в оплаты кэшевые операции потому как они отсутствуют в платежах
obj_i['paid_sum'] += round(sum(item['paid_sum'] for item in cash_orders if item['paid_sum']), 2)
# if obj_i['id'] == 1199:
# print('!')
if obj_receipts:
obj_i['receipts_sum'] += round(
sum(item['receipts_sum'] for item in obj_receipts if item['receipts_sum']), 2)
obj_i['receipts_count'] += round(
sum(item['receipts_count'] for item in obj_receipts if item['receipts_count']), 2)
obj_i['sales_sum'] -= obj_i['receipts_sum']
obj_i['sales_count'] -= obj_i['receipts_count']
if cash_receipts:
# минусуем из оплат кэшевые операции потому как возвраты
obj_i['paid_sum'] -= round(sum(item['receipts_sum'] for item in cash_receipts if item['receipts_sum']), 2)
if obj_pays:
obj_i['paid_sum'] += round(sum(item['sum_pay_byn'] for item in obj_pays if item['sum_pay_byn']), 2)
obj_i['indicative_sales'] = round(( obj_i['sales_sum'] + obj_i['paid_sum']) / 2, 2)
required_del_client = False
if stat_kwargs:
if stat_kwargs and 'sales_count__gte' in stat_kwargs and stat_kwargs['sales_count__gte'] and \
objs[objs_i][
'sales_count'] < float(stat_kwargs['sales_count__gte']):
required_del_client = True
elif stat_kwargs and 'sales_count__lte' in stat_kwargs and stat_kwargs['sales_count__lte'] and \
objs[objs_i][
'sales_count'] > float(stat_kwargs['sales_count__lte']):
required_del_client = True
elif stat_kwargs and 'sales_sum__gte' in stat_kwargs and stat_kwargs['sales_sum__gte'] and objs[objs_i][
'sales_sum'] < float(stat_kwargs['sales_sum__gte']):
required_del_client = True
elif stat_kwargs and 'sales_sum__lte' in stat_kwargs and stat_kwargs['sales_sum__lte'] and objs[objs_i][
'sales_sum'] > float(stat_kwargs['sales_sum__lte']):
required_del_client = True
# if 'months_count' in kwargs:
# obj_i['middle_sum_for_month'] = round( obj_i['indicative_sales'] / kwargs['months_count'], 2)
# # else:
# # print('!')
if required_del_client:
del objs[objs_i]
else:
objs_i += 1
msg = 'managers_sales_by_period_n_regions finish - {0} (processing time = {1}<br>---------------<br><br>'.format(
str(datetime.now()),
str(datetime.now() - log_begin_DT)
)
print(msg)
return objs, chart_data_Dict

3
AuthApp/tests.py Normal file
View File

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

57
AuthApp/urls.py Normal file
View File

@@ -0,0 +1,57 @@
# coding=utf-8
from django.conf.urls import url
# from AuthApp.js_views import *
# from AuthApp.import_funcs import *
from AuthApp.views import *
from django.contrib.auth import views
urlpatterns = [
# ajax ----------------
# url(r'^login$', user_login_View_ajax, name='user_login_View_ajax'),
# url(r'^login_confirm$', user_login_confirm_ajax, name='user_login_confirm_ajax'),
#
# url(r'^logout$', user_logout_ajax, name='user_logout_View_ajax'),
# url(r'^logout_confirm$', user_logout_confirm_ajax, name='user_logout_confirm_ajax'),
#
# url(r'^check_exists_email$', check_exists_email_ajax, name='check_exists_email_ajax'),
#
# url(r'^registration$', user_registration_View_ajax, name='user_registration_View_ajax'),
# url(r'^user_registration_send_confirmation_mail$',
# user_registration_send_confirmation_mail_ajax, name='user_registration_send_confirmation_mail_ajax'),
#
# url(r'^password_recovery$', password_recovery_View_ajax, name='password_recovery_View_ajax'),
# url(r'^password_reset$', password_reset_send_mail_ajax, name='password_reset_send_mail_ajax'),
#
# url(r'^registration_by_order_data_and_send_confirmation_mail$',
# registration_by_order_data_and_send_confirmation_mail_ajax, name='registration_by_order_data_and_send_confirmation_mail_ajax'),
#
#
# # -----------------------
#
# 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'),
#
# # url(r'^user/password/reset/$',
# # 'django.contrib.auth.views.password_reset',
# # {'post_reset_redirect' : '/user/password/reset/done/',
# # 'password_reset_form': ResetForm
# # },
# # name="password_reset"),
# # url(r'^user/password/reset/done/$', views.password_reset_done, name='password_reset_done'),
# url(r'^reset/(?P<uidb64>[0-9A-Za-z_\-]+)/(?P<token>[0-9A-Za-z]{1,13}-[0-9A-Za-z]{1,20})/$',
# views.password_reset_confirm, name='password_reset_confirm'),
# url(r'^reset/done/$', views.password_reset_complete, name='password_reset_complete'),
#
#
# # import
# url(r'^import_one_user/(?P<user_id>[\d+]*)$',
# import_json_mipp_user_by_id, name='import_one_mipp'),
# url(r'^import_web_users$',
# import_json_mipp_webUsers, name='import_mipp_webUsers'),
#
# url(r'^import_invoices_for_user_by_user_id/(?P<user_id>[\d+]*)$',
# import_invoices_for_user_by_user_id, name='import_invoices_for_user_by_user_id'),
]

292
AuthApp/views.py Normal file
View File

@@ -0,0 +1,292 @@
# coding=utf-8
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.template import loader, RequestContext
from django.contrib.auth.decorators import login_required
from BaseModels.mailSender import techSendMail
from django.utils.translation import ugettext as _
from datetime import datetime
def create_personal_user(data, creator):
try:
user_id = str(uuid1().hex)[:10]
user_name = data['email']
mail = user_name
user = User.objects.create_user(username=user_name, email=mail, password=user_id)
if 'first_name' in data and data['first_name']:
user.first_name = data['first_name']
if 'last_name' in data and data['last_name']:
user.last_name = data['last_name']
user.is_staff = False
user.is_active = False
user.is_superuser = False
# user.set_password(user_id)
user.save()
user_communications_ads_list = []
if 'office__name' in data['user_profile']:
del data['user_profile']['office__name']
if 'communications' in data['user_profile']:
user_communications_ads_list.extend(data['user_profile']['communications'])
del data['user_profile']['communications']
if not 'creator' in data['user_profile'] and creator:
data['user_profile']['creator'] = creator
profiles = UserProfile.objects.filter(user=user).update(**data['user_profile'])
if user_communications_ads_list:
from GeneralApp.funcs import create_communications_items_by_list
comm_objs = create_communications_items_by_list(user.user_profile, user_communications_ads_list)
user.refresh_from_db()
return {
'name' : mail,
'pass' : user_id,
'user' : user
}
except Exception as e:
return {
'error': 'Ошибка добавление нового пользователя = {0}'.format(str(e)),
}
def decode_get_param(data):
import base64
import json
d = data['data'].encode()
token_data = base64.b64decode(d)
try:
request_data = token_data.decode('utf8')
except:
request_data = token_data
data = json.loads(request_data)
return data
def check_user_key(data):
# print(u'check_user_key')
# try:
# user_id = int(data[0])
# except:
# user_id = None
# try:
# user1S_id = int(data[1])
#
# company_id = data[2]
# user_key = data[3]
res = u'Key Broken'
# user_id = data[u'userId']
# user_key = data[u'userKey']
# user1S_id = data['user1S_id']
# company_id = data[u'companyId']
data = decode_get_param(data)
try:
token = UserTokens.objects.get(
user_id=data['pk'],
user_key=data['code'],
user1S_id=data['1S_pk'],
company_id = data['comp_pk']
)
res = u'Accept'
# print(u'try1 ok')
except UserTokens.DoesNotExist:
# если не найден id и ключ
try:
token = UserTokens.objects.get(
user_id=data['pk']
)
# techSendMail(
# u'user try access by id={0}, key={1}, c_id={2}'.format(
# str(user_id),
# str(user_key),
# str(company_id)
# )
# )
res = u'Key Broken'
# print(u'try2 ok')
except UserTokens.DoesNotExist:
# если не найден id
token = UserTokens.objects.create(
user_id=data['pk'],
user_key=data['code'],
company_id=data['comp_pk']
)
res = u'Accept'
# print(u'except ok')
return res
def recovery_password_user(request, uidb64=None, token=None):
from django.contrib.auth.views import PasswordResetConfirmView
return PasswordResetConfirmView(request=request, uidb64=uidb64, token=token
)
# def recovery_password_user_complete(request, user_id, authCode):
# from django.contrib.auth.forms import SetPasswordForm
# from AuthApp.funcs import sendActivationMail
#
# try:
# user = User.objects.get(id=user_id, mipp_user__authMailCode=authCode)
#
# except:
# user = None
#
# if user:
#
# user.set_password()
#
# if reg_user.is_active:
# activated_early = True
# else:
# reg_user.backend = 'AuthApp.backends.BaldeniniAuthBackend'
# login(request, reg_user)
#
# reg_user.is_active = True
# reg_user.save()
# sendActivationMail(reg_user)
#
# else:
# raise Http404
# # print(reg_user)
#
# context = {
# 'user': user,
# 'activated_early' : activated_early,
# }
#
# t = loader.get_template('pages/profile.html')
#
# rContext = RequestContext(request, context)
# return HttpResponse(t.render(rContext))
# def check_user_registration_and_activate(request, user_id, authCode):
# from django.contrib.auth import authenticate, login
# from AuthApp.funcs import sendActivationMail
#
# try:
# reg_user = User.objects.get(id=user_id, mipp_user__authMailCode=authCode)
#
# except:
# reg_user = None
#
# activated_early = False
#
# if reg_user:
#
# if reg_user.is_active:
# activated_early = True
# else:
# reg_user.backend = 'AuthApp.backends.BaldeniniAuthBackend'
# login(request, reg_user)
#
# reg_user.is_active = True
# reg_user.save()
# sendActivationMail(reg_user)
#
# else:
# raise Http404
# # print(reg_user)
#
# context = {
# 'reg_user': reg_user,
# 'activated_early' : activated_early,
# }
#
#
#
# t = loader.get_template('admin_pages/pages/Profile/profile.html')
#
# # rContext = RequestContext(request, context)
# # return HttpResponse(t.render(rContext))
# return HttpResponse(t.render(context, request))
def create_temporary_user():
from django.utils.translation import ugettext as _
user_id = str(uuid1().hex)[:10]
user_name = u'user'+user_id
mail = user_id+u'@truenergy.by'
user = User.objects.create_user(mail, mail,user_id)
user.first_name = _(u'незарег. пользователь')
user.last_name = u''
user.is_staff = False
user.is_active = True
user.is_superuser = False
user.set_password(user_id)
# print(u'user_create_pass', user.password)
# p = user.get_profile()
# p.address = ''
# p.phone = ''
# p.group = None
# p.discount = 0
# p.pay_balance = 0
# p.authMailCode = uuid1().hex
# p.save()
user.save()
user.mipp_user.temporary_user = True
user.mipp_user.save()
# print('user',user)
# print('created profile',p)
# print('user created', user)
return {
'name' : mail,
'pass' : user_id,
'user' : user
}
def get_active_user(request):
if request.user.is_anonymous:
return None
else:
user = request.user
# try:
# bd = user.mipp_user.birthdate
# except MIPPUser.DoesNotExist:
# MIPPUser.objects.create(user=user)
return user
def get_active_user_if_anonymous_create_temporary(request):
user = get_active_user(request)
if not user:
new_user_Dict = create_temporary_user()
user = auth.authenticate(username=new_user_Dict['name'], password=new_user_Dict['pass'])
if user:
auth.login(request, user)
return user

68
BaseModels/SMS_sender.py Normal file
View File

@@ -0,0 +1,68 @@
# -*- coding: utf-8 -*-
__author__ = 'SDE'
import urllib3
import json
def send_SMS(phone, text, urgent=False, staff=False):
import re
from BaseModels.mailSender import techSendMail
print('send_SMS')
# change all + for GET request
text = text.replace(' ', '+')
text = text.encode('utf-8')
if not staff:
phone = phone.replace(' ', '')
p = re.compile('\d{7,12}')
phone_list = p.findall(phone)
if not phone_list:
return u'phone DoesNotExist'
phone = phone_list[0]
phone.encode('utf-8')
http_request = 'http://cp.websms.by/?r=api/msg_send' \
'&user=administrator@baldenini.by' \
'&apikey=zTwevODOYl' \
'&sender=Baldenini'
# '&test=1'
if urgent:
http_request = http_request + '&urgent=1'
http_request = http_request + '&recipients=' + phone
http_request = http_request.encode('utf-8')
http_request = http_request + '&message=' + text
http = urllib3.PoolManager()
r = http.request('GET', http_request)
r_status = json.loads(r.data)
if r_status['status'] == 'error':
message = r_status['message']
try:
req = http_request.decode('utf-8')
message = req + u'<br>' + message
# message = message.decode('utf-8')
techSendMail(message)
except:
pass
else:
message = None
stat = {
'status' : r_status,
'message' : message,
}
print('sms_status', phone, stat)
return r_status
# return u'Accept'

1
BaseModels/__init__.py Normal file
View File

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

217
BaseModels/admin_utils.py Normal file
View File

@@ -0,0 +1,217 @@
# -*- coding: utf-8 -*-
__author__ = 'SDE'
from django.contrib.admin.widgets import AdminFileWidget, AdminTextareaWidget
from django.contrib.postgres.fields import JSONField
from django.utils.safestring import mark_safe
from django.db import models
from django.contrib import admin
from django.forms import widgets
import re
import json
# from modeltranslation.admin import TranslationAdmin
# from filebrowser.admin import
class AdminImageWidget(AdminFileWidget):
def render(self, name, value, attrs=None, renderer=None):
output = []
if value and getattr(value, "url", None):
output.append(
u'<img src="{url}" style="max-width: 150px; max-height: 150px; width: auto; height: auto; margin: 10px 10px 10px 10px;"/> '.format(
url=value.url))
output.append(super(AdminFileWidget, self).render(name, value, attrs))
return mark_safe(u''.join(output))
class Admin_BaseIconTabularModel(admin.TabularInline):
def formfield_for_dbfield(self, db_field, **kwargs):
formfield = super(Admin_BaseIconTabularModel, self).formfield_for_dbfield(db_field, **kwargs)
if db_field.name == 'url' or db_field.name == 'name' or db_field.name == 'title' or db_field.name == 'comment':
formfield.widget = admin.widgets.AdminTextInputWidget(attrs={'style': 'width: 500px'})
if db_field.name == 'workListForServicePage':
formfield.widget = admin.widgets.AdminTextInputWidget(attrs={'style': 'width: 800px'})
if db_field.name == 'seo_title':
formfield.widget = admin.widgets.AdminTextInputWidget(attrs={'style': 'width: 800px'})
if db_field.name == 'seo_description' or db_field.name == 'seo_keywords':
formfield.widget = admin.widgets.AdminTextareaWidget(attrs={'style': 'width: 800px'})
if db_field.name in ('text', 'description', 'seo_text'):
formfield.widget = admin.widgets.AdminTextareaWidget(attrs={'style': 'width: 800px'})
return formfield
def image_thumb(self, obj):
try:
image_url = obj.picture.url
except:
image_url = None
if not image_url:
try:
image_url = obj.icon.url
except:
image_url = None
if not image_url:
try:
image_url = obj.logo.url
except:
image_url = None
if not image_url:
try:
image_url = obj.photo.url
except:
image_url = None
if not image_url:
try:
image_url = obj.banner.photo.url
except:
image_url = None
if image_url:
s = str('<img src="' + image_url + '" width="60"/>')
return mark_safe(s)
else:
return '(none)'
image_thumb.short_description = u'Миниатюра'
image_thumb.allow_tags = True
class PrettyJSONWidget(widgets.Textarea):
def format_value(self, value):
try:
value = json.dumps(json.loads(value), indent=2, sort_keys=True, ensure_ascii=False)
# these lines will try to adjust size of TextArea to fit to content
row_lengths = [len(r) for r in value.split('\n')]
self.attrs['rows'] = min(max(len(row_lengths) + 2, 10), 30)
self.attrs['cols'] = min(max(max(row_lengths) + 2, 40), 120)
return value
except Exception as e:
print("Error while formatting JSON: {}".format(e))
# logger.warning("Error while formatting JSON: {}".format(e))
return super(PrettyJSONWidget, self).format_value(value)
class Admin_BaseIconModel(admin.ModelAdmin):
# from codemirror import CodeMirrorTextarea
# codemirror_widget = CodeMirrorTextarea(
# mode="python",
# theme="cobalt",
# config={
# 'fixedGutter': True
# },
# )
def formfield_for_dbfield(self, db_field, **kwargs):
formfield = super(Admin_BaseIconModel, self).formfield_for_dbfield(db_field, **kwargs)
if db_field.name == 'url' or db_field.name == 'name' or db_field.name == 'title':
formfield.widget = admin.widgets.AdminTextInputWidget(attrs={'style': 'width: 500px'})
if db_field.name == 'workListForServicePage':
formfield.widget = admin.widgets.AdminTextInputWidget(attrs={'style': 'width: 800px'})
if db_field.name == 'seo_title':
formfield.widget = admin.widgets.AdminTextInputWidget(attrs={'style': 'width: 800px'})
if db_field.name == 'seo_description' or db_field.name == 'seo_keywords':
formfield.widget = admin.widgets.AdminTextareaWidget(attrs={'style': 'width: 800px'})
if db_field.name in ('lexems',):
formfield.widget = admin.widgets.AdminTextareaWidget(attrs={'style': 'width: 80%'})
if db_field.name in ('type_full_name', 'properties_title_name', 'where_buy_title_name'):
formfield.widget = admin.widgets.AdminTextInputWidget(attrs={'style': 'width: 80%'})
return formfield
formfield_overrides = {
models.ImageField: {'widget': AdminImageWidget},
JSONField: {'widget': PrettyJSONWidget}
}
def image_thumb(self, obj):
try:
image_url = obj.avatar.url
except:
image_url = None
if not image_url:
try:
image_url = obj.picture.url
except:
image_url = None
if not image_url:
try:
image_url = obj.icon.url
except:
image_url = None
if not image_url:
try:
image_url = obj.main_photo().url
except:
image_url = None
if not image_url:
try:
image_url = obj.offer.main_photo().url
except:
image_url = None
if not image_url:
try:
image_url = obj.rel_product.main_photo().url
except:
image_url = None
if not image_url:
try:
image_url = obj.logo.url
except:
image_url = None
if not image_url:
try:
image_url = obj.photo.url
except:
image_url = None
if not image_url:
try:
image_url = obj.picture.url
except:
image_url = None
if image_url:
s = str('<img src="' + image_url + '" width="60"/>')
return mark_safe(s)
else:
return '(none)'
image_thumb.short_description = u'Миниатюра'
image_thumb.allow_tags = True
from modeltranslation.admin import TranslationAdmin
class AdminTranslation_BaseIconModel(Admin_BaseIconModel, TranslationAdmin):
class Media:
js = (
'https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js',
'https://ajax.googleapis.com/ajax/libs/jqueryui/1.10.2/jquery-ui.min.js',
'modeltranslation/js/tabbed_translation_fields.js',
# 'cked/ckeditor/ckeditor.js'
)
css = {
'screen': ('modeltranslation/css/tabbed_translation_fields.css',),
}

View File

View File

@@ -0,0 +1,43 @@
from openpyxl import Workbook
from django.http import HttpResponse
from openpyxl.writer.excel import save_virtual_workbook
def xls_export(data, filename):
print('xls_export')
# wb = Workbook()
# ws = wb.active
#
# r = 1
# for row in data:
# c = 1
# for val in row.values():
# try:
# ws.cell(row=r, column=c).value = val
# except:
# ws.cell(row=r, column=c).value = str(val)
# c += 1
#
# r += 1
#
# dims = {}
# for row in ws.rows:
# for cell in row:
# if cell.value:
# dims[cell.column] = max((dims.get(cell.column, 0), len(str(cell.value))))
# for col, value in dims.items():
# ws.column_dimensions[col].width = value
# filepath = "/demo.xlsx"
# wb.save(filepath)
# output = BytesIO()
# wb.save(output)
from ..office_documents_utils import get_xls_file_by_data_list
xls_file = get_xls_file_by_data_list(data)
response = HttpResponse(xls_file, content_type='application/ms-excel')
response['Content-Disposition'] = 'attachment; filename="{0}"'.format(filename)
return response

View File

@@ -0,0 +1,23 @@
def check_and_get_specific_output_format(obj, data=None, filename=None):
if obj.request.query_params and 'output_format' in obj.request.query_params and obj.request.query_params['output_format'] == 'xlsx':
if not data:
serializer = obj.get_serializer(obj.get_queryset(), many=True)
data = serializer.data
from .api_export_xls import xls_export
return xls_export(data, filename)
return None
def fix_txt_for_use_in_interlinks(txt):
txt = txt.replace('/', ' ')
txt = txt.replace('?', ' ')
txt = txt.replace(';', ' ')
txt = txt.replace(',', ' ')
txt = txt.replace('+', ' ')
txt = txt.replace(':', ' ')
return txt

View File

@@ -0,0 +1,19 @@
# from rest_framework import viewsets
#
# class APILogMiddleware(viewsets.ModelViewSet):
# # def __init__(self, get_response):
# # self.get_response = get_response
# # One-time configuration and initialization.
#
# def __call__(self, request):
# # Code to be executed for each request before
# # the view (and later middleware) are called.
#
# response = self.get_response(request)
#
# self
#
# # Code to be executed for each request/response after
# # the view is called.
#
# return response

View File

@@ -0,0 +1,22 @@
import codecs
from django.conf import settings
from rest_framework.exceptions import ParseError
from rest_framework.parsers import BaseParser
class PlainTextParser(BaseParser):
media_type = "text/plain"
def parse(self, stream, media_type=None, parser_context=None):
"""
Parses the incoming bytestream as Plain Text and returns the resulting data.
"""
parser_context = parser_context or {}
encoding = parser_context.get('encoding', settings.DEFAULT_CHARSET)
try:
decoded_stream = codecs.getreader(encoding)(stream)
text_content = decoded_stream.read()
return text_content
except ValueError as exc:
raise ParseError('Plain text parse error - %s' % str(exc))

View File

@@ -0,0 +1,36 @@
from rest_framework.permissions import BasePermission
class StaffOnly_perm(BasePermission):
"""
Allows access only to staff users.
"""
def has_permission(self, request, view):
return request.user and request.user.is_staff
class api_1C_perm(BasePermission):
"""
Allows access only 1C users.
"""
# def has_object_permission(self, request, view, obj):
def has_permission(self, request, view):
if request.user.id == 8751:
try:
if request.req_type == 'warehouse_import':
return True
else:
return False
except:
return False
perm = request.user.has_perm('AuthApp.1c_api')
return perm
class full_api_perm(BasePermission):
"""
Allows access only users w full access.
"""
def has_permission(self, request, view):
return request.user.has_perm('AuthApp.full_api')

View File

@@ -0,0 +1,44 @@
from rest_framework import serializers
from django.contrib.contenttypes.models import ContentType
from BaseModels.mailSender import techSendMail
class Import_Element_Srializer(serializers.Serializer):
element = serializers.JSONField()
class Meta:
fields = (
'element',
)
class Import_Pocket_Srializer(serializers.Serializer):
timestamp = serializers.IntegerField()
warehouse = serializers.CharField()
data_list = Import_Element_Srializer(many=True)
class Meta:
fields = (
'timestamp', 'warehouse', 'data_list'
)
class Generic_base_Serializer(serializers.ModelSerializer):
linked_object_type = serializers.CharField(required=False)
def create(self, validated_data):
if 'linked_object_type' in validated_data:
try:
validated_data['content_type'] = ContentType.objects.get(model=validated_data['linked_object_type'])
del validated_data['linked_object_type']
except Exception as e:
msg = 'Ошибка создания generic объекта<br>{0}({1})<br>{2}'.format(
str(e),
str(e.args),
str(validated_data)
)
print(msg)
title = 'ОШИБКА tE Generic_base_Serializer create'
techSendMail(msg, title)
return super(Generic_base_Serializer, self).create(validated_data)

View File

@@ -0,0 +1,356 @@
# coding=utf-8
from rest_framework import generics
from rest_framework.authentication import BasicAuthentication, SessionAuthentication
from rest_framework.permissions import IsAuthenticated, DjangoObjectPermissions
from rest_framework.views import APIView
from rest_framework import viewsets
from rest_framework.renderers import JSONRenderer
from rest_framework.permissions import AllowAny
from rest_framework.response import Response
from rest_framework.schemas import SchemaGenerator
from rest_framework_swagger import renderers
from BaseModels.api.base_api_permissions import *
from datetime import datetime
from GeneralApp.temp_data_funcs import add_element_in_tmp_data_list, check_exists_element_in_tmp_data_list, add_element_list_to_tmp_data
from rest_framework.utils.serializer_helpers import ReturnList
from rest_framework.decorators import action
from rest_framework import status
from django.contrib.contenttypes.models import ContentType
from BaseModels.mailSender import techSendMail
# from BaseModels.api.api_middlewares import APILogMiddleware
class SwaggerSchemaView(APIView):
permission_classes = [AllowAny]
renderer_classes = [
renderers.OpenAPIRenderer,
renderers.SwaggerUIRenderer
]
def get(self, request):
generator = SchemaGenerator()
schema = generator.get_schema(request=request)
return Response(schema)
JSONCustomRenderer = JSONRenderer
JSONCustomRenderer.charset = 'utf-8'
class APIBasePublicClass(APIView):
# authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (AllowAny,)
# renderer_classes = [JSONCustomRenderer]
pagination_class = None
# def finalize_response(self, request, response, *args, **kwargs):
#
# res = super(APIBasePublicClass, self).finalize_response(request, response, *args, **kwargs)
#
# from CompaniesApp.models import Region
# regions = Region.objects.filter().values_list(
# 'id', 'domain'
# ).order_by('id')
# res.data.update({'regions': tuple(regions)})
#
# return res
# def get(self, request, *args, **kwargs):
#
# if not 'region_id' in request.headers:
# request.headers['region_id'] = '1'
#
# return super(APIBasePublicClass, self).get(request, *args, **kwargs)
class APIListBaseClass(generics.ListAPIView):
# authentication_classes = (SessionAuthentication, BasicAuthentication, )#
permission_classes = (IsAuthenticated,)
pagination_class = None
class APIBaseClass(generics.RetrieveAPIView):
# authentication_classes = (SessionAuthentication, BasicAuthentication, )#
permission_classes = (IsAuthenticated, )
# renderer_classes = [JSONCustomRenderer]
pagination_class = None
class APIBaseSimplaClass(generics.GenericAPIView):
# authentication_classes = (SessionAuthentication, BasicAuthentication)
permission_classes = (IsAuthenticated,)
# renderer_classes = [JSONCustomRenderer]
pagination_class = None
# ----------------------------
class APIViewSet_ModelReadOnlyClass(viewsets.ReadOnlyModelViewSet):
pass
# authentication_classes = (SessionAuthentication, BasicAuthentication, )#
permission_classes = (IsAuthenticated, )
# renderer_classes = [JSONCustomRenderer]
pagination_class = None
exclude_actions_for_logging = []
create_kwargs = [
'create', 'create_short', 'create_item',
'copy_item', 'create_short', 'create_reminder'
]
exclude_actions_for_logging.extend(create_kwargs)
exclude_actions_for_logging.extend([
'update', 'partial_update', 'destroy', 'update_items', 'update_item'
])
def log_save_cur_state_obj(query_data, response=None, init=False):
if query_data.basename == 'alert' or not query_data.action in exclude_actions_for_logging:
return None
if response and response.status_code > 299:
return None
data_Dict = {}
data_target = 'log_{0}'.format(str(query_data.basename))
obj_id = None
try:
if type(query_data.request.data) == list and query_data.request.data and len(query_data.request.data) > 0 and \
'id' in query_data.request.data[0]:
objs_list_ids = [obj['id'] for obj in query_data.request.data]
elif response and response.data and type(response.data) == dict and 'id' in response.data:
objs_list_ids = [response.data['id']]
elif response and response.data and getattr(response.data.serializer, 'instance', None):
objs_list_ids = [response.data.serializer.instance.id]
elif response and response.data and 'id' in response.data and response.data['id']:
objs_list_ids = [response.data['id']]
elif query_data.request.data and 'id' in query_data.request.data:
objs_list_ids = [query_data.request.data['id']]
elif 'pk' in query_data.kwargs:
objs_list_ids = [query_data.kwargs['pk']]
elif query_data.queryset:
objs_list_ids = query_data.queryset.values_list('id')
else:
return None
# if not objs_list_ids:
#
# serializer = query_data.serializer_class()
# data = serializer(data=query_data.request.data)
#
# data_Dict = {
# 'data': data,
# 'DT': str(datetime.now()),
# 'user': str(query_data.request.user),
# 'oper_type': query_data.action,
# 'init': init
# }
#
# add_element_in_tmp_data_list('log', data_target, obj_id, data_Dict)
objs_list = query_data.queryset.filter(id__in=objs_list_ids)
cur_action = query_data.action
query_data.action = 'retrieve'
serializer = query_data.get_serializer_class()
query_data.action = cur_action
obj_data_list = serializer(objs_list, many=True)
elements_list_for_add_to_tmp_data = []
for obj_data in obj_data_list.data:
obj_id = obj_data['id']
# фиксим json-неподходящие поля
for item_data in obj_data.keys():
if type(obj_data[item_data]) not in (str, int, float, dict, list, bool):
obj_data[item_data] = str(obj_data[item_data])
# if init:
# if check_exists_element_in_tmp_data_list('log', data_target, obj_id):
# continue
data_Dict = {
'id': obj_id,
'data': obj_data,
'DT': str(datetime.now()),
'user': str(query_data.request.user),
'oper_type': query_data.action,
'init': init
}
# add_element_in_tmp_data_list('log', data_target, obj_id, data_Dict)
elements_list_for_add_to_tmp_data.append(data_Dict)
add_element_list_to_tmp_data('log', data_target, init, elements_list_for_add_to_tmp_data)
except Exception as e:
response_data = ''
if response and response.data:
response_data = str(response.data)
msg = 'log_save_cur_state_obj fail save to log w data = {0}<br>{1}<br>{2}<br>response_data={3}'.format(
str(e),
'log - ' + str(data_target) + ' - ' + str(obj_id),
str(data_Dict),
response_data
)
techSendMail(msg)
return 'OK'
class APIViewSet_ModelClass(viewsets.ModelViewSet):
# pass
# # authentication_classes = (SessionAuthentication, BasicAuthentication, )#
# permission_classes = (IsAuthenticated, )
# # renderer_classes = [JSONCustomRenderer]
# pagination_class = None
def initial(self, request, *args, **kwargs):
res = super(APIViewSet_ModelClass, self).initial(request, *args, **kwargs)
if self.basename == 'alert' or not self.action in exclude_actions_for_logging:
return res
if not self.action in create_kwargs:
log_save_cur_state_obj(self, init=True)
return res
def finalize_response(self, request, response, *args, **kwargs):
res = super(APIViewSet_ModelClass, self).finalize_response(request, response, *args, **kwargs)
if self.basename == 'alert' or not self.action in exclude_actions_for_logging:
return res
log_save_cur_state_obj(self, response=response)
return res
def create(self, request, *args, **kwargs):
obj = super(APIViewSet_ModelClass, self).create(request, *args, **kwargs)
# data_Dict = {}
# try:
# data_Dict = {
# 'data': prepare_data_for_json(vars(obj)),
# 'DT': str(datetime.now()),
# 'user': str(request.user),
# 'oper_type': 'create'
# }
#
# add_element_in_tmp_data_list('log', 'properties_log', obj.id, data_Dict)
# except Exception as e:
# msg = 'fail save to log w data = {0}<br>{1}'.format(
# 'log - properties_log - ' + str(obj.id),
# str(data_Dict)
# )
# techSendMail(msg)
return obj
def partial_update(self, request, *args, **kwargs):
if request.data:
request.data['modifiedDT'] = datetime.now()
obj = super(APIViewSet_ModelClass, self).partial_update(request, *args, **kwargs)
# data_Dict = {}
# try:
# data_Dict = {
# 'data': prepare_data_for_json(vars(obj)),
# 'DT': str(datetime.now()),
# 'user': str(request.user),
# 'oper_type': 'create'
# }
#
# add_element_in_tmp_data_list('log', 'properties_log', obj.id, data_Dict)
# except Exception as e:
# msg = 'fail save to log w data = {0}<br>{1}'.format(
# 'log - properties_log - ' + str(obj.id),
# str(data_Dict)
# )
# techSendMail(msg)
return obj
class APIViewSet_ModelClass_w_Expenses(APIViewSet_ModelClass):
@action(methods=['GET'], detail=True)
def expenses_rates(self, request, *args, **kwargs):
from ExpensesApp.api.v1.expenses_rate.expenses_rate_api_serializers import ExpensesRate_get_Serializer
model = self.serializer_class.Meta.model
try:
obj = model.objects.get(id=kwargs['pk'])
except model.DoesNotExist:
return Response({'error': u'ошибка получения expenses_rates'},
status=status.HTTP_400_BAD_REQUEST)
expenses_rates = obj.expenses_rates.all()
serializer = ExpensesRate_get_Serializer(expenses_rates, many=True)
# if serializer.data:
# return Response(serializer.data)
return Response(serializer.data)
@action(methods=['GET'], detail=True)
def expenses_data(self, request, *args, **kwargs):
from ExpensesApp.api.v1.expenses_data.expenses_data_api_serializers import ExpensesData_get_Serializer
model = self.serializer_class.Meta.model
try:
obj = model.objects.get(id=kwargs['pk'])
except model.DoesNotExist:
return Response({'error': u'ошибка получения expenses_rates'},
status=status.HTTP_400_BAD_REQUEST)
expenses_data = obj.expenses_data.all()
serializer = ExpensesData_get_Serializer(expenses_data, many=True)
# if serializer.data:
# return Response(serializer.data)
return Response(serializer.data)
class APIViewSet_BaseClass(viewsets.ViewSet):
pass
# authentication_classes = (SessionAuthentication, BasicAuthentication,) #
permission_classes = (IsAuthenticated, )
# renderer_classes = [JSONCustomRenderer]
pagination_class = None
# class APIBaseClass(generics.RetrieveAPIView):
# authentication_classes = (SessionAuthentication, BasicAuthentication, )#
# permission_classes = (IsAuthenticated,)
# # renderer_classes = [JSONCustomRenderer]
# pagination_class = None
#
#
# class APIBaseSimplaClass(APIView):
# authentication_classes = (SessionAuthentication, BasicAuthentication)
# permission_classes = (IsAuthenticated,)
# # renderer_classes = [JSONCustomRenderer]
# pagination_class = None

111
BaseModels/base_models.py Normal file
View File

@@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-
__author__ = 'SDE'
from django.db import models
from datetime import datetime
# from ckeditor.fields import RichTextField
# from BaseModels.pil_graphic_utils import *
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
# add_introspection_rules([], ["^tinymce\.models\.HTMLField"])
class BaseModel(models.Model):
name = models.TextField(verbose_name=_('Название'),
help_text=_('Название'), null=True, blank=True)
name_plural = models.TextField(verbose_name=_('Название (множественное число)'),
null=True, blank=True)
order = models.IntegerField(verbose_name=_('Очередность отображения'), null=True, blank=True)
createDT = models.DateTimeField(auto_now_add=True, verbose_name=_('Дата и время создания'))
modifiedDT = models.DateTimeField(verbose_name=_('Дата и время последнего изменения'), null=True, blank=True)
enable = models.BooleanField(verbose_name=_('Включено'), default=True, db_index=True)
json_data = models.JSONField(verbose_name=_('Дополнительные данные'), default=dict, blank=True)
def __str__(self):
return self.name
def get_node_by_name(self, node_name):
if not self.json_data or not node_name in self.json_data:
return None
return self.json_data[node_name]
def add_node_to_json_data(self, node_data, save=False):
if not self.json_data:
self.json_data = {}
if type(self.json_data) == dict:
self.json_data.update(node_data)
elif type(self.json_data) == list:
self.json_data.append(node_data)
if save:
self.save(update_fields=['json_data'])
return self.json_data
def save(self, *args, **kwargs):
self.modifiedDT = datetime.now()
super().save(*args, **kwargs)
class Meta:
abstract = True
def preSave_BaseModel(sender, instance, **kwargs):
if instance and instance.user_profile:
instance.modifiedDT = datetime.now()
pre_save.connect(preSave_BaseModel, sender=BaseModel, dispatch_uid='pre_save_connect')
class BaseModelViewPage(BaseModel):
url = models.TextField(verbose_name=_('URL привязанной страницы'), unique=True,
help_text=_(
'можно изменить адрес страницы (!!! ВНИМАНИЕ !!! поисковые системы потеряют страницу и найдут лишь спустя неделю...месяц)'))
description = RichTextField(verbose_name=_('Краткое описание'), null=True, blank=True, # max_length=240,
help_text=_('краткое описание страницы (до 240 символов)'))
text = RichTextField(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'')
# icon = FileBrowseField("Image", max_length=200, directory="files/", extensions=[".jpg"], blank=True, null=True)
visible = models.BooleanField(verbose_name=_('Отображать'), default=True)
background_image_left = models.ImageField(verbose_name=_('Левая подложка'), blank=True, null=True)
background_image_right = models.ImageField(verbose_name=_('Правая подложка'), blank=True, null=True)
seo_title = models.CharField(max_length=250, verbose_name=_('Title (80 знаков)'), null=True, blank=True)
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)
class Meta:
abstract = True
def get_description_exists(self):
if self.description:
return True
return False
def get_text_exists(self):
if self.text:
return True
return False
# @receiver(pre_save, sender=User)
def preSaveBaseModelViewPage(sender, instance, **kwargs):
if not sender.url:
sender.url = slugify(sender.name)
pre_save.connect(preSaveBaseModelViewPage, sender=BaseModelViewPage, dispatch_uid='pre_save_connect')

View File

@@ -0,0 +1,150 @@
from colorsys import hls_to_rgb, rgb_to_hls, rgb_to_hsv, hsv_to_rgb
from random import uniform, randint
DEFAULT_LIGHTNESS = 0.5
DEFAULT_SATURATION = 1
DEFAULT_VARIANCE = 0.2
def get_next_HSV_color(cur_color, offset_hue=0, offset_value=0, offset_saturation=0):
red = int(cur_color[0:2], base=16)
green = int(cur_color[2:4], base=16)
blue = int(cur_color[4:6], base=16)
hue, saturation, value = rgb_to_hsv(red, green, blue)
new_hue = hue + offset_hue
new_value = value + offset_value
new_saturation = saturation - offset_saturation
# new_hue = hue + offset_hue
# new_lightness = lightness + offset_lightness
# new_saturation = saturation + offset_saturation
# red, green, blue = map(
# lambda v: int(v * 255),
# hls_to_rgb(
# new_hue,
# new_lightness,
# new_saturation,
# ),
# )
red, green, blue = hsv_to_rgb(new_hue, new_saturation, new_value)
# red, green, blue = hls_to_rgb(hue_variant, lightness, saturation)
res = f"{int(red):02x}{int(green):02x}{int(blue):02x}"
return res
def get_next_color(cur_color, offset_hue=0, offset_lightness=0, offset_saturation=0):
red = int(cur_color[0:2], base=16)
green = int(cur_color[2:4], base=16)
blue = int(cur_color[4:6], base=16)
hue, lightness, saturation = rgb_to_hls(red, green, blue)
lightness = lightness / 255
if saturation < 0.1:
saturation = 1
new_hue = hue + offset_hue
new_lightness = lightness + offset_lightness
new_saturation = saturation + offset_saturation
if new_hue > 1: new_hue = offset_hue
if new_hue < 0: new_hue = 1
if new_lightness > 1: new_lightness = offset_lightness
if new_lightness < 0: new_lightness = 1
if new_saturation > 1: new_saturation = offset_saturation
if new_saturation < 0: new_saturation = 1
red, green, blue = map(
lambda v: int(v * 255),
hls_to_rgb(
new_hue,
new_lightness,
new_saturation,
),
)
res = f"{red:02x}{green:02x}{blue:02x}"
return res
class Huetify(object):
lightness: float
saturation: float
variance: float
half_variance: float
def __init__(
self,
lightness=DEFAULT_LIGHTNESS,
saturation=DEFAULT_SATURATION,
variance=DEFAULT_VARIANCE,
) -> None:
self.lightness = lightness
self.saturation = saturation
self.variance = variance
self.half_variance = variance / 2.0
def huetify_to_rgb_hex(self, hue) -> str:
hue_variant = uniform(
hue - self.half_variance,
hue + self.half_variance,
)
red, green, blue = map(
lambda v: int(v * 255),
hls_to_rgb(
hue_variant,
self.lightness,
self.saturation,
),
)
return f"{red:02x}{green:02x}{blue:02x}"
def huetify_next_variant_to_rgb_hex(self, cur_variant):
hue_variant = cur_variant + self.half_variance
red, green, blue = map(
lambda v: int(v * 255),
hls_to_rgb(
hue_variant,
self.lightness,
self.saturation,
),
)
return red, green, blue
@property
def reddish(self):
return self.huetify_to_rgb_hex(0)
@property
def greenish(self):
return self.huetify_to_rgb_hex(0.333)
@property
def blueish(self):
return self.huetify_to_rgb_hex(0.666)
def blue_colors(self, cur_variant=None):
if not cur_variant:
cur_variant = 0.666 - self.half_variance
return self.huetify_next_variant_to_rgb_hex(cur_variant=cur_variant)
@property
def yellowish(self):
return self.huetify_to_rgb_hex(0.166)
@property
def random_color(self):
ch = randint(1, 4)
if ch == 1:
return self.reddish
elif ch == 2:
return self.greenish
elif ch == 3:
return self.greenish
else:
return self.yellowish

View File

View File

@@ -0,0 +1,108 @@
import requests
import json
from datetime import datetime, timedelta
from BaseModels.mailSender import techSendMail
from GeneralApp.temp_data_funcs import *
def get_alfabank_nb_rate_by_currency_code(code, date=None):
rate = None
res = None
req_str = None
try:
msg = f'get_alfabank_nb_rate_by_currency_code'
print(msg)
int_code = None
if code == 'USD':
int_code = 840
elif code == 'EUR':
int_code = 978
elif code == 'RUB':
int_code = 643
code_str = ''
if int_code:
code_str = f'?currencyCode={int_code}'
date_str = ''
if date:
date_str = f'date={datetime.now().strftime("%d.%m.%Y")}'
if int_code:
date_str = f'&{date_str}'
else:
date_str = f'?{date_str}'
req_str = f'https://developerhub.alfabank.by:8273/partner/1.0.1/public/nationalRates{code_str}{date_str}'
try:
msg = f'GET {req_str}'
print(msg)
res = requests.get(req_str)
msg = f'answer received = {str(res)}'
print(msg)
except Exception as e:
msg = f'Exception GET {req_str} = {str(e)} ({str(res)})'
print(msg)
res = None
if res:
# if not res and res != 200:
# if tmp_rec:
# rate = tmp_rec.json_data['rate']
# else:
# rate_Dict = {
# 'rate': 1,
# 'DT': datetime.now().strftime('%d.%m.%Y %H:%M')
# }
# create_or_update_tmp_data('currency_rate', code, rate_Dict)
# rate = 1
#
# msg = '<b style="color : red;">!!!!! --- get_alfabank_nbrb_rate_by_currency_code requests GET error={0}</b><br>{1}<br>{2}<br>rate set = {3}'.format(
# str(e),
# str(res),
# str(req_str),
# str(rate)
# )
# print(msg)
# techSendMail(msg, 'tE get_alfabank_nbrb_rate_by_currency_code error')
data = json.loads(res.content)
for item in data['rates']:
if item['iso'].upper() == code.upper():
rate = item['rate'] / item['quantity']
rate_Dict = {
'rate': rate,
'DT': datetime.now().strftime('%d.%m.%Y %H:%M')
}
create_or_update_tmp_data('currency_rate', code, rate_Dict)
break
except Exception as e:
msg = '<b style="color : red;">!!!!! --- get_alfabank_nb_rate_by_currency_code error={0}</b><br>{1}<br>{2}'.format(
str(e),
str(res),
str(req_str)
)
print(msg)
techSendMail(msg, 'tE get_alfabank_nb_rate_by_currency_code error')
# if not res:
# rate_Dict = {
# 'rate': 1,
# 'DT': datetime.now().strftime('%d.%m.%Y %H:%M')
# }
# create_or_update_tmp_data('currency_rate', code, rate_Dict)
# return 1
# if rate:
msg = f'get alfabank nb {code} rate = {str(rate)}'
print(msg)
return rate

View File

@@ -0,0 +1,51 @@
import requests
import json
from datetime import datetime, timedelta
from BaseModels.mailSender import techSendMail
from GeneralApp.temp_data_funcs import *
def get_rate_nb_by_currency_code(code, date=None):
from .nbrb.nbrb_currency_exchange import get_nbrb_rate_by_currency_code
from .alfabank_api.alfabank_api_funcs import get_alfabank_nb_rate_by_currency_code
if code == 'BYN':
return 1
rate = None
request_required = True
try:
tmp_rec = get_tmp_data('currency_rate', code)
if tmp_rec and tmp_rec.json_data:
if 'rate' in tmp_rec.json_data:
# если с момента последнего импорта прошло меньше 30 минут - забираем курс из базы
if datetime.strptime(tmp_rec.json_data['DT'], '%d.%m.%Y %H:%M') + timedelta(
minutes=30) > datetime.now():
rate = tmp_rec.json_data['rate']
if not rate:
# если с последней попытки меньше минуты - отдаем старый курс или None
if tmp_rec.modifiedDT + timedelta(minutes=5) > datetime.now():
if 'rate' in tmp_rec.json_data:
rate = tmp_rec.json_data['rate']
else:
request_required = False
if request_required:
if not rate:
rate = get_alfabank_nb_rate_by_currency_code(code)
# if not rate:
# rate = get_nbrb_rate_by_currency_code(code)
tmp_rec.modifiedDT = datetime.now()
tmp_rec.save()
except Exception as e:
msg = f'<b style="color : red;">!!!!! --- get_rate_nb_by_currency_code error={str(e)}</b>'
print(msg)
techSendMail(msg, 'tE get_rate_nb_by_currency_code error')
return rate

View File

@@ -0,0 +1,115 @@
import requests
import json
from datetime import datetime, timedelta
from BaseModels.mailSender import techSendMail
from GeneralApp.temp_data_funcs import *
def get_nbrb_currency_id_by_currency_code(code):
data = requests.get('https://www.nbrb.by/api/exrates/currencies')
json_data = json.loads(data.content)
for item in json_data:
if 'Cur_Abbreviation' in item and item['Cur_Abbreviation'] == code:
return item['Cur_Code']
return None
def get_nbrb_rate_by_currency_code(code, date=None):
# if code == 'BYN':
# return 1
#
# tmp_rec = get_tmp_data('currency_rate', code)
# if tmp_rec and tmp_rec.json_data:
# # если с момента последнего импорта прошло меньше 30 минут - забираем курс из базы
# if datetime.strptime(tmp_rec.json_data['DT'], '%d.%m.%Y %H:%M') + timedelta(minutes=30) > datetime.now():
# return tmp_rec.json_data['rate']
# currency_id = get_nbrb_currency_id_by_currency_code('USD')
rate = None
res = None
req_str = None
try:
msg = f'get_nbrb_rate_by_currency_code'
print(msg)
if not date:
# data = requests.get('https://www.nbrb.by/API/ExRates/Rates/{0}?Periodicity=0'.format(str(currency_id)))
req_str = 'https://www.nbrb.by/api/exrates/rates/{0}?parammode=2'.format(str(code))
else:
date_str = datetime.now().strftime('%Y-%m-%d')
date_str = date_str.replace('-0', '-')
req_str = 'https://www.nbrb.by/api/exrates/rates/{0}?parammode=2&ondate={1}'.format(
str(code),
date_str
)
e = None
try:
msg = f'GET {req_str}'
print(msg)
res = requests.get(req_str, timeout=3)
msg = f'answer received = {str(res)}'
print(msg)
except Exception as e:
msg = f'Exception GET {req_str} = {str(e)} ({str(res)})'
print(msg)
res = None
if not res and res != 200:
# if tmp_rec:
# rate = tmp_rec.json_data['rate']
# else:
# rate_Dict = {
# 'rate': 1,
# 'DT': datetime.now().strftime('%d.%m.%Y %H:%M')
# }
# create_or_update_tmp_data('currency_rate', code, rate_Dict)
# rate = 1
msg = '<b style="color : red;">!!!!! --- get_nbrb_rate_by_currency_code requests GET error={0}</b><br>{1}<br>{2}<br>rate set = {3}'.format(
str(e),
str(res),
str(req_str),
str(rate)
)
print(msg)
techSendMail(msg, 'tE get_nbrb_rate_by_currency_code error')
data = json.loads(res.content)
if data and 'Cur_OfficialRate' in data and 'Cur_Scale' in data:
rate = data['Cur_OfficialRate'] / data['Cur_Scale']
rate_Dict = {
'rate': rate,
'DT': datetime.now().strftime('%d.%m.%Y %H:%M')
}
create_or_update_tmp_data('currency_rate', code, rate_Dict)
except Exception as e:
msg = '<b style="color : red;">!!!!! --- get_nbrb_rate_by_currency_code error={0}</b><br>{1}<br>{2}'.format(
str(e),
str(res),
str(req_str)
)
print(msg)
techSendMail(msg, 'tE get_nbrb_rate_by_currency_code error')
# if not res:
# rate_Dict = {
# 'rate': 1,
# 'DT': datetime.now().strftime('%d.%m.%Y %H:%M')
# }
# create_or_update_tmp_data('currency_rate', code, rate_Dict)
# return 1
if rate:
msg = f'get nbrb nb rate = {rate}'
print(msg)
return rate

35
BaseModels/decorators.py Normal file
View File

@@ -0,0 +1,35 @@
from django.http import HttpResponse, JsonResponse
import json
# _make_result = lambda result: HttpResponse(json.dumps(result), mimetype='application/json')
_make_result = lambda result: JsonResponse(result)
def jsonifydata():
def decorator(func):
def wrapper(request, *args, **kwargs):
result = func(request, *args, **kwargs)
return HttpResponse(json.dumps(result), mimetype='application/json')
return wrapper
return decorator
def jsonify(validation_form=None):
def decorator(func):
def wrapper(request, *args, **kwargs):
if not validation_form is None:
form = validation_form(data=request.POST, files=request.FILES)
if form.is_valid():
request.form_data = form.cleaned_data
else:
return _make_result({'result': False, 'errors': form.errors})
# return _make_result({'result': func(request, *args, **kwargs)})
return _make_result(func(request, *args, **kwargs))
return wrapper
return decorator

View File

@@ -0,0 +1,67 @@
from tEDataProj.settings import EXCEPTION_IMPORT_LOG_PATH, EXCEPTION_LOG_PATH
import codecs
from datetime import datetime
def open_log_file(message, filename=None, import_exc=False):
if not filename:
if import_exc:
filename = u'import_errors.log'
else:
filename = u'errors.log'
if import_exc:
path = EXCEPTION_IMPORT_LOG_PATH
else:
path = EXCEPTION_LOG_PATH
f = codecs.open(path + filename, 'a', "utf-8")
msg = u'{0} - {1}\n---------------------------\n\n'.format(
str(datetime.now()),
message
)
f.write(msg)
return f
def close_log_file(f, message):
msg = u'---------------------------\n{0} - {1}\n\n'.format(
str(datetime.now()),
message
)
f.write(msg)
f.close()
return True
def save_log_string(f, exc_data):
msg = u'- {0} - {1} ({2})\n{3}\n'.format(
str(datetime.now()),
exc_data['err_code'],
exc_data['err_text'],
exc_data['err_data'],
)
f.write(msg)
return True
def generate_error(f, err_code, err_text, err_data):
exc_data = {
'err_code' : err_code,
'err_text' : err_text,
'err_data' : err_data
}
save_log_string(f, exc_data)
return exc_data

537
BaseModels/functions.py Normal file
View File

@@ -0,0 +1,537 @@
## -*- coding: utf-8 -*-
__author__ = 'SDE'
from django.utils.html import strip_tags
# from uuslug import slugify
import json
import os.path
from PIL import Image
from django.core.files.uploadedfile import InMemoryUploadedFile
from BaseModels.mailSender import techSendMail
from datetime import datetime, timedelta
def get_near_work_day(DT):
if DT.isoweekday() < 6:
return DT
return DT + timedelta(days=8 - DT.isoweekday())
def get_next_DT_for_monthes_delta_great(monthes_delta, fromDT=datetime.now()):
DT = fromDT
i = 0
cur_month = DT.month
while cur_month == DT.month:
DT = DT + timedelta(days=1)
# подбираем ближайший день, существующий в месяце
fail = True
i = 0
while fail:
try:
DT = DT.replace(day=fromDT.day - i)
fail = False
except:
i += 1
# DT = DT - timedelta(days=1)
# DT = DT.replace(hour=23, minute=59, second=59)
return DT
def get_prev_DT_for_monthes_delta_less(monthes_delta, fromDT=datetime.now()):
DT = fromDT
i = 0
while i < monthes_delta:
DT = DT.replace(day=1)
DT = DT - timedelta(days=1)
i += 1
# подбираем ближайший день, существующий в месяце
fail = True
i = 0
while fail:
try:
DT = DT.replace(day=fromDT.day - i)
fail = False
except:
i += 1
# DT = DT - timedelta(days=1)
# DT = DT.replace(hour=23, minute=59, second=59)
return DT
def correct_filter_name_for_filter_and_create(filter_kwargs):
filter_Dict = {}
create_Dict = {}
filter_Dict.update(filter_kwargs)
create_Dict.update(filter_kwargs)
if 'name' in filter_kwargs:
filter_Dict['name__iexact'] = filter_kwargs['name']
del filter_Dict['name']
if 'id' in filter_kwargs:
del filter_Dict['id']
del create_Dict['id']
return filter_Dict, create_Dict
def date_range_as_Dict(start_date, end_date):
import datetime
# for ordinal in range(start_date.toordinal(), end_date.toordinal()):
# yield datetime.date.fromordinal(ordinal)
return [{start_date + datetime.timedelta(n): {}} for n in range(int((end_date - start_date).days) + 1)]
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 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):
if request.query_params and 'img_type' in request.query_params:
return request.query_params['img_type']
return 'webp'
def image_convert_to_png(photo_file, save_file_path=None):
from io import BytesIO
from PIL import Image as Img
print('image_convert_to_png')
try:
fn_list = photo_file.name.split('.')
if len(fn_list) > 1:
fp = fn_list[0] + '.png'
else:
fp = photo_file.name + '.png'
image = Img.open(photo_file)
print('photo was uploaded')
try:
image.convert("RGB")
print('photo was converted to RGB')
except:
print('!!! fail convert photo to RGB')
if save_file_path:
image.save(save_file_path, format="PNG")
print('photo was saved')
fileBytes = BytesIO()
image.save(fileBytes, format="PNG")
print('photo was preparing for streaming')
memoryFile = InMemoryUploadedFile(fileBytes, None, fp, 'image/png', 1, None)
return memoryFile
except Exception as e:
msg = 'image_convert_to_png error={0}'.format(str(e))
print(msg)
techSendMail(msg, 'image_convert_to_png error')
return {'error': msg}
def image_convert_to_webP(photo_file, save_file_path=None):
from io import BytesIO
from PIL import Image as Img
fn_list = photo_file.name.split('.')
if len(fn_list) > 1:
webP_fp = fn_list[0] + '.webp'
else:
webP_fp = photo_file.name + '.webp'
image = Img.open(photo_file)
image.convert("RGB")
if save_file_path:
image.save(save_file_path, format="WEBP")
fileBytes = BytesIO()
image.save(fileBytes, format="WEBP")
memoryFile = InMemoryUploadedFile(fileBytes, None, webP_fp, 'image/webp', 1, None)
return memoryFile
def get_thumb_path(full_filepath, img_type):
if img_type == 'webp':
convert_to_webP = True
else:
convert_to_webP = False
icon_path = None
full_filepath = full_filepath.replace('\\', '/')
if not os.path.exists(full_filepath):
return None
path_list = full_filepath.split('/')
filename = path_list[-1]
filepath = '/'.join(path_list[:-1])
if convert_to_webP:
fn_list = filename.split('.')
if len(fn_list) > 1:
filename = fn_list[0] + '.webp'
else:
filename = filename + '.webp'
icon_path = '{0}/icon-{1}'.format(filepath, filename)
if not os.path.exists(icon_path):
size = (300, 300)
img = Image.open(full_filepath)
if convert_to_webP:
img.convert("RGB")
img.thumbnail(size)
if convert_to_webP:
img.save(icon_path, 'WEBP')
else:
img.save(icon_path)
return icon_path
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_free_filename(filename, filepath):
from os import path, access, R_OK # W_OK for write permission.
full_path = filepath + filename
i = 0
while path.exists(full_path) and path.isfile(full_path) and access(full_path, R_OK):
i += 1
full_path = filepath + filename + '-{0}'.format(str(i))
return full_path
def url_translit(value):
value = translit(value).lower()
# value = slugify_text(value).lower()
# value = value.replace(u',', u'-')
# value = value.replace(u'.', u'-')
# value = value.replace(u'_', u'-')
# value = value.replace(u'"', u'')
# value = value.replace(u'“', u'')
# value = value.replace(u'”', u'')
# value = value.replace(u"'", u'')
# value = value.replace(u'/', u'-')
# value = value.replace(u'\\', u'-')
# value = value.replace(u'(', u'')
# value = value.replace(u')', u'')
# value = value.replace(u'&', u'-and-')
# value = value.replace(u' ', u'-')
# value = value.replace(u'%', u'')
# value = value.replace(u'*', u'-')
# value = value.replace(u'±', u'-')
allow_symbols = '0123456789abcdefghijklmnopqrstuvwxyz-'
i = 0
while i < len(value):
if not value[i] in allow_symbols:
value = value.replace(value[i], '-')
i += 1
while '--' in value:
value = value.replace(u'--', u'-')
if value[len(value) - 1] == '-':
value = value[:-1]
return value
def translit(locallangstring):
conversion = {
u'\u0410': 'A', u'\u0430': 'a',
u'\u0411': 'B', u'\u0431': 'b',
u'\u0412': 'V', u'\u0432': 'v',
u'\u0413': 'G', u'\u0433': 'g',
u'\u0414': 'D', u'\u0434': 'd',
u'\u0415': 'E', u'\u0435': 'e',
u'\u0401': 'Yo', u'\u0451': 'yo',
u'\u0416': 'Zh', u'\u0436': 'zh',
u'\u0417': 'Z', u'\u0437': 'z',
u'\u0418': 'I', u'\u0438': 'i',
u'\u0419': 'Y', u'\u0439': 'y',
u'\u041a': 'K', u'\u043a': 'k',
u'\u041b': 'L', u'\u043b': 'l',
u'\u041c': 'M', u'\u043c': 'm',
u'\u041d': 'N', u'\u043d': 'n',
u'\u041e': 'O', u'\u043e': 'o',
u'\u041f': 'P', u'\u043f': 'p',
u'\u0420': 'R', u'\u0440': 'r',
u'\u0421': 'S', u'\u0441': 's',
u'\u0422': 'T', u'\u0442': 't',
u'\u0423': 'U', u'\u0443': 'u',
u'\u0424': 'F', u'\u0444': 'f',
u'\u0425': 'H', u'\u0445': 'h',
u'\u0426': 'Ts', u'\u0446': 'ts',
u'\u0427': 'Ch', u'\u0447': 'ch',
u'\u0428': 'Sh', u'\u0448': 'sh',
u'\u0429': 'Sch', u'\u0449': 'sch',
u'\u042a': '', u'\u044a': '',
u'\u042b': 'Y', u'\u044b': 'y',
u'\u042c': '', u'\u044c': '',
u'\u042d': 'E', u'\u044d': 'e',
u'\u042e': 'Yu', u'\u044e': 'yu',
u'\u042f': 'Ya', u'\u044f': 'ya',
u'': 'no',
}
translitstring = []
for c in locallangstring:
translitstring.append(conversion.setdefault(c, c))
return ''.join(translitstring)
def slugify_text(str_text):
utf8_code = False
try:
str_text = str_text.encode('utf-8').decode('utf-8')
utf8_code = True
except:
pass
if utf8_code == False:
try:
str_text = str_text.decode('utf-8')
except:
pass
str_text = del_bad_symbols(str_text)
str_text = str_text.replace(u'"', u'')
str_text = str_text.replace(u"'", u'')
str_text = str_text.replace(u".", u'')
str_text = str_text.replace(u",", u'')
str_text = str_text.replace(u" -", u'-')
str_text = str_text.replace(u"- ", u'-')
str_text = str_text.replace(u"", u'')
str_text = str_text.replace(u"(", u'')
str_text = str_text.replace(u")", u'')
str_text = str_text.replace(u"{", u'')
str_text = str_text.replace(u"}", u'')
str_text = str_text.replace(u"<", u'')
str_text = str_text.replace(u">", u'')
str = translit(str_text)
str = translit(str)
if len(str) < 2 or len(str) + 3 < len(str_text):
str = translit(str_text)
str = translit(str)
str = str.replace(u"'", u'')
str = str.replace(u'"', u'')
if len(str) < 2:
str = u''
return str
def get_price_from_string_w_del_tails(string):
string = del_bad_symbols(string)
while string.find(' ') > -1:
string = string.replace(' ', '')
string = string.replace(u'$', '')
string = string.replace(u'USD', '')
string = string.replace(u'Br', '')
string = string.replace(u'руб.', '')
string = string.replace(u',', '.')
return string
def kill_pretexts(txt):
pretexts = [
'в', 'без', 'до', 'из', 'к', 'на', 'по', 'о', 'от', 'перед', 'при', 'через', 'с', 'у', 'за', 'над',
'об', 'под', 'про', 'для'
]
words = txt.split(' ')
words = [item for item in words if not item in pretexts]
return ' '.join(words)
def stay_only_text_and_numbers(txt):
bad_symbols = '"~`{}[]|!@#$%^&*()_+№;:?= '
nums = '0123456789'
for symbol in bad_symbols:
txt = txt.replace(symbol, ' ')
symbols_for_check = ',.'
i = 0
while i < len(txt):
if txt[i] in ['.', ',']:
if i < 1 or not txt[i - 1] in nums or i == len(txt) - 1 or not txt[i + 1] in nums:
txt_list = list(txt)
txt_list[i] = ' '
txt = ''.join(txt_list)
# if txt[i] in ['"']:
# if i < 1 or not txt[i - 1] in nums:
# txt_list = list(txt)
# txt_list[i] = ' '
# txt = ''.join(txt_list)
i += 1
txt = txt.strip()
while ' ' in txt:
txt = txt.replace(' ', ' ')
return txt
def del_bad_symbols_and_enters_and_tags(string):
# from string import maketrans
try:
string = strip_tags(string)
string = string.replace('\r\n', '')
del_bad_symbols(string)
except:
pass
return string
def del_bad_symbols(string):
# from string import maketrans
try:
# string = strip_tags(string)
# string = string.replace('\r\n','')
string = string.strip()
while string.find('&nbsp;') > -1:
string = string.replace('&nbsp;', ' ')
# table = maketrans('&nbsp;', ' ')
# string = string.translate(table)
while string.find(' ') > -1:
string = string.replace(' ', ' ')
except:
pass
return string
# def get_offers_from_cookie(request):
# if 'oknaplast_right_offers' in request.COOKIES:
# order_list = json.loads(request.COOKIES['oknaplast_right_offers'], encoding='utf8')
# return WindowOfferModel.objects.filter(id__in=order_list)
# else:
# return []
def del_nbsp(string):
mapping = [
("&quot;", u'"'),
('&amp;', u'&'),
('&lt;', u'<'),
('&gt;', u'>'),
('&nbsp;', u' '),
('&iexcl;', u'¡'),
('&cent;', u'¢'),
('&pound;', u'£'),
('&curren;', u'¤'),
('&yen;', u'¥'),
('&brvbar;', u'¦'),
('&sect;', u'§'),
('&uml;', u'¨'),
('&copy;', u'©'),
('&ordf;', u'ª'),
('&laquo;', u'«'),
('&not;', u'¬'),
('&reg;', u'®'),
('&macr;', u'¯'),
('&deg;', u'°'),
('&plusmn;', u'±'),
('&sup2;', u'²'),
('&sup3;', u'³'),
('&acute;', u'´'),
('&micro;', u'µ'),
('&para;', u''),
('&middot;', u''),
('&cedil;', u'¸'),
('&sup1;', u'¹'),
('&ordm;', u'º'),
('&raquo;', u'»'),
('&frac14;', u'¼'),
('&frac12;', u'½'),
('&frac34;', u'¾'),
('&euro;', u''),
('\n', ''),
('\r', ''),
('\t', ' '),
('&mdash;', '-'),
]
for pair in mapping:
string = string.replace(pair[0], pair[1])
return string

189
BaseModels/inter.py Normal file
View File

@@ -0,0 +1,189 @@
# -*- coding: utf-8 -*-
from django.http import HttpResponse
import json
import csv
from .mailSender import techSendMail
import re
numbers = '0123456789.,'
def get_unique_url(model, name, url=None):
from .functions import url_translit
if not url:
url = url_translit(name)
try:
obj = model.objects.get(url=url)
except model.DoesNotExist:
return url
urls = model.objects.all().values_list('url', flat=True)
i = 1
while url in urls:
url = f'{url}-{i}'
i += 1
return url
def dicts_join(dict1, dict2, inplace=False):
result = dict1 if inplace else dict1.copy()
result.update(dict2)
return result
def set_ru_locale():
import locale
try:
locale.setlocale(locale.LC_ALL, 'ru_RU.utf8')
except Exception as e:
msg = '<b style="color : red;">!!!!! --- set_ru_locale exception error={0}<br>{1}</b>'.format(
str(e),
str(e.args)
)
print(msg)
techSendMail(msg, 'set_ru_locale')
return False
return True
def get_all_videos_from_html_content(html):
if not html:
return None
res = re.findall('iframe.*src=\"(.+?)\"', html)
return res
def get_all_photos_from_html_content(html):
res = re.findall('src=\"(.+?)\"', html)
return res
def get_choices_value_by_choices_id(choices, id):
for ch_id, ch_val in choices:
if ch_id == id:
return ch_val
return None
def sortByLength(inputStr):
return len(inputStr)
def get_current_language(request):
return request.LANGUAGE_CODE
def cut_to_number_w_point(string):
import re
if not string:
return string
string = string.replace(',', '.')
# str_list = string.split(',')
#
# if len(str_list) > 1:
# string = str_list[0]
# else:
# str_list = string.split('.')
# if len(str_list) > 2:
# string = u'{0}.{1}'.format(str_list[0], str_list[1])
try:
# шаблон для обрезки до цифр
p = '[0-9]+.[0-9]+'
number = u''.join(re.findall(p, string))
if number == u'':
p = '[0-9]+'
number = u''.join(re.findall(p, string))
except:
number = None
return number
def cut_to_number(string):
import re
if not string:
return string
# шаблон для обрезки до цифр
p = '[\d]+'
number = ''.join(re.findall(p, string))
return number
def range_dates(start, end):
""" Returns the date range """
from datetime import timedelta
list = [start + timedelta(days=days) for days in range(0, (end - start).days + 1)]
return list
# assert start <= end
# current = start.year * 12 + start.month - 1
# end = end.year * 12 + end.month - 1
# list = []
# while current <= end:
# yield date(current // 12, current % 12 + 1, 1)
# current += 1
# разбираем csv строку, получаем Dict
def get_Dict_from_csv_data(csv_data):
data = {}
for item in csv_data.split(';'):
try:
if item:
data.update(dict([item.split(':')[0:2]]))
except ValueError:
continue
return data
# return dict([item.split(':')[0:2] for item in csv_data.split(';') if item])
def cut_url_toPageName(url):
pageName = url.split('/')[-1] # получаем урл страницы
return pageName
def jsonify():
def decorator(func):
def wrapper(request, *args, **kwargs):
result = func(request, *args, **kwargs)
return HttpResponse(json.dumps(result), mimetype='application/json')
return wrapper
return decorator
def check_perms_for_view_order(request, order):
def decorator(func):
def wrapper(request, *args, **kwargs):
c_user = request.user
if order:
if c_user == order.user or c_user == order.forUser:
return True
else:
if c_user.has_perm('OrdersApp.can_see_orders_all_companys'):
return True
else:
if order.group == c_user.group and c_user.has_perm('OrdersApp.can_see_orders_self_company'):
return True
return wrapper
return decorator

31
BaseModels/json_funcs.py Normal file
View File

@@ -0,0 +1,31 @@
import json
def del_from_txt_bad_json_symbols(txt):
log = ''
error = True
while error and len(txt) > 0:
try:
json.loads(txt)
error = None
except json.JSONDecodeError as e:
msg = '- длина контента = {2} - {1} - удален символ {0}'.format(
txt[e.pos],
str(e),
str(len(txt)-1)
)
log = '{0}<br>{1}'.format(log, msg)
print(msg)
txt = txt[:e.pos] + txt[e.pos+1:]
error = e
# import re
# r_str = r'[{\[]([,:{}\[\]0-9.\-+A-zr-u \n\r\t]|".*:?")+[}\]]'
# pattern = re.compile(r_str)
# txt = re.sub(r_str, '',txt)
# res = pattern.search(txt)
# if res:
# txt = res.string
return txt, log

103
BaseModels/log/log_funcs.py Normal file
View File

@@ -0,0 +1,103 @@
from datetime import datetime, date
from django.db.models.fields.files import ImageFieldFile
from collections import OrderedDict
def send_mail_alert_w_data(obj, data, user=None):
# try:
#
# article = getattr(obj, 'article', None)
# if article and article in ('10751', '10752', '10753', '10754', '10801', '10802', '10803', '10804'):
# from BaseModels.mailSender import techSendMail
# msg = f'change product {article}<br>' \
# f'{datetime.now()}<br>' \
# f'{str(user)}<br>' \
# f'obj = {str(obj.__dict__)}<br>' \
# f'data = {str(data)}<br>'
# techSendMail(msg, 'tE checkpoint alert')
#
# except Exception as e:
# print(f'send_mail_alert_w_data ERROR = {str(e)}')
return True
def get_normalized_data(data):
if type(data) == OrderedDict:
data = dict(data)
if '_state' in data:
del data['_state']
if type(data) == dict:
for key, val in data.items():
if type(data[key]) in (dict, list, OrderedDict):
data[key] = get_normalized_data(val)
if type(data[key]) in (datetime, date, ImageFieldFile):
data[key] = str(val)
if type(data) == list:
i = 0
while i < len(data):
# if type(item) == OrderedDict:
# item = dict(item)
item = data[i]
if type(item) in (dict, list, OrderedDict):
data[i] = get_normalized_data(item)
if type(item) == dict:
for key, val in item.items():
if type(item[key]) in (datetime, date, ImageFieldFile):
item[key] = str(val)
elif type(item) == list:
ei = 0
while ei < len(item):
if type(item[ei]) in (datetime, date, ImageFieldFile):
item[ei] = str(item[ei])
ei += 1
i += 1
return data
def prepare_data_for_json(data):
data = get_normalized_data(data)
# if type(data) == OrderedDict:
# data = dict(data)
#
# if '_state' in data:
# del data['_state']
#
# if type(data) == dict:
# for key, val in data.items():
# if type(data[key]) in (datetime, date, ImageFieldFile):
# data[key] = str(val)
#
# if type(data) == list:
# for item in data:
# if type(item) == OrderedDict:
# item = dict(item)
#
# if type(item) == dict:
# for key, val in item.items():
# if type(data[key]) in (datetime, date, ImageFieldFile):
# item[key] = str(val)
#
# elif type(item) == list:
# for el in item:
# if type(el) in (datetime, date, ImageFieldFile):
# el = str(el)
return data

View File

@@ -0,0 +1,259 @@
from .mailSender import techSendMail
# def get_order_changes_list_for_template(order):
#
# if order.json_data and 'data_log' in order.json_data:
#
# i = 0
# changes_log = []
#
# if len(order.json_data['data_log']) > 0:
# first_log_record = order.json_data['data_log'][0]
#
# while i<len(order.json_data['data_log'])-1:
# dict1 = order.json_data['data_log'][i]['order_data']
# dict2 = order.json_data['data_log'][i+1]['order_data']
#
# res = {'order' : dict_compare(dict1, dict2)}
# rec_DT = res['order']['modified']['now_DT'][1]
# del res['order']['modified']['now_DT']
#
# # for item in res['order'].values():
# # if type(item) == set:
# # item = list(item)
#
# # dishes_set1 = order.json_data['data_log'][i]['dishes_data'])
# # dishes_set2 = set(order.json_data['data_log'][i+1]['dishes_data'])
# # res = dishes_set1.symmetric_difference(dishes_set2)
# # dict1 = {'dishes': order.json_data['data_log'][i]['dishes_data']}
# # dict2 = {'dishes': order.json_data['data_log'][i + 1]['dishes_data']}
# list1 = []
# list2 = []
# list1.extend(order.json_data['data_log'][i]['dishes_data'])
# list2.extend(order.json_data['data_log'][i + 1]['dishes_data'])
# # res['modified'].
# res.update({'dishes' : list_compare(list1, list2)})
# changes_log.append({rec_DT: res})
#
# i += 1
#
# return {
# 'changes_log' : changes_log,
# 'first_log_record' : first_log_record
# }
#
# return {}
def get_changes_for_Dicts(old_Dict, newDict):
res = {}
required_save = False
try:
# order_Dict = get_orderDict_by_order(order)
# if order.json_data and 'data_log' in order.json_data:
# last_rec = order.json_data['data_log'][-1]
res = dict_compare(old_Dict, newDict)
# del res['modified']['now_DT']
if res and (res['modified'] or res['added'] or res['removed']):
required_save = True
# res = dict_compare({'dishes' : last_rec['dishes_data']}, {'dishes' : order_Dict['dishes_data']})
# if res['modified'] or res['added'] or res['removed']:
# required_save = True
# else:
# required_save = True
# json_data = order.json_data
# if required_save:
# if not json_data:
# json_data = {}
# if not 'data_log' in json_data:
# json_data.update({'data_log' : []})
#
# json_data['data_log'].append(order_Dict)
except Exception as e:
msg = 'get_changes_for_Dicts Error = {0}<br>{1}'.format(str(e), str(e.args))
print(msg)
techSendMail(msg)
return res, required_save
def list_compare(old_data_list, new_data_list):
res = {
'added': [],
'removed': [],
'modified': [],
}
old_list = []
old_list.extend(old_data_list)
new_list = []
new_list.extend(new_data_list)
if new_list == old_list:
return {}
i2 = 0
list_w_id = False
while i2 < len(new_list):
req_del = False
i1 = 0
if 'id' in new_list[i2]:
list_w_id = True
while i1 < len(old_list):
if old_list[i1] == new_list[i2]:
req_del = True
else:
if old_list[i1]['id'] == new_list[i2]['id']:
if type(old_list[i1]) == dict:
res_dict_compare = dict_compare(old_list[i1], new_list[i2])
if 'property' in new_list[i2]:
res['modified'].append({new_list[i2]['property']['name']: res_dict_compare})
else:
res['modified'].append({new_list[i2]['id']: res_dict_compare})
elif type(old_list[i1]) in [tuple, list]:
res_list_compare = list_compare(old_list[i1], new_list[i2])
res['modified'].append(res_list_compare)
else:
res['modified'].append(new_list[i2])
req_del = True
# else:
# i1 += 1
if req_del:
del old_list[i1]
del new_list[i2]
break
else:
i1 += 1
else:
if not new_list[i2] in old_list:
if i2 < len(old_list):
res['modified'].append([old_list[i2], new_list[i2]])
del old_list[i2]
else:
res['modified'].append(new_list[i2])
del new_list[i2]
continue
else:
i2 += 1
req_del = True
# если не была найдена в обоих списках - значит добавлена
if not req_del:
res['added'].append(new_list[i2])
del new_list[i2]
# else:
# i2 += 1
if list_w_id and old_list:
res['removed'].extend(old_list)
if res['added'] or res['modified'] or res['removed']:
return res
return {}
def dict_compare(old_d2, new_d1):
added = []
removed = []
modified = []
if not old_d2 and not new_d1:
return {}
try:
if not old_d2 and new_d1:
added.append('None > ' + str(new_d1))
# for val, key in new_d1.items():
# added.update({key: (None, val)})
elif not new_d1 and old_d2:
# modified = (old_d2)
# removed = {}
# for val, key in old_d2.items():
# removed.update({key: (None, val)})
removed.append(str(old_d2) + ' > None')
else:
d1_keys = set(new_d1.keys())
d2_keys = set(old_d2.keys())
intersect_keys = d1_keys.intersection(d2_keys)
added = d1_keys - d2_keys
removed = d2_keys - d1_keys
modified = {}
for o in intersect_keys:
if new_d1[o] != old_d2[o]:
if type(new_d1[o]) == dict:
modified.update({
o: dict_compare(old_d2[o], new_d1[o])
})
elif type(new_d1[o]) in [list, tuple]:
modified.update({
o: list_compare(old_d2[o], new_d1[o])
})
else:
modified.update({
o: (old_d2[o], new_d1[o])
})
# modified = {o : (new_d1[o], old_d2[o]) for o in intersect_keys if new_d1[o] != old_d2[o]}
same = set(o for o in intersect_keys if new_d1[o] == old_d2[o])
# if not added:
# added = []
# if not removed:
# removed = []
# if not modified:
# modified = []
if added or removed or modified:
return {
'added': added,
'removed': removed,
'modified': modified,
# 'added' : list(added),
# 'removed' : list(removed),
# 'modified' : list(modified),
# 'same' : same
}
except Exception as e:
msg = 'dict_compare Error = {0}<br>{1}<br>{2}<br>{3}'.format(
str(e),
str(e.args),
old_d2,
new_d1
)
print(msg)
techSendMail(msg)
return {}

353
BaseModels/mailSender.py Normal file
View File

@@ -0,0 +1,353 @@
## -*- coding: utf-8 -*-
__author__ = 'SDE'
from django.core.mail import EmailMultiAlternatives
# from AuthApp.models import UserProfileModel
import smtplib
from tEDataProj.settings import prod_server
from email.mime.multipart import MIMEMultipart
from email.mime.text import MIMEText
from email.mime.application import MIMEApplication
from email.mime.multipart import MIMEMultipart
from os.path import basename
from email.mime.base import MIMEBase
from email import encoders
import ssl
import time
import random
from tEDataProj import settings
# tech@truenergy.by
# k7n2d3ZFZo4@CU5$4YDk
# administrator@truenergy.by
# 6&#WfW8$qR2w8uv69e5$
def fix_mailing_links_in_mail(html):
from GeneralApp.views import get_cur_domain
serv_domain, client_domain = get_cur_domain()
while 'src="/media/' in html:
html = html.replace('src="/media/', f'src="{serv_domain}media/')
return html
def mailing_direct_by_maillist(subject, from_email, to, html_content, attachments=None):
log = ''
for email in to:
res = admin_send_mail_by_SMTPlib(subject, from_email, email, html_content, attachments)
# print(str(res))
log = '{0}<br>{1}'.format(log, str(res))
time.sleep(random.randint(1, 5))
return log
def prepare_attach_file(filepath, filename=None):
try:
if not filename:
filename = basename(filepath)
if not settings.MEDIA_ROOT in filepath:
filepath = f'{settings.MEDIA_ROOT}{filepath}'
with open(filepath, "rb") as fil:
part = MIMEApplication(
fil.read(),
Name=filename
)
# After the file is closed
part['Content-Disposition'] = 'attachment; filename="%s"' % filename
except Exception as e:
msg = f'prepare_attach_file Error = {str(e)}'
techSendMail(msg, title='prepare_attach_file')
return msg
return part
def prepare_xls_attach_by_xls_virtual_file(virtual_file, filename):
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
# with open(filepath, 'rb') as fp:
file = MIMEBase(maintype, subtype) # Используем общий MIME-тип
file.set_payload(virtual_file) # Добавляем содержимое общего типа (полезную нагрузку)
# fp.close()
encoders.encode_base64(file) # Содержимое должно кодироваться как Base64
file.add_header('Content-Disposition', 'attachment', filename=filename)
return file
def admin_send_mail_by_SMTPlib(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)
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)
return str(res)
def send_mail_by_SMTPlib(subject, from_email, to_init, html_content, smtp_server, smtp_port, smtp_login, smtp_password,
attachments=None):
to = to_init
if not 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
time.sleep(1)
try:
# context = ssl.create_default_context()
mail_lib = smtplib.SMTP(smtp_server, smtp_port)
res = mail_lib.ehlo()
res = mail_lib.starttls() # context=context)
# print('mail_lib.starttls = {0}'.format(str(res)))
res = mail_lib.ehlo()
# print('mail_lib.ehlo = {0}'.format(str(res)))
res = mail_lib.set_debuglevel = 2
# print('mail_lib.set_debuglevel = {0}'.format(str(res)))
res = mail_lib.esmtp_features['auth'] = 'LOGIN PLAIN'
# print('mail_lib.esmtp_features = {0}'.format(str(res)))
res = mail_lib.login(smtp_login, smtp_password)
# print('mail_lib.login = {0}'.format(str(res)))
res = None
if type(to) in (list, tuple):
if 'support@truenergy.by' in to:
to.remove('support@truenergy.by')
if len(to) > 1:
to_str = u', '.join(to)
else:
to_str = to[0]
else:
if to == 'support@truenergy.by':
return None
to_str = to
to = []
to.append(to_str)
if type(subject) != str:
try:
subject = subject.decode('utf-8')
except:
try:
subject = subject.encode('utf-8')
except:
pass
msg = MIMEMultipart()
from email.headerregistry import Address
msg['From'] = from_email
msg['Reply-To'] = from_email
# msg['In-Reply-To'] = "email2@example.com"
msg['To'] = to_str
msg['Subject'] = subject
msg.attach(MIMEText(html_content, 'html', 'utf-8'))
# print('attach message complete')
if attachments:
if type(attachments) in (list, tuple):
try:
for item in attachments:
res = msg.attach(item)
# print('attach file complete = {0}'.format(str(res)))
except:
res = msg.attach(attachments)
# print('except attach file complete = {0}'.format(str(res)))
else:
res = msg.attach(attachments)
# print('else attach file complete = {0}'.format(str(res)))
res = mail_lib.sendmail(from_email, to, msg.as_string())
msg = mail_lib.quit()
# 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:
mail_lib.quit()
# print('mail_lib.quit = {0}'.format(str(msg)))
except:
pass
try:
print(str(mail_lib.__dict__))
except:
pass
return msg
# techSendMail(msg)
msg = 'send_mail_by_SMTPlib subj={3} init_to={2} to={0} res={1}'.format(str(to), str(res), str(to_init),
str(subject))
print(msg)
return msg
def sendMail(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)
# msg = EmailMultiAlternatives(subject, text_content, from_email, [to])
# msg.attach_alternative(html_content, "text/html")
# msg.send()
print(u'Accept')
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):
try:
print('techSendMail_for_specified_email_list')
if title:
subject = title
else:
subject = u'truEnergy Data техническое оповещение'
from_email = 'support@truenergy.by'
res = admin_send_mail_by_SMTPlib(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)
print(msg)
return 'Fail'
def techSendMail(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')
try:
# subject = u'truEnergy Data техническое оповещение'
from_email = 'support@truenergy.by'
to = ['web@syncsystems.net']
if add_emails:
to.extend(add_emails)
text_content = 'Technical message from truEnergy.'
if title:
subject = title
else:
subject = u'truEnergy Data техническое оповещение'
res = admin_send_mail_by_SMTPlib(subject, from_email, to, html_content)
print(res)
except Exception as e:
msg = 'techSendMail error={0}'.format(str(e))
# techSendMail(msg)
print(msg)
return u'Accept'

163
BaseModels/messages.py Normal file
View File

@@ -0,0 +1,163 @@
## -*- coding: utf-8 -*-
__author__ = 'SDE'
# from Baldenini_site.inter import jsonify
def get_error_message_Dict(show_icon=None):
print('get_error_message_Dict')
Dict = {
'form_style' : u'border-color: #FFBBBB; background-color: #FFEAEA;',
}
if show_icon:
Dict.update({
'form_icon' : 'canceled.png',
})
return Dict
def get_good_message_Dict(show_icon=None):
Dict = {
'form_style' : u'border-color: #BBFFBB; background-color: #EAFFEA;',
}
if show_icon:
Dict.update({
'form_icon' : 'accepted.png',
})
return Dict
def get_return_to_ready_but():
return {
'buttons' : u'<div class="button close">ГОТОВО</div>'
}
def get_return_to_choice_buts(but_ok_name, but_cancel_name):
return {
'buttons' : u'<div class="button ok">{0}</div>'
u'<div class="button cancel">{1}</div>'.format(but_ok_name, but_cancel_name)
}
def get_error_message(caption, text, show_icon=None):
Dict = {
'message' : text,
'caption' : caption
}
Dict.update(get_error_message_Dict(show_icon))
Dict.update(get_return_to_ready_but())
return Dict
# @jsonify()
def show_error_message(caption, text, show_icon=None):
from django.template.loader import render_to_string
return {'error':'error',
'html': render_to_string(
'm_show_message.html',
get_error_message(caption, text, show_icon)
)
}
# @jsonify()
def show_good_message(caption, text):
from django.template.loader import render_to_string
return {'html': render_to_string(
'm_show_message.html',
get_good_message(caption, text)
)
}
def show_good_message_ok_go_to_blank_page(caption, text, button_caption, url):
from django.template.loader import render_to_string
return {'html': render_to_string(
'm_show_message.html',
get_good_message_ok_go_to_blank_page(caption, text, button_caption, url)
)
}
# def show_choice_message_w_input(caption, text, but_ok_name, but_cancel_name, form):
# from django.template.loader import render_to_string
#
# return {'html': render_to_string(
# 'Messages/m_show_message.html',
# get_choice_message(caption, text, but_ok_name, but_cancel_name)
# )
# }
def show_choice_message_green(caption, text, but_ok_name, but_cancel_name, form=None):
from django.template.loader import render_to_string
return {'html': render_to_string(
'm_show_message.html',
get_choice_message(caption, text, but_ok_name, but_cancel_name, form, u'green')
)
}
def show_choice_message_red(caption, text, but_ok_name, but_cancel_name, form=None):
from django.template.loader import render_to_string
return {'html': render_to_string(
'm_show_message.html',
get_choice_message(caption, text, but_ok_name, but_cancel_name, form, u'red')
)
}
def get_choice_message(caption, text, but_ok_name, but_cancel_name, form=None, color=u'red', show_icon=None):
Dict = {
'message' : text,
'caption' : caption,
'form' : form
}
if color == u'red':
Dict.update(get_error_message_Dict(show_icon))
elif color == u'green':
Dict.update(get_good_message_Dict(show_icon))
Dict.update(get_return_to_choice_buts(but_ok_name, but_cancel_name))
return Dict
def get_but_ok_go_to_blank_page(button_caption, url):
return {
'buttons' : u'<p class="button_box"><a target="_blank" class="button close" href="/restaurant_control/users_control/show_users_report/{0}">{1}</a></p>'.format(url,button_caption)
}
def get_good_message_ok_go_to_blank_page(caption, text, button_caption, url, show_icon=None):
Dict = {
'message' : text,
'caption' : caption
}
Dict.update(get_good_message_Dict(show_icon))
Dict.update(get_but_ok_go_to_blank_page(button_caption, url))
return Dict
def get_good_message(caption, text, show_icon=None):
Dict = {
'message' : text,
'caption' : caption
}
Dict.update(get_good_message_Dict(show_icon))
Dict.update(get_return_to_ready_but())
return Dict

View File

@@ -0,0 +1,21 @@
from datetime import datetime
class WebRequestMiddleware:
def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
response = self.get_response(request)
if not '/v1/alert/' in request.path:
user = getattr(request, 'user', None)
if user and not user.is_anonymous and user.user_profile:
user.user_profile.last_web_request = datetime.now()
user.user_profile.save(update_fields=['last_web_request'])
# from GeneralApp.temp_data_funcs import add_element_in_tmp_data_list
# add_element_in_tmp_data_list('user_activity', user.email, 'activities_DT', str(user.user_profile.last_web_request))
return response

View File

@@ -0,0 +1,211 @@
import copy
from openpyxl import Workbook
from django.http import HttpResponse
from openpyxl.writer.excel import save_virtual_workbook
from openpyxl.utils import get_column_letter
from BaseModels.mailSender import techSendMail
from openpyxl.styles import PatternFill, Font, Alignment
from openpyxl.styles.borders import Border, Side
from openpyxl.styles.numbers import BUILTIN_FORMATS
from colorsys import rgb_to_hls
def pairwise(iterable):
a = iter(iterable)
return zip(a, a)
options_params_splitter = '<|>'
def set_col_options(ws, row, col, rows_len, options):
if type(options) == str:
options = dict(item.split("=")[::1] for item in options.split('&'))
cols_set = 1
if options:
exists_group_option_for_column = False
for key in options.keys():
if key.startswith('g_col_'):
exists_group_option_for_column = True
break
if 'cols_merge' in options and options['cols_merge']:
cols_set = int(options['cols_merge'])
if cols_set > 1:
ws.merge_cells(start_row=row, start_column=col, end_row=row, end_column=col + cols_set - 1)
if exists_group_option_for_column:
g_col_back_color = None
if 'g_col_back_color' in options and options['g_col_back_color']:
g_col_back_color = options['g_col_back_color']
g_col_num_w_sep = None
if 'g_col_num_w_sep' in options and options['g_col_num_w_sep']:
g_col_num_w_sep = options['g_col_num_w_sep']
cur_col = col
while cur_col < col + cols_set:
cur_row = row
while cur_row < rows_len:
if g_col_back_color:
ws.cell(row=cur_row, column=cur_col).fill = PatternFill('solid', fgColor=g_col_back_color)
if g_col_num_w_sep:
ws.cell(row=cur_row, column=cur_col).number_format = '#,##0.00'
cur_row += 1
cur_col += 1
if 'col_show_total' in options and options['col_show_total']:
ws.cell(row=rows_len, column=col).font = Font(bold=True)
ws.cell(row=rows_len, column=col).value = "=SUM({0}{1}:{0}{2})".format(
get_column_letter(col),
row + 1,
rows_len - 1
)
ws.cell(row=rows_len, column=col).number_format = '#,##0.00'
if 'back_color' in options and options['back_color']:
ws.cell(row=row, column=col).fill = PatternFill('solid', fgColor=options['back_color'])
if 'bold' in options and options['bold']:
ws.cell(row=row, column=col).font = Font(bold=True)
if 'col_bold' in options and options['col_bold']:
cur_col = col
while cur_col < col + cols_set:
cur_row = row
while cur_row < rows_len:
ws.cell(row=cur_row, column=cur_col).font = Font(bold=True)
cur_row += 1
cur_col += 1
return cols_set
def add_table_in_workbook(work_sheet, data, convert_minus_to_null=False, headers_rows_count=0):
thin_border = Border(left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin'))
r = 1
for row in data:
try:
c = 1
cols = row
if type(data) == dict:
cols = row.values()
for val in cols:
options = None
inc_c = 1
work_sheet.cell(row=r, column=c).border = thin_border
# получаем опции
if type(val) == str:
val_w_options = val.split(options_params_splitter)
if len(val_w_options) > 1:
val = val_w_options[0]
# применяем опции
inc_c = set_col_options(work_sheet, row=r, col=c, rows_len=len(data) + 1,
options=val_w_options[1])
elif type(val) == dict:
inc_c = set_col_options(work_sheet, row=r, col=c, rows_len=len(data) + 1, options=val)
val = val['val']
# если стоит опция "минусовые значения преобразовывать в нулевые"
if convert_minus_to_null:
try:
if val < 0:
val = 0
except:
pass
try:
work_sheet.cell(row=r, column=c).value = val
except:
work_sheet.cell(row=r, column=c).value = str(val)
c += inc_c
except Exception as e:
msg = f'add_table_in_workbook in row {str(r)} ERROR = {str(e)}'
print(msg)
r += 1
try:
dims = {}
row_c = 0
for row in work_sheet.rows:
# не подгоняем данные под надписи в хэдере
if row_c < headers_rows_count:
row_c += 1
continue
for cell in row:
if cell.value:
dims[cell.column] = max((dims.get(cell.column, 0), len(str(cell.value))))
row_c += 1
for col, value in dims.items():
if value > 150:
value = 150
if value < 3:
value = 3
work_sheet.column_dimensions[get_column_letter(col)].width = value
except Exception as e:
msg = f'add_table_in_workbook in sets width ERROR = {str(e)}'
print(msg)
return
def get_xls_file_by_data_list(data, convert_minus_to_null=False):
try:
wb = Workbook()
ws = wb.active
if type(data) == list and len(data) and type(data[0]) == dict:
i = 0
for page in data:
title = None
if 'title' in page:
title = page['title']
# если первая страница - она уже создана, просто переименовываем
if i == 0:
if title:
ws.title = title
else:
ws = wb.create_sheet(title)
headers_rows_count = 0
if 'headers_rows_count' in page:
headers_rows_count = page['headers_rows_count']
add_table_in_workbook(ws, page['table'], convert_minus_to_null, headers_rows_count)
i += 1
else:
add_table_in_workbook(ws, data, convert_minus_to_null)
xls_file = save_virtual_workbook(wb)
return xls_file
except Exception as e:
msg = str(e)
print(msg)
return msg

111
BaseModels/paging.py Normal file
View File

@@ -0,0 +1,111 @@
# -*- coding: utf-8 -*-
__author__ = 'SDE'
def get_paging_Dict(request, elements_count, elements_on_page, from_page, to_page=None):
pages_count = elements_count / elements_on_page
if elements_count % elements_on_page > 0:
pages_count = pages_count + 1
pages = []
if to_page:
cur_page = to_page
else:
cur_page = from_page
# количство страниц, которое отображается в ряд (без разделенного пэйджинга)
pages_wo_separated_paging = 20
if pages_count<pages_wo_separated_paging+1:
for page in range(1,pages_count+1):
if page==1:
pages.append((page, 1))
else:
pages.append((page, page))
else:
# количество страниц при разделенном пэйджинге в начале и конце пэйджинга, которые выводятся обязательно
pages_count_for_begin_end_paging = 3
for p in range(1,pages_count_for_begin_end_paging+1):
pages.append((p,p))
# pages.append((1,1))
# pages.append((2,2))
# pages.append((3,3))
# количество страниц, которые находятся в центре разделенного пэджинга
center_pages_count = 5
# количество страниц, при котором текущая страница будет находится в непрерывном ряду
# 1 2 3 4 5 6 7 ... 1241 1242 1243
# ^
pages_count_wo_separate_for_side = pages_count_for_begin_end_paging + center_pages_count+1
if cur_page < pages_count_wo_separate_for_side:
for p in range(pages_count_for_begin_end_paging+1,pages_count_wo_separate_for_side+1):
pages.append((p,p))
mid_rigth_page = (pages_count+1-pages_count_for_begin_end_paging-pages_count_wo_separate_for_side) / 2 #считаем сколько страниц внутри точек
mid_rigth_page = pages_count_wo_separate_for_side + mid_rigth_page # это и есть средняя страница в правой части
pages.append((u'...',mid_rigth_page))
# if num_current_page < 7:
# pages.append((4,4))
# pages.append((5,5))
# pages.append((6,6))
# pages.append((7,7))
# mid_rigth_page = (pages_count -2 -7) / 2 #считаем сколько страниц внутри точек
# mid_rigth_page = 7 + mid_rigth_page # это и есть средняя страница в правой части
# pages.append((u'...',mid_rigth_page))
elif cur_page>pages_count+1-pages_count_wo_separate_for_side:
mid_left_page = (pages_count+1-pages_count_for_begin_end_paging-pages_count_wo_separate_for_side) / 2 #считаем сколько страниц внутри точек
mid_left_page = pages_count_for_begin_end_paging + mid_left_page # это и есть средняя страница в левой части
pages.append((u'...',mid_left_page))
for p in range(pages_count+1-pages_count_wo_separate_for_side,pages_count-pages_count_for_begin_end_paging+1):
pages.append((p,p))
# elif num_current_page>pages_count-6:
# mid_left_page = (pages_count -2 -7) / 2 #считаем сколько страниц внутри точек
# mid_left_page = 3 + mid_left_page # это и есть средняя страница в левой части
# pages.append((u'...',mid_left_page))
# pages.append((pages_count-6,pages_count-6))
# pages.append((pages_count-5,pages_count-5))
# pages.append((pages_count-4,pages_count-4))
# pages.append((pages_count-3,pages_count-3))
else:
mid_page = cur_page
mid_left_page = (mid_page-1 -pages_count_for_begin_end_paging)/2 + pages_count_for_begin_end_paging
mid_rigth_page = (pages_count - (mid_page+pages_count_for_begin_end_paging))/2 + mid_page+1
# количество страниц, которые добавляются слева и и справа от текущей центральной
pages_count_for_add_to_left_and_right_from_current_central_page = center_pages_count / 2
pages.append((u'...',mid_left_page))
for p in range(mid_page-pages_count_for_add_to_left_and_right_from_current_central_page,mid_page+pages_count_for_add_to_left_and_right_from_current_central_page+1):
pages.append((p,p))
pages.append((u'...',mid_rigth_page))
# mid_left_page = (mid_page-1 -3)/2 + 3
# mid_rigth_page = (pages_count - (mid_page+1 +2))/2 + mid_page+1
#
# pages.append((u'...',mid_left_page))
# pages.append((mid_page-1,mid_page-1))
# pages.append((mid_page,mid_page))
# pages.append((mid_page+1,mid_page+1))
# pages.append((u'...',mid_rigth_page))
for p in range(pages_count+1-pages_count_for_begin_end_paging,pages_count+1):
pages.append((p,p))
# pages.append((pages_count-2,pages_count-2))
# pages.append((pages_count-1,pages_count-1))
# pages.append((pages_count,pages_count))
if not to_page:
to_page = from_page
return {
'paging' : pages,
'from_page' : from_page,
'to_page' : to_page,
'max_page' : pages_count,
}

View File

@@ -0,0 +1,32 @@
# -*- coding: utf-8 -*-
__author__ = 'SDE'
import os
from PIL import Image
def get_thumbnail_url(image_url, size=150):
thumbs_part = 'thumbs_' + str(size)
image_url_parts = image_url.rsplit('/', 1)
return image_url_parts[0] + '/' + thumbs_part + '/' + image_url_parts[1]
def get_thumbnail_path(image_path, size=150):
thumbs_dir = 'thumbs_' + str(size)
dirname, filename = os.path.split(image_path)
dirname = os.path.join(dirname, thumbs_dir)
if not os.path.exists(dirname):
os.mkdir(dirname, mode=0o755)
return os.path.join(dirname, filename)
def create_thumbnail(image_path, size=150):
thumb_path = get_thumbnail_path(image_path, size)
delete_thumbnail(image_path, size)
img = Image.open(image_path)
img.thumbnail((size, size), Image.ANTIALIAS)
img.save(thumb_path)
def delete_thumbnail(image_path, size=150):
thumb_path = get_thumbnail_path(image_path, size)
if os.path.exists(thumb_path):
os.remove(thumb_path)

123
BaseModels/search_funcs.py Normal file
View File

@@ -0,0 +1,123 @@
try:
import settings_local
pg_fts_config = 'pg_catalog.russian' # 'public.mipp_fulltext'
except:
pg_fts_config = 'pg_catalog.russian'
from django.db import models
from django.contrib.postgres.search import Value, Func
import copy
# получаем из списка только слова содержащие цифры
def get_list_words_contains_nums(txt):
from .inter import numbers
if type(txt) == str:
words = txt.split(' ')
else:
words = txt
words_w_nums = []
# получаем слова с цифрами
res_words = []
for word in words:
i = 0
while i < len(word):
if word[i] in numbers:
res_words.append(word)
break
i += 1
return res_words
# получаем список слов с разделенными цифрами и текстом
def get_list_split_words_w_nums(txt):
from .inter import numbers
if type(txt) == str:
words = txt.split(' ')
else:
words = txt
# words_w_nums = []
# получаем слова с цифрами
words_w_devided_nums = []
for word in copy.copy(words):
i = 0
is_number = False
cut_piece_compete = False
while i < len(word):
if i == 0:
if word[i] in numbers:
is_number = True
else:
is_number = False
else:
if word[i] in numbers:
if not is_number:
cut_piece_compete = True
else:
if is_number:
cut_piece_compete = True
if cut_piece_compete:
cut_piece_compete = False
words_w_devided_nums.append(word[0:i])
# if is_number:
# words_w_nums.append(word[0:i])
word = word[i:]
i = 0
else:
i += 1
if i > 0:
words_w_devided_nums.append(word[0:i])
# if is_number:
# words_w_nums.append(word[0:i])
return words_w_devided_nums
class Headline(Func):
function = 'ts_headline'
def __init__(self, field, query, config=None, options=None, **extra):
expressions = [field, query]
if config:
expressions.insert(0, Value(config))
if options:
expressions.append(Value(options))
extra.setdefault('output_field', models.TextField())
super(Headline, self).__init__(*expressions, **extra)
def get_search_lexems_list(search_phrase):
from django.db import connection
search_lexems_list = None
cursor = connection.cursor()
cursor.execute("SET NAMES 'UTF8';")
# cursor.execute(u"SET CHARACTER SET 'utf8';")
# cursor.execute(u"SET character_set_connection='utf8';")
cursor.execute("SELECT plainto_tsquery('{1}', '{0}');".format(search_phrase, pg_fts_config))
search_lexems = cursor.fetchone()
s = search_lexems[0] # .decode('utf8')
if search_lexems:
search_lexems = s.replace('\\', '')
search_lexems = search_lexems.replace("'", '')
search_lexems = search_lexems.replace(" ", '')
search_lexems_list = search_lexems.split('&')
return search_lexems_list

View File

@@ -0,0 +1,2 @@
# https://oembed.com/
# https://habr.com/ru/post/141303/

View File

@@ -0,0 +1 @@
# https://yandex.ru/dev/turbo/doc/quick-start/articles.html

View File

@@ -0,0 +1,2 @@
# https://amp.dev/ru/
# https://www.seonews.ru/analytics/optimization-2020-vnedrenie-amp-dlya-internet-magazina-bez-poteri-konversii-v-google/

View File

@@ -0,0 +1 @@
# https://developers.google.com/search/docs/advanced/appearance/enable-web-stories?hl=ru#google-discover

View File

@@ -0,0 +1 @@
https://developers.google.com/search/docs/beginner/seo-starter-guide?hl=ru#understand_your_content

View File

@@ -0,0 +1,38 @@
def get_ld_speakebale(name, theme_xpath, info_xpath, url):
data = {
"@context": "https://schema.org/",
"@type": "WebPage",
"name": name,
"speakable": {
"@type": "SpeakableSpecification",
"xPath": [
theme_xpath,
info_xpath
]
},
"url": url
}
return data
# <title>Speakable markup example</title>
# <meta name="description" content="This page is all about the quick brown fox" />
# <script type="application/ld+json">
# {
# "@context": "https://schema.org/",
# "@type": "WebPage",
# "name": "Quick Brown Fox",
# "speakable":
# {
# "@type": "SpeakableSpecification",
# "xPath": [
# "/html/head/title",
# "/html/head/meta[@name='description']/@content"
# ]
# },
# "url": "http://www.quickbrownfox_example.com/quick-brown-fox"
# }
# </script>

View File

@@ -0,0 +1,22 @@
def get_ld_FAQ(data_Dict):
FAQ_list = []
for key, val in data_Dict.items():
FAQ_list.append({
"@type": "Question",
"name": key,
"acceptedAnswer": {
"@type": "Answer",
"text": val
}
})
data = {
"@context": "https://schema.org",
"@type": "FAQPage",
"mainEntity": FAQ_list
}
return data

View File

@@ -0,0 +1,36 @@
def get_ld_QA(data_Dict):
data = {
"@context": "https://schema.org",
"@type": "QAPage",
"mainEntity": {
"@type": "Question",
"name": "How many ounces are there in a pound?",
"text": "I have taken up a new interest in baking and keep running across directions in ounces and pounds. I have to translate between them and was wondering how many ounces are in a pound?",
"answerCount": 3,
"upvoteCount": 26,
"acceptedAnswer": {
"@type": "Answer",
"text": "1 pound (lb) is equal to 16 ounces (oz).",
"upvoteCount": 1337,
"url": "https://example.com/question1#acceptedAnswer"
},
"suggestedAnswer": [
{
"@type": "Answer",
"text": "Are you looking for ounces or fluid ounces? If you are looking for fluid ounces there are 15.34 fluid ounces in a pound of water.",
"upvoteCount": 42,
"url": "https://example.com/question1#suggestedAnswer1"
}, {
"@type": "Answer",
"text": " I can't remember exactly, but I think 18 ounces in a lb. You might want to double check that.",
"upvoteCount": 0,
"url": "https://example.com/question1#suggestedAnswer2"
}
]
}
}
return data

View File

@@ -0,0 +1,41 @@
import json
import project_sets
from project_sets import *
from django.urls import reverse
from django.utils.html import strip_tags
def get_ld_article_news(art_name, art_txt, art_DT, url_data):
from BaseModels.inter import get_all_photos_from_html_content
img_list = get_all_photos_from_html_content(art_txt)
if img_list:
img_list = list(map(lambda img: "{0}{1}".format(project_sets.domain, img), img_list))
data = {
"@context": "https://schema.org",
"@type": "NewsArticle",
"url": "{0}{1}".format(project_sets.domain, reverse(**url_data)),
"publisher":{
"@type":"Organization",
"name": project_sets.company_name,
"logo": project_sets.logo
},
"author": {
"@type": "Organization",
"name": project_sets.company_name,
"logo": project_sets.logo,
"url": project_sets.domain,
},
"headline": art_name,
# "mainEntityOfPage": "http://www.bbc.com/news/world-us-canada-39324587", # ссылка на источник
"articleBody": strip_tags(art_txt),
"datePublished": art_DT.isoformat()
}
if img_list:
data.update({
'image': img_list
})
return json.dumps(data)

View File

@@ -0,0 +1,39 @@
import json
def get_ld_breadcrambs(items_list):
elements_list = []
i = 1
while i <= len(items_list):
item = items_list[i-1]
url = None
if type(item) == str:
name = item
elif type(item) == dict:
name = item['name']
url = item['url']
else:
name = item.name
url = item.url
Dict = {
"@type": "ListItem",
"position": i,
"name": name,
}
if i < len(items_list):
Dict.update({
"item": url
})
elements_list.append(Dict)
i += 1
data = {
"@context": "https://schema.org",
"@type": "BreadcrumbList",
"itemListElement": elements_list
}
return json.dumps(data)

View File

@@ -0,0 +1,243 @@
import json
import project_sets
from collections import OrderedDict
def get_ld_logo():
data = {
"@context": "https://schema.org",
"@type": "Organization",
"url": project_sets.domain,
"logo": project_sets.logo
}
return data
def get_ld_company(offices):
try:
main_office = offices.get(main_office=True)
except:
main_office = offices[0]
data = {
"@context": "https://schema.org",
"@type": "LocalBusiness",
"logo": project_sets.logo,
}
ld_for_main_office = get_ld_office(main_office)
data.update(ld_for_main_office)
departments = []
for office in offices:
if office == main_office:
continue
departments.append(get_ld_office(office))
# if departments:
# data.update({
# 'department': departments
# })
return json.dumps(data)
def get_ld_office(office):
try:
phones = office.phones()
except:
phones = []
if not phones:
try:
phones = office.rel_contacts_for_office
except:
phones = []
data = {
"name": office.name,
}
# На каждой странице (с разметкой или без нее) должно присутствовать хотя бы одно изображение. Робот Google выберет лучшее изображение для показа в результатах поиска с учетом соотношения сторон и разрешения.
# URL изображений должны быть доступны для сканирования и индексирования. Проверить, есть ли у поискового робота Google доступ к URL вашего контента, можно с помощью инструмента, описанного в этой статье.
# Изображения должны соответствовать размеченному контенту.
# Допускаются только графические файлы форматов, совместимых с Google Картинками.
# Предоставьте несколько изображений в высоком разрешении (не менее 50 000 пикселей по произведению ширины и высоты) со следующими соотношениями сторон: 16 × 9, 4 × 3 и 1 × 1.
data.update({
"image": [
project_sets.logo,
]
})
# data.update({
# "@type": "Store",
# })
# не обязательно!
# AnimalShelter
# ArchiveOrganization
# AutomotiveBusiness
# ChildCare
# Dentist
# DryCleaningOrLaundry
# EmergencyService
# EmploymentAgency
# EntertainmentBusiness
# FinancialService
# FoodEstablishment
# GovernmentOffice
# HealthAndBeautyBusiness
# HomeAndConstructionBusiness
# InternetCafe
# LegalService
# Library
# LodgingBusiness
# MedicalBusiness
# ProfessionalService
# RadioStation
# RealEstateAgent
# RecyclingCenter
# SelfStorage
# ShoppingCenter
# SportsActivityLocation
# Store
# TelevisionStation
# TouristInformationCenter
# TravelAgency
i_Dict = {
"address": {
"@type": "PostalAddress",
"streetAddress": office.address,
"addressLocality": office.city,
# "addressRegion": "CA",
# "postalCode": "95129",
# "addressCountry": "US"
},
}
if phones:
i_Dict["address"].update({
"telephone": '{0}{1}'.format(phones[0].prefix, phones[0].nomber_phone),
})
data.update(i_Dict)
gps_longitude = getattr(office, 'gps_longitude', None)
gps_latitude = getattr(office, 'gps_latitude', None)
if not gps_longitude:
gps_longitude = getattr(project_sets, 'gps_longitude', None)
if not gps_latitude:
gps_latitude = getattr(project_sets, 'gps_latitude', None)
if gps_longitude and gps_latitude:
i_Dict = {
"geo": {
"@type": "GeoCoordinates",
"latitude": gps_latitude,
"longitude": gps_longitude
},
}
data.update(i_Dict)
data.update({
"url": project_sets.domain
})
# "foundingDate": "2005-02-07", # дата основания
company_reference_links = getattr(project_sets, 'company_reference_links')
if company_reference_links:
data.update({
"sameAs": company_reference_links
})
priceRange = getattr(office, 'priceRange', '$')
if priceRange:
data.update({
"priceRange": priceRange
})
work_time_from = getattr(office, 'work_time_from', None)
if not work_time_from:
work_time_from = getattr(project_sets, 'work_time_from', '9:00')
work_time_to = getattr(office, 'work_time_to', None)
if not work_time_to:
work_time_to = getattr(project_sets, 'work_time_to', '18:00')
i_Dict = {
"openingHoursSpecification": [
{
"@type": "OpeningHoursSpecification",
"dayOfWeek": [
"https://schema.org/Monday",
"https://schema.org/Tuesday",
"https://schema.org/Wednesday",
"https://schema.org/Thursday",
"https://schema.org/Friday",
# "https://schema.org/Saturday"
],
"opens": work_time_from,
"closes": work_time_to
},
# {
# "@type": "OpeningHoursSpecification",
# "dayOfWeek": "Sunday",
# "opens": "08:00",
# "closes": "23:00"
# }
],
}
# i_Dict = {
# "openingHoursSpecification": [
# {
# "@type": "OpeningHoursSpecification",
# "dayOfWeek": "https://schema.org/Monday",
# "opens": work_time_from,
# "closes": work_time_to
# },
# {
# "@type": "OpeningHoursSpecification",
# "dayOfWeek": "https://schema.org/Tuesday",
# "opens": work_time_from,
# "closes": work_time_to
# },
# {
# "@type": "OpeningHoursSpecification",
# "dayOfWeek": "https://schema.org/Wednesday",
# "opens": work_time_from,
# "closes": work_time_to
# },
# {
# "@type": "OpeningHoursSpecification",
# "dayOfWeek": "https://schema.org/Thursday",
# "opens": work_time_from,
# "closes": work_time_to
# },
# {
# "@type": "OpeningHoursSpecification",
# "dayOfWeek": "https://schema.org/Friday",
# "opens": work_time_from,
# "closes": work_time_to
# },
# {
# "@type": "OpeningHoursSpecification",
# "dayOfWeek": "https://schema.org/Saturday",
# "opens": work_time_from,
# "closes": work_time_to
# },
# {
# "@type": "OpeningHoursSpecification",
# "dayOfWeek": "https://schema.org/Sunday",
# "opens": work_time_from,
# "closes": work_time_to
# },
# ],
# }
data.update(i_Dict)
return data

View File

@@ -0,0 +1,53 @@
import json
import re
import project_sets
from django.urls import reverse
from django.utils.html import strip_tags
def create_videoobject(video_path, name, description, DT):
video_id = video_path.split('/')[-1]
thumbs = list(map(lambda s: "https://img.youtube.com/vi/{0}/{1}.jpg".format(video_id, str(s)), range(1, 5)))
data = {
"@context": "https://schema.org",
"@type": "VideoObject",
"name": name,
"description": description,
"thumbnailUrl": thumbs,
"uploadDate": DT.isoformat(),
# "duration": "PT1M54S", # продолжительность видео
# "contentUrl": "https://www.example.com/video/123/file.mp4", # адрес к видеофайлу
"embedUrl": video_path,
# "interactionStatistic": { # количество просмотров
# "@type": "InteractionCounter",
# "interactionType": { "@type": "WatchAction" },
# "userInteractionCount": 5647018
# },
# "regionsAllowed": "US,NL" # разрешенные регионы
}
return data
def get_ld_videoobjects_for_page_html(obj, name, description, DT, content):
from BaseModels.inter import get_all_videos_from_html_content
res_list = []
if obj.video:
data = create_videoobject(obj.video, name, description, DT)
res_list.append(json.dumps(data))
if not content:
return res_list
videos_list = get_all_videos_from_html_content(content)
# if videos_list:
# img_list = list(map(lambda img: "{0}{1}".format(project_sets.domain, img), videos_list))
for video_path in videos_list:
if not video_path in obj.video and not obj.video in video_path:
data = create_videoobject(video_path, name, description, DT)
res_list.append(json.dumps(data))
return res_list

View File

@@ -0,0 +1,178 @@
# import json
#
# import project_sets
# from BaseModels.functions import add_domain
#
#
# def get_ld_shipping_data_for_product(shipping_terms):
# shipping_terms_list = []
# for item in shipping_terms:
# data = {
# "@type": "OfferShippingDetails",
# "shippingRate": {
# "@type": "MonetaryAmount",
# "value": item.price,
# "currency": project_sets.base_currency
# },
# "shippingDestination": {
# "@type": "DefinedRegion",
# "addressCountry": project_sets.shipping_region, # обязательно
# # "postalCodeRange": {
# # "postalCodeBegin": "98100",
# # "postalCodeEnd": "98199"
# # }
# },
# "deliveryTime": {
# "@type": "ShippingDeliveryTime",
# "cutOffTime": project_sets.cutOffTime, # "19:30-08:00",
#
# # Стандартное время от получения оплаты до отправки товаров со склада (или подготовки к самовывозу, если используется такой вариант)
# "handlingTime": {
# "@type": "QuantitativeValue",
# "minValue": "0", # дней
# "maxValue": "1" # дней
# },
# # Стандартное время от отправки заказа до его прибытия к конечному покупателю.
# "transitTime": {
# "@type": "QuantitativeValue",
# "minValue": "1", # дней
# "maxValue": "5" # дней
# },
# # Время, после которого новые заказы не обрабатываются в тот же день
#
# # Дни недели, по которым вы обрабатываете заказы
# "businessDays": {
# "@type": "OpeningHoursSpecification",
# "dayOfWeek": ["https://schema.org/Monday", "https://schema.org/Tuesday",
# "https://schema.org/Wednesday", "https://schema.org/Thursday"]
# }
# }
# }
#
# shipping_terms_list.append(data)
#
# data = {
# "shippingDetails": shipping_terms_list
# }
#
# return data
#
#
# def get_ld_offers_for_product(product, domain, shipping_terms):
# data = {
# "offers": {
# "@type": "Offer",
# "url": '{0}{1}'.format(domain, product.get_site_url()),
# "itemCondition": "https://schema.org/NewCondition",
# # "https://schema.org/NewCondition"
# # "https://schema.org/UsedCondition"
# "availability": "https://schema.org/InStock",
# # https://schema.org/BackOrder
# # https://schema.org/Discontinued
# # https://schema.org/InStock
# # https://schema.org/InStoreOnly
# # https://schema.org/LimitedAvailability
# # https://schema.org/OnlineOnly
# # https://schema.org/OutOfStock
# # https://schema.org/PreOrder
# # https://schema.org/PreSale
# # https://schema.org/SoldOut
# "price": str(product.price),
# "priceCurrency": project_sets.base_currency,
# # "priceValidUntil": "2020-11-20", #дата окончания действия цены
# # "shippingSettingsLink": '{0}{1}'.format(project_sets.domain, 'delivery/'),
#
# },
# }
#
# if shipping_terms:
# data["offers"].update(get_ld_shipping_data_for_product(shipping_terms))
#
# return data
#
#
# def get_aggregate_rating(product):
# data = {
# # "review": {
# # "@type": "Review",
# # "reviewRating": {
# # "@type": "Rating",
# # "ratingValue": "4",
# # "bestRating": "5"
# # },
# # "author": {
# # "@type": "Person",
# # "name": "Fred Benson"
# # }
# # },
# "aggregateRating": {
# "@type": "AggregateRating",
# "ratingValue": product.ratingValue,
# "reviewCount": product.reviewCount
# }
# }
#
# return data
#
#
# def get_ld_product(product, domain, shipping_terms):
# from GeneralApp.views import get_cur_domain
# serv_domain, local_domain = get_cur_domain()
#
# data = {
# "@context": "https://schema.org/",
# "@type": "Product",
# "name": product.name,
# "sku": '{0}-{1}'.format(str(product.brand), str(product.article)),
# "url": '{0}{1}'.format(domain, product.get_site_url()),
# }
#
# if product.description:
# data.update({
# "description": product.description,
# })
#
# barcode = getattr(product, 'barcode', None)
# if barcode:
# data.update({
# "gtin14": barcode,
# })
#
# gallery = getattr(product, 'gallery', None)
# if gallery:
# try:
# photos = gallery.get_photos()
# photos = list(map(lambda ph: '{0}{1}'.format(serv_domain, ph), photos))
# except Exception as e:
# photos = None
#
# if photos:
# data.update({
# "image": photos,
# })
#
# brand = getattr(product, 'brand', None)
# if brand:
# if type(brand) not in [str]:
# brand = brand.name
#
# data.update({
# "brand": {
# "@type": "Brand",
# "name": brand
# },
# })
#
# FAQ = {}
#
# from ...
#
# aggregate_rating = getattr(product, 'ratingValue', None)
# if aggregate_rating != None:
# data.update(get_aggregate_rating(product))
#
# price = getattr(product, 'price', None)
# if price:
# data.update(get_ld_offers_for_product(product, domain, shipping_terms))
#
# return json.dumps(data)

View File

@@ -0,0 +1,22 @@
import json
import project_sets
def get_ld_search(domain):
# Только для главной страницы
data = {
"@context": "https://schema.org",
"@type": "WebSite",
"url": domain, #"https://truenergy.by/",
"potentialAction": {
"@type": "SearchAction",
"target": {
"@type": "EntryPoint",
"urlTemplate": "{domain}/{search_term_string}/".format(domain=domain, search_term_string='{search_term_string}')
},
"query-input": "required name=search_term_string"
}
}
return json.dumps(data)

View File

@@ -0,0 +1,140 @@
import datetime
import project_sets
def get_ld_vacancies(data_Dict):
# Разметку JobPosting можно размещать только на страницах, которые содержат одно объявление о вакансии.
# Не разрешается добавлять разметку JobPosting на какие-либо другие страницы, в том числе те, на которых нет информации ни об одной вакансии.
vacancies_list = []
for item in data_Dict:
data = {
"@context": "https://schema.org/",
"@type": "JobPosting",
"title": item['title'],
"description": item['description'],
"datePosted": datetime.datetime.now().strftime('%Y-%m-%d'),
"validThrough": item['validThrough'].strftime('%Y-%m-%dT%H:%M'), #"2017-03-18T00:00", # окончание срока действия
"identifier": {
"@type": "PropertyValue",
"name": project_sets.company_name,
"value": str(item['id'])
},
"hiringOrganization": {
"@type": "Organization",
"name": project_sets.company_name,
"sameAs": project_sets.domain,
"logo": project_sets.logo
},
}
if 'office' in item:
# используется для указания места, в котором сотрудник будет выполнять работу. Если определенного места (например, офиса или производственной площадки) нет, использовать это свойство не обязательно.
job_place_Dict = {
"jobLocation": {
"@type": "Place",
"address": {
"@type": "PostalAddress",
"streetAddress": item['office'].address,
"addressLocality": item['office'].city,
"addressCountry": "BY"
},
},
}
else:
job_place_Dict = {
"jobLocationType": "TELECOMMUTE" # только удаленка
}
data.update(job_place_Dict)
if 'required_country_of_residence' in item:
# используется для указания территории, на которой может проживать кандидат на должность. Необходимо, чтобы была задана по меньшей мере одна страна
required_country_of_residence = {
"applicantLocationRequirements": {
"@type": "Country",
"name": item['required_country_of_residence']['country']
},
}
data.update(required_country_of_residence)
if 'salary' in item:
salary_Dict = {
"baseSalary": {
"@type": "MonetaryAmount",
"currency": item['salary']['currency'],
"value": {
"@type": "QuantitativeValue",
"unitText": item['salary']['time_unit']
# HOUR
# DAY
# WEEK
# MONTH
# YEAR
}
}
}
if 'price' in item['salary']:
salary_Dict['baseSalary']['value']['value'] = item['salary']['price']
elif 'price_from' in item['salary']:
salary_Dict['baseSalary']['value']['minValue'] = item['salary']['price_from']
if 'price_to' in item['salary']:
salary_Dict['baseSalary']['value']['maxValue'] = item['salary']['price_to']
data.update(salary_Dict)
# Указание на то, поддерживается ли на странице с объявлением о вакансии отправка резюме напрямую.
data.update({
'directApply': item['directApply']
})
# Вид занятости Укажите одно или несколько значений
if 'employmentType' in item:
# FULL_TIME
# PART_TIME
# CONTRACTOR
# TEMPORARY
# INTERN
# VOLUNTEER
# PER_DIEM
# OTHER
data.update({
'employmentType': item['employmentType']
})
if 'educationRequirements' in item:
e_Dict = {
"educationRequirements": {
"@type": "EducationalOccupationalCredential",
"credentialCategory": item['educationRequirements']
# high school
# associate degree
# bachelor degree
# professional certificate
# postgraduate degree
},
}
data.update(e_Dict)
if 'experienceRequirements' in item:
e_Dict = {
"experienceRequirements": {
"@type": "OccupationalExperienceRequirements",
"monthsOfExperience": item['experienceRequirements'] # опыт работы в месяцах
},
}
data.update(e_Dict)
# Со значением "истина" это свойство будет указывать на то, что кандидатам достаточно иметь опыт, если у них нет требуемого образования
if 'required_only_experience' in item:
if 'experienceRequirements' in item and 'educationRequirements' in item:
data.update({
'experienceInPlaceOfEducation': item['required_only_experience']
})
vacancies_list.append(data)
return vacancies_list

View File

@@ -0,0 +1 @@
# https://yandex.ru/dev/turbo-shop/doc/quick-start/markets.html

View File

@@ -0,0 +1 @@
https://yandex.ru/support/webmaster/index.html

View File

@@ -0,0 +1,258 @@
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

View File

@@ -0,0 +1,15 @@
<div class="modal">
<div class="alert-window"{% if order %} data-order_id="{{ order.id }}" data-pay_type="{{ order.payType }}"{% endif %} style="{{ form_style|safe }}">
<img class="stat-img" src="/static/img/{{ form_icon }}"/>
<p class="caption">{{ caption|safe }}</p>
<p class="message">{{ message|safe }}</p>
{% if form %}
<div class="fieldset">
{% for item in form %}
{{ item }}
{% endfor %}
</div>
{% endif %}
{{ buttons|safe }}
</div>
</div>

View File

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

View File

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

0
FirePlayProj/__init__.py Normal file
View File

16
FirePlayProj/asgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
ASGI config for FirePlayProj project.
It exposes the ASGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/
"""
import os
from django.core.asgi import get_asgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'FirePlayProj.settings')
application = get_asgi_application()

252
FirePlayProj/settings.py Normal file
View File

@@ -0,0 +1,252 @@
"""
Django settings for FirePlayProj project.
Generated by 'django-admin startproject' using Django 4.2.1.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/topics/settings/
For the full list of settings and their values, see
https://docs.djangoproject.com/en/4.2/ref/settings/
"""
from pathlib import Path
# Build paths inside the project like this: BASE_DIR / 'subdir'.
BASE_DIR = Path(__file__).resolve().parent.parent
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'django-insecure-k=2q3&t1pufsxpu#)0hfd(#!9%horaq$krbbxm=7*w$0x5(h1b'
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'ckeditor',
'ckeditor_uploader',
'AuthApp',
'QuestionsApp',
'GameApp',
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'FirePlayProj.urls'
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / 'templates']
,
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.debug',
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
WSGI_APPLICATION = 'FirePlayProj.wsgi.application'
# Database
# https://docs.djangoproject.com/en/4.2/ref/settings/#databases
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql_psycopg2',
'NAME': 'fireGameDB',
'USER': 'test_user',
'PASSWORD': 'test_db_pass',
'HOST': '127.0.0.1',
'PORT': '5432',
}
}
# Password validation
# https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
},
{
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
},
]
# Internationalization
# https://docs.djangoproject.com/en/4.2/topics/i18n/
LANGUAGE_CODE = 'ru-RU'
TIME_ZONE = 'Europe/Minsk'
USE_I18N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/4.2/howto/static-files/
MEDIA_URL = '/media/'
MEDIA_ROOT = 'media/'
STATIC_URL = '/static/'
STATIC_ROOT = '/'
# Default primary key field type
# https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
gettext = lambda s: s
LANGUAGES = (
(u'ru', gettext(u'Russian')),
(u'en', gettext(u'English')),
# (u'de', gettext(u'Deutsch')),
# (u'fr', gettext(u'Francais')),
)
MODELTRANSLATION_LANGUAGES = ('ru', 'en')
MODELTRANSLATION_ENABLE_FALLBACKS = True
MODELTRANSLATION_FALLBACK_LANGUAGES = {
'default': ('ru','en'),
'ru': ('ru','en'),
'en': ('en', 'ru'),
}
# Add custom languages not provided by Django
import django.conf.locale
LANG_INFO = dict(django.conf.locale.LANG_INFO.items())
django.conf.locale.LANG_INFO = LANG_INFO
CKEDITOR_BASEPATH = "/static/ckeditor/ckeditor/"
CKEDITOR_UPLOAD_PATH = "uploads/"
CKEDITOR_IMAGE_BACKEND = "pillow"
# CKEDITOR_BROWSE_SHOW_DIRS = True
CKEDITOR_JQUERY_URL = 'https://ajax.googleapis.com/ajax/libs/jquery/2.2.4/jquery.min.js'
CKEDITOR_OPTIONS = {
'height': 291,
'width': '95%',
'filebrowserWindowHeight': 600,
'filebrowserWindowWidth': "100%",
'toolbar': 'YourCustomToolbarConfig',
'allowedContent': True,
'enterMode': 2,
'basicEntities' : False,
'entities_additional': '',
'entities' : False,
'htmlEncodeOutput' : False,
'toolbar_Basic': [
['Source', '-', 'Bold', 'Italic']
],
'toolbar_YourCustomToolbarConfig': [
{'name': 'document', 'items': ['Source', '-', 'Save', 'NewPage', 'Preview', 'Print', '-', 'Templates']},
{'name': 'clipboard', 'items': ['Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo']},
{'name': 'editing', 'items': ['Find', 'Replace', '-', 'SelectAll']},
{'name': 'forms',
'items': ['Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton',
'HiddenField']},
'/',
{'name': 'basicstyles',
'items': ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat']},
{'name': 'paragraph',
'items': ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-',
'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl',
'Language']},
{'name': 'links', 'items': ['Link', 'Unlink', 'Anchor']},
{'name': 'insert',
'items': ['Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe']},
'/',
{'name': 'styles', 'items': ['FontSize']},
{'name': 'colors', 'items': ['TextColor', 'BGColor']},
{'name': 'tools', 'items': ['Maximize', 'ShowBlocks']},
# {'name': 'about', 'items': ['About']},
'/', # put this to force next toolbar on new line
# {'name': 'yourcustomtools', 'items': [
# # put the name of your editor.ui.addButton here
# 'Preview',
# 'Maximize',
#
# ]},
],
'tabSpaces': 4,
'removePlugins': 'stylesheetparser',
# 'extraPlugins': ','.join([
# 'uploadimage', # the upload image feature
# # your extra plugins here
# 'div',
# 'autolink',
# 'autoembed',
# 'embedsemantic',
# 'autogrow',
# # 'devtools',
# 'widget',
# 'lineutils',
# 'clipboard',
# 'dialog',
# 'dialogui',
# 'elementspath'
# ]),
}
try:
from tEDataProj import db_local_sets
DATABASES = db_local_sets.DATABASES
except ImportError as e:
pass
# global prod_server
try:
from settings_local import *
prod_server = True
except ImportError:
prod_server = False

27
FirePlayProj/urls.py Normal file
View File

@@ -0,0 +1,27 @@
"""
URL configuration for FirePlayProj project.
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/4.2/topics/http/urls/
Examples:
Function views
1. Add an import: from my_app import views
2. Add a URL to urlpatterns: path('', views.home, name='home')
Class-based views
1. Add an import: from other_app.views import Home
2. Add a URL to urlpatterns: path('', Home.as_view(), name='home')
Including another URLconf
1. Import the include() function: from django.urls import include, path
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
"""
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
from django.conf import settings
urlpatterns = [
path('admin/', admin.site.urls),
path('ckeditor/', include('ckeditor_uploader.urls')),
]
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

16
FirePlayProj/wsgi.py Normal file
View File

@@ -0,0 +1,16 @@
"""
WSGI config for FirePlayProj project.
It exposes the WSGI callable as a module-level variable named ``application``.
For more information on this file, see
https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/
"""
import os
from django.core.wsgi import get_wsgi_application
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'FirePlayProj.settings')
application = get_wsgi_application()

0
GameApp/__init__.py Normal file
View File

16
GameApp/admin.py Normal file
View File

@@ -0,0 +1,16 @@
from django.contrib import admin
from BaseModels.admin_utils import Admin_BaseIconModel
from .models import *
class Admin_Game(Admin_BaseIconModel):
pass
admin.site.register(Game, Admin_Game)
class Admin_UserInGame(Admin_BaseIconModel):
pass
admin.site.register(UserInGame, Admin_UserInGame)
class Admin_QuestionInGameForUser(Admin_BaseIconModel):
pass
admin.site.register(QuestionInGameForUser, Admin_QuestionInGameForUser)

6
GameApp/apps.py Normal file
View File

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

View File

@@ -0,0 +1,84 @@
# Generated by Django 4.2.1 on 2023-05-16 14:01
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
('QuestionsApp', '0001_initial'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Game',
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='Дополнительные данные')),
('level', models.IntegerField(default=1, verbose_name='Уровень игры')),
('status', models.CharField(default='waiting_users', max_length=100, verbose_name='Статус')),
('time_for_waiting_users', models.IntegerField(default=15, verbose_name='Время ожидания пользователей (сек)')),
('cur_lap', models.IntegerField(default=0, verbose_name='Текущий круг')),
('comment', models.TextField(blank=True, null=True, verbose_name='Комментарий')),
('winner', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='rel_games_for_user', to=settings.AUTH_USER_MODEL, verbose_name='id пользователя')),
],
options={
'verbose_name': 'Игра',
'verbose_name_plural': 'Игры',
},
),
migrations.CreateModel(
name='UserInGame',
fields=[
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('name', models.TextField(blank=True, help_text='Название', null=True, verbose_name='Название')),
('name_plural', models.TextField(blank=True, null=True, verbose_name='Название (множественное число)')),
('order', models.IntegerField(blank=True, null=True, verbose_name='Очередность отображения')),
('createDT', models.DateTimeField(auto_now_add=True, verbose_name='Дата и время создания')),
('modifiedDT', models.DateTimeField(blank=True, null=True, verbose_name='Дата и время последнего изменения')),
('enable', models.BooleanField(db_index=True, default=True, verbose_name='Включено')),
('json_data', models.JSONField(blank=True, default=dict, verbose_name='Дополнительные данные')),
('last_lap', models.IntegerField(default=0, verbose_name='Последний круг')),
('status', models.CharField(default='waiting_users', max_length=100, verbose_name='Статус')),
('game', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='rel_userInGame_for_game', to='GameApp.game', verbose_name='Игра')),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='rel_userInGame_for_user', to=settings.AUTH_USER_MODEL, verbose_name='Игрок')),
],
options={
'verbose_name': 'Игрок',
'verbose_name_plural': 'Игроки',
},
),
migrations.CreateModel(
name='QuestionInGameForUser',
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='Дополнительные данные')),
('status', models.CharField(default='wait', max_length=100, verbose_name='Статус')),
('answer_right', models.BooleanField(default=False, verbose_name='Верный ответ')),
('use_time_for_answer', models.IntegerField(default=0, verbose_name='Потрачено времени на ответ (сек)')),
('question', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='rel_questionInGame_for_question', to='QuestionsApp.question', verbose_name='Вопрос')),
('user_in_game', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='rel_questionInGame_for_userInGame', to='GameApp.useringame', verbose_name='Игра')),
],
options={
'verbose_name': 'Игрок',
'verbose_name_plural': 'Игроки',
},
),
]

View File

@@ -0,0 +1,21 @@
# Generated by Django 4.2.1 on 2023-05-16 14:07
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('GameApp', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='questioningameforuser',
options={'verbose_name': 'Вопрос в игре', 'verbose_name_plural': 'Вопросы в игре'},
),
migrations.AlterModelOptions(
name='useringame',
options={'verbose_name': 'Пользователь в игре', 'verbose_name_plural': 'Пользователи в игре'},
),
]

View File

94
GameApp/models.py Normal file
View File

@@ -0,0 +1,94 @@
from django.db import models
from BaseModels.base_models import BaseModel
from django.utils.translation import gettext_lazy as _
from AuthApp.models import User
game_status_choices = (
('waiting_users', 'Ожидание пользователей'),
('game', 'В процессе'),
('finished', 'Завершена')
)
class Game(BaseModel):
level = models.IntegerField(verbose_name=_('Уровень игры'), default=1)
status = models.CharField(max_length=100, verbose_name=_('Статус'), default='waiting_users')
time_for_waiting_users = models.IntegerField(verbose_name=_('Время ожидания пользователей (сек)'), default=15)
cur_lap = models.IntegerField(verbose_name=_('Текущий круг'), default=0)
winner = models.OneToOneField(User, verbose_name=_(u'id пользователя'), related_name='rel_games_for_user',
null=True, blank=True, on_delete=models.CASCADE)
comment = models.TextField(verbose_name=_('Комментарий'), null=True, blank=True)
def __str__(self):
if self.name:
return f'{self.name}'
else:
return str(self.id)
class Meta:
verbose_name = _('Игра')
verbose_name_plural = _('Игры')
# ordering = ('user__last_name', 'user__first_name')
user_in_game_status_choices = (
('waiting_users', 'Ожидание пользователей'),
('in_game', 'В игре'),
('lose', 'Выбыл'),
('finish', 'Завершил игру')
)
class UserInGame(BaseModel):
game = models.OneToOneField(
Game, verbose_name=_('Игра'), related_name='rel_userInGame_for_game', on_delete=models.CASCADE)
user = models.OneToOneField(
User, verbose_name=_('Игрок'), related_name='rel_userInGame_for_user', on_delete=models.CASCADE)
last_lap = models.IntegerField(verbose_name=_('Последний круг'), default=0)
status = models.CharField(max_length=100, verbose_name=_('Статус'), default='waiting_users')
def __str__(self):
if self.name:
return f'{self.name}'
else:
return str(self.id)
class Meta:
verbose_name = _('Пользователь в игре')
verbose_name_plural = _('Пользователи в игре')
# ordering = ('question')
question_in_game_status_choices = (
('wait', 'Ожидание ответа'),
('answered', 'Ответил'),
)
class QuestionInGameForUser(BaseModel):
from QuestionsApp.models import Question
user_in_game = models.OneToOneField(
UserInGame, verbose_name=_('Игра'), related_name='rel_questionInGame_for_userInGame', on_delete=models.CASCADE)
question = models.OneToOneField(
Question, verbose_name=_('Вопрос'), related_name='rel_questionInGame_for_question', on_delete=models.CASCADE)
status = models.CharField(max_length=100, verbose_name=_('Статус'), default='wait')
answer_right = models.BooleanField(verbose_name=_('Верный ответ'), default=False)
use_time_for_answer = models.IntegerField(verbose_name=_('Потрачено времени на ответ (сек)'), default=0)
def __str__(self):
if self.name:
return f'{self.name}'
else:
return str(self.id)
class Meta:
verbose_name = _('Вопрос в игре')
verbose_name_plural = _('Вопросы в игре')
# ordering = ('question')

3
GameApp/tests.py Normal file
View File

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

3
GameApp/views.py Normal file
View File

@@ -0,0 +1,3 @@
from django.shortcuts import render
# Create your views here.

0
QuestionsApp/__init__.py Normal file
View File

11
QuestionsApp/admin.py Normal file
View File

@@ -0,0 +1,11 @@
from django.contrib import admin
from BaseModels.admin_utils import Admin_BaseIconModel
from .models import *
class Admin_Question(Admin_BaseIconModel):
pass
admin.site.register(Question, Admin_Question)
class Admin_Answer(Admin_BaseIconModel):
pass
admin.site.register(Answer, Admin_Answer)

6
QuestionsApp/apps.py Normal file
View File

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

View File

@@ -0,0 +1,58 @@
# Generated by Django 4.2.1 on 2023-05-16 14:01
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
]
operations = [
migrations.CreateModel(
name='Question',
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='Дополнительные данные')),
('game_level', models.IntegerField(default=1, verbose_name='Уровень игры')),
('time_for_answer', models.IntegerField(default=7, verbose_name='Время на ответ (сек)')),
('used_count', models.IntegerField(default=0, verbose_name='Количество использования')),
('comment', models.TextField(blank=True, null=True, verbose_name='Комментарий')),
],
options={
'verbose_name': 'Вопрос',
'verbose_name_plural': 'Вопросы',
'permissions': (),
},
),
migrations.CreateModel(
name='Answer',
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='Дополнительные данные')),
('right_answer', models.BooleanField(default=False, verbose_name='Правильный')),
('comment', models.TextField(blank=True, null=True, verbose_name='Комментарий')),
('question', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='rel_answers_for_question', to='QuestionsApp.question', verbose_name='Вопрос')),
],
options={
'verbose_name': 'Ответ',
'verbose_name_plural': 'Ответы',
'ordering': ('question',),
},
),
]

View File

45
QuestionsApp/models.py Normal file
View File

@@ -0,0 +1,45 @@
from django.db import models
from BaseModels.base_models import BaseModel
from django.utils.translation import gettext_lazy as _
class Question(BaseModel):
game_level = models.IntegerField(verbose_name=_('Уровень игры'), default=1)
time_for_answer = models.IntegerField(verbose_name=_('Время на ответ (сек)'), default=7)
used_count = models.IntegerField(verbose_name=_('Количество использования'), default=0)
comment = models.TextField(verbose_name=_('Комментарий'), null=True, blank=True)
def __str__(self):
if self.name:
return f'{self.name}'
else:
return str(self.id)
class Meta:
permissions = (
)
verbose_name = _('Вопрос')
verbose_name_plural = _('Вопросы')
# ordering = ('user__last_name', 'user__first_name')
class Answer(BaseModel):
question = models.ForeignKey(
Question, verbose_name=_('Вопрос'), related_name='rel_answers_for_question', on_delete=models.CASCADE)
right_answer = models.BooleanField(verbose_name=_('Правильный'), default=False)
comment = models.TextField(verbose_name=_('Комментарий'), null=True, blank=True)
def __str__(self):
if self.name:
return f'{self.name}'
else:
return str(self.id)
class Meta:
verbose_name = _('Ответ')
verbose_name_plural = _('Ответы')
ordering = ('question',)

3
QuestionsApp/tests.py Normal file
View File

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

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