diff --git a/.flake8 b/.flake8 index 80288648..55649641 100644 --- a/.flake8 +++ b/.flake8 @@ -1,5 +1,5 @@ [flake8] -ignore = E501, E265, F811, PT001, DJ05, D100, D105, D104, W504, W292, D106, D107 +ignore = E501, E265, F811, PT001, DJ05, D100, D105, D104, W504, W292, D106, D107, W503 max-line-length = 79 exclude = */migrations/ diff --git a/.gitignore b/.gitignore index 52ceaa86..006002f7 100644 --- a/.gitignore +++ b/.gitignore @@ -60,7 +60,7 @@ coverage.xml local_settings.py db.sqlite3 db.sqlite3-journal -static/ +staticfiles/ media/ logs/ diff --git a/Makefile b/Makefile index e0451651..7d0e8ccc 100644 --- a/Makefile +++ b/Makefile @@ -17,7 +17,7 @@ help: # Show help .PHONY: runbot-init -runbot-init: deletedb rundb migrate filldb runbot-db # Build and run Database Docker-image +runbot-init: deletedb rundb migrate filldb collectstatic createsuperuser runbot-db # Build and run Database Docker-image @echo -e "$(COLOR_YELLOW)Starting initialization...$(COLOR_RESET)" @source $$(poetry env info -p)/bin/activate @@ -90,6 +90,10 @@ migrate: # Commit migrations to Database @sleep 3; @echo -e "$(COLOR_GREEN)Migrated$(COLOR_RESET)" +.PHONY: createsuperuser +createsuperuser: + @echo -e "$(COLOR_YELLOW)Creating Django superuser...$(COLOR_RESET)" + @poetry run python src/manage.py createsuperuser .PHONY: run_tests run_tests: run_unit_tests # Run all tests diff --git a/infra/dev/docker-compose.stage.yaml b/infra/dev/docker-compose.stage.yaml index 27b8e37c..8f65f46c 100644 --- a/infra/dev/docker-compose.stage.yaml +++ b/infra/dev/docker-compose.stage.yaml @@ -45,7 +45,7 @@ services: - redis - db volumes: - - ../../static:/app/src/static + - ../../static:/app/src/staticfiles - ../../media:/app/src/media - ./logs/bot/:/app/src/logs restart: always diff --git a/infra/init.sh b/infra/init.sh deleted file mode 100755 index eb1c3e98..00000000 --- a/infra/init.sh +++ /dev/null @@ -1,4 +0,0 @@ -#!/bin/bash -docker exec -it spread-wings-bot python manage.py migrate -docker exec -it spread-wings-bot python manage.py collectstatic --noinput -docker exec -it spread-wings-bot python manage.py createsuperuser --email example@yandex.ru --noinput diff --git a/infra/prod/docker-compose.prod.yaml b/infra/prod/docker-compose.prod.yaml index 743a781b..b3e20a05 100644 --- a/infra/prod/docker-compose.prod.yaml +++ b/infra/prod/docker-compose.prod.yaml @@ -45,7 +45,7 @@ services: - redis - db volumes: - - ../../static:/app/src/static + - ../../static:/app/src/staticfiles - ../../media:/app/src/media - ./logs/bot/:/app/src/logs restart: always diff --git a/src/bot/admin.py b/src/bot/admin.py index 63ec548f..c0eb4419 100644 --- a/src/bot/admin.py +++ b/src/bot/admin.py @@ -23,8 +23,8 @@ class QuestionAdmin(RegionForAdmin): form = QuestionAdminForm list_display = ( "get_question", - "get_short_description", - "get_question_type", + "short_description", + "question_type", "get_answer", "get_regions", ) @@ -41,16 +41,6 @@ def get_question(self, obj): """Display questions in admin panel.""" return obj.question[:100] - @admin.display(description="Короткое описание") - def get_short_description(self, obj): - """Display short_descriptions in admin panel.""" - return obj.short_description[:100] - - @admin.display(description="Тип вопроса") - def get_question_type(self, obj): - """Display question_type in admin panel.""" - return obj.question_type[:100] - @admin.display(description="Ответ") def get_answer(self, obj): """Display answer in admin panel.""" @@ -95,11 +85,6 @@ def get_title(self, obj): """Display title in admin panel.""" return obj.title[:100] - @admin.display(description="Короткое описание") - def get_short_description(self, obj): - """Display short_description in admin panel.""" - return obj.short_description[:100] - @admin.display(description="Описание программы") def get_fund_text(self, obj): """Display fund_text in admin panel.""" diff --git a/src/bot/bot.py b/src/bot/bot.py index e54052a9..40654f0f 100644 --- a/src/bot/bot.py +++ b/src/bot/bot.py @@ -146,8 +146,8 @@ async def build_app() -> Application: states={ States.GET_ASSISTANCE: [ CallbackQueryHandler(get_assistance, pattern=GET_ASSISTANCE), - ], - States.REGION: [ + ] + + [ CallbackQueryHandler( select_type_of_assistance, pattern=PATTERN.format(state=key), diff --git a/src/bot/constants/messages.py b/src/bot/constants/messages.py index e98cb0eb..ed48c7c4 100644 --- a/src/bot/constants/messages.py +++ b/src/bot/constants/messages.py @@ -1,18 +1,6 @@ -START_MESSAGE = 'Здравствуйте! Это бот фонда "Расправь крылья".' -HELP_MESSAGE = ( - "[Заглушка]Здесь выводится краткое описание возможностей бота (/help)" -) -ASSISTANCE_MESSAGE = ( - '[Заглушка]Сообщение после нажатия на кнопку "Получить помощь" ' - "(Выбор региона)" -) -CHOOSE_REGION_MESSAGE = "Выберите, пожалуйста, регион" ASSISTANCE_TYPE_MESSAGE = ( "[Заглушка]Сообщение в момент выбора юр., соц., психол., помощи и т.д." ) -HOW_CAN_WE_HELP = "Чем мы можем помочь?" -MENU_MESSAGE = "Команды и меню обновлены" -CONTACT_US_MESSAGE = "[Заглушка] Задать вопрос." SELECT_FUND_PROGRAM = "Выбор программы из списка" SELECT_QUESTION = "Выбор вопроса из списка" diff --git a/src/bot/constants/patterns.py b/src/bot/constants/patterns.py index 42620b6b..788ca524 100644 --- a/src/bot/constants/patterns.py +++ b/src/bot/constants/patterns.py @@ -11,9 +11,11 @@ state="".join(f"{h_type}|" for h_type in HelpTypes.names) ) HELP_TYPE = rf"({POSSIBLE_TYPE_OF_ASSISTANCE})(?:{PAGE_SEP_SYMBOL}(\d+))?" +GET_ASSISTANCE = PATTERN.format( + state=rf"({States.GET_ASSISTANCE.value})(?:{PAGE_SEP_SYMBOL}(\d+))?" +) SEND_EMAIL = PATTERN.format(state=States.SEND_EMAIL.value) GET_USER_QUESTION = PATTERN.format(state=States.GET_USER_QUESTION.value) -GET_ASSISTANCE = PATTERN.format(state=States.GET_ASSISTANCE.value) FUND_PROGRAMS = PATTERN.format( state=rf"({States.FUND_PROGRAMS.value})(?:{PAGE_SEP_SYMBOL}(\d+))?" ) diff --git a/src/bot/forms.py b/src/bot/forms.py index 884a2465..d575b296 100644 --- a/src/bot/forms.py +++ b/src/bot/forms.py @@ -11,6 +11,7 @@ class Meta: model = Question widgets = { + "question": forms.Textarea(attrs={"rows": 3, "cols": 50}), "answer": forms.Textarea(attrs={"rows": 10, "cols": 80}), "regions": forms.CheckboxSelectMultiple, } @@ -25,6 +26,7 @@ class Meta: models = FundProgram widgets = { + "title": forms.Textarea(attrs={"rows": 3, "cols": 50}), "regions": forms.CheckboxSelectMultiple, } fields = "__all__" diff --git a/src/bot/handlers/assistance.py b/src/bot/handlers/assistance.py index 54db260e..f288ff1c 100644 --- a/src/bot/handlers/assistance.py +++ b/src/bot/handlers/assistance.py @@ -8,7 +8,7 @@ SELECT_FUND_PROGRAM, SELECT_QUESTION, ) -from bot.constants.patterns import FUND_PROGRAMS, HELP_TYPE +from bot.constants.patterns import FUND_PROGRAMS, GET_ASSISTANCE, HELP_TYPE from bot.constants.states import States from bot.handlers.debug_handlers import debug_logger from bot.keyboards.assistance import ( @@ -20,27 +20,34 @@ ) from bot.keyboards.assistance_types import assistance_types_keyboard_markup from bot.keyboards.utils.callback_data_parse import parse_callback_data -from bot.models import HelpTypes from bot_settings.models import BotSettings DEFAULT_PAGE = 1 -@debug_logger(state=States.REGION, run_functions_debug_loger="get_assistance") +@debug_logger( + state=States.GET_ASSISTANCE, run_functions_debug_loger="get_assistance" +) async def get_assistance( update: Update, context: ContextTypes.DEFAULT_TYPE, ) -> States: """Select a region of assistance.""" - await update.callback_query.answer() - keyboard = await build_region_keyboard() + query = update.callback_query + callback_data = query.data.replace("back_to_", "") + _, page_number = parse_callback_data(callback_data, GET_ASSISTANCE) + page_number = page_number or DEFAULT_PAGE + await query.answer() + keyboard = await build_region_keyboard(page_number) assistance_message = await BotSettings.objects.aget( key="assistance_message" ) - await update.callback_query.edit_message_text( - text=assistance_message.value, reply_markup=keyboard - ) - return States.REGION + if query.message.reply_markup.to_json() != keyboard.markup: + await query.edit_message_text( + text=assistance_message.value, + reply_markup=keyboard.markup, + ) + return States.GET_ASSISTANCE @debug_logger( @@ -139,7 +146,7 @@ async def contact_with_us( ) -> States: """Ask question and show contacts.""" query = update.callback_query - context.user_data[States.QUESTION_TYPE] = HelpTypes.COMMON_QUESTION.value + context.user_data[States.QUESTION_TYPE] = "COMMON_QUESTION" await query.answer() await query.edit_message_text( text=CONTACT_SHOW_MESSAGE, diff --git a/src/bot/handlers/service_handlers.py b/src/bot/handlers/service_handlers.py index 9f318622..fada5633 100644 --- a/src/bot/handlers/service_handlers.py +++ b/src/bot/handlers/service_handlers.py @@ -41,11 +41,11 @@ async def answer_all_messages( FUNCTIONS: dict[str, Callable[[Any, Any], Awaitable[States]]] = { - States.GET_ASSISTANCE.value: start, + States.START: start, States.ASSISTANCE_TYPE: select_type_of_assistance, States.CONTACT_US: contact_with_us, States.FUND_PROGRAMS: fund_programs, - States.REGION: get_assistance, + States.REGION.value: get_assistance, States.SHOW_CONTACT: show_contact, States.GET_USERNAME: get_user_question, States.USERNAME_AFTER_RETURNING: get_username_after_returning_back, diff --git a/src/bot/keyboards/assistance.py b/src/bot/keyboards/assistance.py index 70723504..2d31e6fd 100644 --- a/src/bot/keyboards/assistance.py +++ b/src/bot/keyboards/assistance.py @@ -17,9 +17,6 @@ from bot_settings.models import BotSettings from core.models import Region -PROGRAMS_PER_PAGE = 6 -QUESTIONS_PER_PAGE = 6 - async def build_assistance_keyboard() -> InlineKeyboardMarkup: """ @@ -43,30 +40,42 @@ async def build_assistance_keyboard() -> InlineKeyboardMarkup: ) -async def build_region_keyboard() -> InlineKeyboardMarkup: +async def build_region_keyboard( + page: int, +) -> InlineKeyboardPaginator: """ Build telegram assistance type keyboard async. After building cache it. """ - keyboard = [ - [ - InlineKeyboardButton( - text=region.region_name, - callback_data=region.region_key, - ) - ] - async for region in Region.objects.all() - ] - back_button = [ - [ + queryset = await sync_to_async(list)( + Region.objects.all().values("region_name", "region_key") + ) + region_per_page = await BotSettings.objects.aget( + key="regions_pagination_setting" + ) + data_paginator = Paginator(queryset, int(region_per_page.value)) + telegram_paginator = InlineKeyboardPaginator( + data_paginator.num_pages, + current_page=page, + data_pattern="".join( + [States.GET_ASSISTANCE.value, PAGE_SEP_SYMBOL, "{page}"] + ), + ) + for region in data_paginator.page(page): + telegram_paginator.add_before( InlineKeyboardButton( - text=BACK_BUTTON, - callback_data=f"back_to_{States.GET_ASSISTANCE.value}", + text=region.get("region_name"), + callback_data=region.get("region_key"), ) - ] - ] - return InlineKeyboardMarkup(keyboard + back_button) + ) + telegram_paginator.add_after( + InlineKeyboardButton( + text=BACK_BUTTON, + callback_data=f"back_to_{States.START.value}", + ), + ) + return telegram_paginator async def build_question_keyboard( @@ -85,7 +94,10 @@ async def build_question_keyboard( question_type=question_type, ).values("id", "short_description") ) - data_paginator = Paginator(queryset, QUESTIONS_PER_PAGE) + questions_per_page = await BotSettings.objects.aget( + key="questions_pagination_setting" + ) + data_paginator = Paginator(queryset, int(questions_per_page.value)) telegram_paginator = InlineKeyboardPaginator( data_paginator.num_pages, current_page=page, @@ -131,7 +143,10 @@ async def build_fund_program_keyboard( regions__region_key=region, ).values("id", "short_description") ) - data_paginator = Paginator(queryset, PROGRAMS_PER_PAGE) + programs_per_page = await BotSettings.objects.aget( + key="programs_pagination_setting" + ) + data_paginator = Paginator(queryset, int(programs_per_page.value)) telegram_paginator = InlineKeyboardPaginator( data_paginator.num_pages, current_page=page, diff --git a/src/bot/migrations/0005_alter_coordinator_email_address_and_more.py b/src/bot/migrations/0005_alter_coordinator_email_address_and_more.py new file mode 100644 index 00000000..5fb326ef --- /dev/null +++ b/src/bot/migrations/0005_alter_coordinator_email_address_and_more.py @@ -0,0 +1,50 @@ +# Generated by Django 4.2.4 on 2023-08-31 21:06 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + """Migrations for bot.""" + + dependencies = [ + ("core", "0002_alter_region_region_name"), + ("bot", "0004_alter_coordinator_options_coordinator_is_chief"), + ] + + operations = [ + migrations.AlterField( + model_name="coordinator", + name="email_address", + field=models.EmailField( + help_text="Введите адрес электронной почты", + max_length=254, + unique=True, + verbose_name="Email", + ), + ), + migrations.AlterField( + model_name="coordinator", + name="region", + field=models.OneToOneField( + help_text="Выберите регион из списка", + on_delete=django.db.models.deletion.PROTECT, + primary_key=True, + related_name="coordinators", + serialize=False, + to="core.region", + verbose_name="Регион", + ), + ), + migrations.AlterField( + model_name="fundprogram", + name="regions", + field=models.ManyToManyField( + blank=True, + help_text="Выберите регион(ы) для программы", + related_name="programs", + to="core.region", + verbose_name="Регионы", + ), + ), + ] diff --git a/src/bot/migrations/0006_alter_fundprogram_short_description_and_more.py b/src/bot/migrations/0006_alter_fundprogram_short_description_and_more.py new file mode 100644 index 00000000..b1518f22 --- /dev/null +++ b/src/bot/migrations/0006_alter_fundprogram_short_description_and_more.py @@ -0,0 +1,36 @@ +# Generated by Django 4.2.4 on 2023-09-01 10:51 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("bot", "0005_alter_coordinator_email_address_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="fundprogram", + name="short_description", + field=models.CharField( + help_text="Введите название кнопки в боте для данной программы", + max_length=20, + verbose_name="Текст на кнопке", + ), + ), + migrations.AlterField( + model_name="question", + name="question_type", + field=models.CharField( + choices=[ + ("LEGAL_ASSISTANCE", "Юридическая помощь"), + ("SOCIAL_ASSISTANCE", "Социальная помощь"), + ("PSYCHOLOGICAL_ASSISTANCE", "Психологическая помощь"), + ], + default="LEGAL_ASSISTANCE", + help_text="Выберите тип помощи для вопроса", + max_length=100, + verbose_name="Тип вопроса", + ), + ), + ] diff --git a/src/bot/migrations/0007_alter_question_short_description.py b/src/bot/migrations/0007_alter_question_short_description.py new file mode 100644 index 00000000..209e2534 --- /dev/null +++ b/src/bot/migrations/0007_alter_question_short_description.py @@ -0,0 +1,23 @@ +# Generated by Django 4.2.4 on 2023-09-01 15:43 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + """Migrations for bot.""" + + dependencies = [ + ("bot", "0006_alter_fundprogram_short_description_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="question", + name="short_description", + field=models.CharField( + help_text="Введите название кнопки в боте для данного вопроса", + max_length=20, + verbose_name="Текст на кнопке", + ), + ), + ] diff --git a/src/bot/migrations/0008_alter_coordinator_phone_number_and_more.py b/src/bot/migrations/0008_alter_coordinator_phone_number_and_more.py new file mode 100644 index 00000000..51eea2b1 --- /dev/null +++ b/src/bot/migrations/0008_alter_coordinator_phone_number_and_more.py @@ -0,0 +1,49 @@ +# Generated by Django 4.2.4 on 2023-09-03 17:19 + +import django.core.validators +from django.db import migrations, models + + +class Migration(migrations.Migration): + """Migrations for bot.""" + + dependencies = [ + ("bot", "0007_alter_question_short_description"), + ] + + operations = [ + migrations.AlterField( + model_name="coordinator", + name="phone_number", + field=models.CharField( + blank=True, + help_text="Введите номер телефона регионального координатора", + max_length=20, + null=True, + validators=[ + django.core.validators.RegexValidator( + message="Введите номер телефона в формате: +7 (777) 777-77-77", + regex="^[\\+]?[7, 8][-\\s\\.]?[(]?[0-9]{3}[)]?[-\\s\\.]?[0-9]{3}[-\\s\\.]?[0-9]{2}[-\\s\\.]?[0-9]{2}$", + ) + ], + verbose_name="Номер телефона", + ), + ), + migrations.AlterField( + model_name="coordinator", + name="telegram_account", + field=models.CharField( + blank=True, + help_text="Введите телеграмм-аккаунт регионального координатора", + max_length=32, + null=True, + validators=[ + django.core.validators.RegexValidator( + message="Введите название аккаунта telegram в формате: username", + regex="^[\\w\\_]{5,32}$", + ) + ], + verbose_name="Telegram", + ), + ), + ] diff --git a/src/bot/models.py b/src/bot/models.py index 338a2e5b..da9b93a4 100644 --- a/src/bot/models.py +++ b/src/bot/models.py @@ -31,11 +31,15 @@ class Coordinator(BaseModel): primary_key=True, related_name="coordinators", verbose_name="Регион", + help_text="Выберите регион из списка", + ) + email_address = models.EmailField( + unique=True, + verbose_name="Email", + help_text="Введите адрес электронной почты", ) - email_address = models.EmailField(unique=True, verbose_name="Email") phone_number = models.CharField( max_length=20, - unique=True, validators=[phone_regex], blank=True, null=True, @@ -44,7 +48,6 @@ class Coordinator(BaseModel): ) telegram_account = models.CharField( max_length=32, - unique=True, validators=[telegram_regex], blank=True, null=True, @@ -78,7 +81,7 @@ def __repr__(self): def __str__(self): return f"{self.first_name} {self.last_name}" - class Meta: # noqa + class Meta: verbose_name = "Координатор" verbose_name_plural = "Координаторы" ordering = ( @@ -95,7 +98,6 @@ class HelpTypes(models.TextChoices): PSYCHOLOGICAL_ASSISTANCE = "PSYCHOLOGICAL_ASSISTANCE", _( "Психологическая помощь" ) - COMMON_QUESTION = "COMMON_QUESTION", _("Общий вопрос") class Question(BaseModel): @@ -113,7 +115,7 @@ class Question(BaseModel): ) short_description = models.CharField( max_length=20, - verbose_name="Короткое описание", + verbose_name="Текст на кнопке", help_text="Введите название кнопки в боте для данного вопроса", ) regions = models.ManyToManyField( @@ -131,7 +133,10 @@ class Question(BaseModel): help_text="Выберите тип помощи для вопроса", ) - class Meta: # noqa + def __str__(self): + return self.short_description + + class Meta: verbose_name = "Вопрос" verbose_name_plural = "Вопросы" ordering = ("question",) @@ -153,7 +158,7 @@ class FundProgram(BaseModel): ) short_description = models.CharField( max_length=20, - verbose_name="Короткое описание", + verbose_name="Текст на кнопке", help_text="Введите название кнопки в боте для данной программы", ) regions = models.ManyToManyField( @@ -161,9 +166,13 @@ class FundProgram(BaseModel): related_name="programs", blank=True, verbose_name="Регионы", + help_text="Выберите регион(ы) для программы", ) - class Meta: # noqa + def __str__(self): + return self.short_description + + class Meta: verbose_name = "Программа фонда" verbose_name_plural = "Программы фонда" ordering = ("title",) @@ -172,7 +181,7 @@ class Meta: # noqa class ProxyRegion(Region): """ProxyRegion model.""" - class Meta: # noqa + class Meta: proxy = True verbose_name_plural = "Регионы" verbose_name = "Регион" diff --git a/src/bot/services.py b/src/bot/services.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/bot_settings/admin.py b/src/bot_settings/admin.py index f8a7c662..2809e632 100644 --- a/src/bot_settings/admin.py +++ b/src/bot_settings/admin.py @@ -7,11 +7,11 @@ class BotSettingsAdmin(admin.ModelAdmin): """Base admin configuration for BotSettings model.""" - list_display = ("get_title", "type", "get_value") - list_filter = ("title", "type", "value") + list_display = ("get_title", "get_type", "get_value") + list_filter = ("type",) search_fields = ("title", "type", "value") readonly_fields = ("type",) - exclude = ("key",) + exclude = ("key", "title") def has_delete_permission(self, request, obj=None): """Disable delete permission for BotSettings model.""" @@ -22,6 +22,11 @@ def get_title(self, obj): """Display title of settings in admin panel.""" return obj.title[:100] + @admin.display(description="Тип значения") + def get_type(self, obj): + """Display type of settings in admin panel.""" + return obj.get_type_display()[:100] + @admin.display(description="Значение настройки") def get_value(self, obj): """Display value of settings in admin panel.""" diff --git a/src/bot_settings/migrations/0002_add_settings.py b/src/bot_settings/migrations/0002_add_settings.py index b9a75123..7876496e 100644 --- a/src/bot_settings/migrations/0002_add_settings.py +++ b/src/bot_settings/migrations/0002_add_settings.py @@ -76,6 +76,60 @@ def remove_assistance_message_setting(apps, schema_editor): setting.delete() +def create_regions_pagination_setting(apps, schema_editor): + """Create regions pagination setting instance.""" + BotSettings = apps.get_model("bot_settings", "BotSettings") + BotSettings.objects.create( + key="regions_pagination_setting", + title="Количество регионов на одной странице", + type=BotSettingsModel.INT, + value=6, + ) + + +def remove_regions_pagination_setting(apps, schema_editor): + """Remove regions pagination setting instance.""" + BotSettings = apps.get_model("bot_settings", "BotSettings") + setting = BotSettings.objects.get(key="regions_pagination_setting") + setting.delete() + + +def create_programs_pagination_setting(apps, schema_editor): + """Create programs pagination setting instance.""" + BotSettings = apps.get_model("bot_settings", "BotSettings") + BotSettings.objects.create( + key="programs_pagination_setting", + title="Количество программ на одной странице", + type=BotSettingsModel.INT, + value=6, + ) + + +def remove_programs_pagination_setting(apps, schema_editor): + """Remove programs pagination setting instance.""" + BotSettings = apps.get_model("bot_settings", "BotSettings") + setting = BotSettings.objects.get(key="programs_pagination_setting") + setting.delete() + + +def create_questions_pagination_setting(apps, schema_editor): + """Create questions pagination setting instance.""" + BotSettings = apps.get_model("bot_settings", "BotSettings") + BotSettings.objects.create( + key="questions_pagination_setting", + title="Количество вопросов на одной странице", + type=BotSettingsModel.INT, + value=6, + ) + + +def remove_questions_pagination_setting(apps, schema_editor): + """Remove questions pagination setting instance.""" + BotSettings = apps.get_model("bot_settings", "BotSettings") + setting = BotSettings.objects.get(key="questions_pagination_setting") + setting.delete() + + def create_select_type_of_help_setting(apps, schema_editor): """Create select_type_of_help setting instance.""" BotSettings = apps.get_model("bot_settings", "BotSettings") @@ -190,6 +244,18 @@ class Migration(migrations.Migration): create_assistance_message_setting, reverse_code=remove_assistance_message_setting, ), + migrations.RunPython( + create_regions_pagination_setting, + reverse_code=remove_regions_pagination_setting, + ), + migrations.RunPython( + create_programs_pagination_setting, + reverse_code=remove_programs_pagination_setting, + ), + migrations.RunPython( + create_questions_pagination_setting, + reverse_code=remove_questions_pagination_setting, + ), migrations.RunPython( create_select_type_of_help_setting, reverse_code=remove_select_type_of_help_setting, diff --git a/src/bot_settings/migrations/0003_alter_botsettings_title_alter_botsettings_value.py b/src/bot_settings/migrations/0003_alter_botsettings_title_alter_botsettings_type_and_more.py similarity index 67% rename from src/bot_settings/migrations/0003_alter_botsettings_title_alter_botsettings_value.py rename to src/bot_settings/migrations/0003_alter_botsettings_title_alter_botsettings_type_and_more.py index 8f7c8821..7bdd26a8 100644 --- a/src/bot_settings/migrations/0003_alter_botsettings_title_alter_botsettings_value.py +++ b/src/bot_settings/migrations/0003_alter_botsettings_title_alter_botsettings_type_and_more.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.4 on 2023-08-28 21:33 +# Generated by Django 4.2.4 on 2023-09-01 16:07 import ckeditor.fields from django.db import migrations, models @@ -21,6 +21,19 @@ class Migration(migrations.Migration): verbose_name="Название настройки", ), ), + migrations.AlterField( + model_name="botsettings", + name="type", + field=models.CharField( + choices=[ + ("url", "Ссылка"), + ("text", "Текст"), + ("int", "Число"), + ], + max_length=100, + verbose_name="Тип значения", + ), + ), migrations.AlterField( model_name="botsettings", name="value", diff --git a/src/bot_settings/migrations/0004_alter_botsettings_type.py b/src/bot_settings/migrations/0004_alter_botsettings_type.py deleted file mode 100644 index 3d4c28c1..00000000 --- a/src/bot_settings/migrations/0004_alter_botsettings_type.py +++ /dev/null @@ -1,24 +0,0 @@ -# Generated by Django 4.2.4 on 2023-08-30 17:04 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - dependencies = [ - ( - "bot_settings", - "0003_alter_botsettings_title_alter_botsettings_value", - ), - ] - - operations = [ - migrations.AlterField( - model_name="botsettings", - name="type", - field=models.CharField( - choices=[("url", "Ссылка"), ("text", "Текст")], - max_length=100, - verbose_name="Тип значения", - ), - ), - ] diff --git a/src/bot_settings/models.py b/src/bot_settings/models.py index d967cba5..6b57a0c3 100644 --- a/src/bot_settings/models.py +++ b/src/bot_settings/models.py @@ -10,9 +10,11 @@ class BotSettings(BaseModel): URL = "url" TEXT = "text" + INT = "int" __VALUE_TYPES = ( (URL, "Ссылка"), (TEXT, "Текст"), + (INT, "Число"), ) key = models.CharField( max_length=100, @@ -41,7 +43,7 @@ def clean(self): URLValidator()(self.value) def __str__(self): - return f"" + return self.title class Meta: """Meta for BotSettings model.""" diff --git a/src/config/settings.py b/src/config/settings.py index 562ee1ad..8c4ade0c 100644 --- a/src/config/settings.py +++ b/src/config/settings.py @@ -55,7 +55,7 @@ }, ] -WSGI_APPLICATION = "config.wsgi.application" +ASGI_APPLICATION = "config.asgi.application" DATABASES = { @@ -101,18 +101,19 @@ USE_TZ = True STATIC_URL = "/static/" -STATIC_ROOT = BASE_DIR / "static" -STATIC_ROOT.mkdir(exist_ok=True) -STATICFILES_DIRS = [ - ("ckeditor/ckeditor/plugins", "ckeditor_add-on/plugins/"), - ("ckeditor/ckeditor/skins", "ckeditor_add-on/skins/"), +STATIC_ROOT = str(BASE_DIR / "staticfiles") +STATICFILES_FINDERS = [ + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", ] + DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" EMAIL_BACKEND = env.str( "EMAIL_BACKEND", default="django.core.mail.backends.console.EmailBackend" ) +EMAIL_TEMPLATE_NAME = "email.html" EMAIL_HOST = env.str("EMAIL_HOST") try: EMAIL_PORT = env.int("EMAIL_PORT") diff --git a/src/core/mailing.py b/src/core/mailing.py index 60715739..31110fe9 100644 --- a/src/core/mailing.py +++ b/src/core/mailing.py @@ -2,10 +2,10 @@ from typing import Sequence from asgiref.sync import sync_to_async -from django.conf import settings from django.core.mail import EmailMultiAlternatives from django.template.loader import get_template +from config import settings from core.validators import MailValidator logger = logging.getLogger("core") diff --git a/src/core/migrations/0002_alter_region_region_name.py b/src/core/migrations/0002_alter_region_region_name.py new file mode 100644 index 00000000..c55b4257 --- /dev/null +++ b/src/core/migrations/0002_alter_region_region_name.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.4 on 2023-08-31 21:06 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + """Migrations for bot.""" + + dependencies = [ + ("core", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="region", + name="region_name", + field=models.CharField( + help_text="Это название также будет отображаться на кнопках бота", + max_length=200, + unique=True, + verbose_name="Название региона", + ), + ), + ] diff --git a/src/core/migrations/0003_alter_region_region_name.py b/src/core/migrations/0003_alter_region_region_name.py new file mode 100644 index 00000000..a53bba19 --- /dev/null +++ b/src/core/migrations/0003_alter_region_region_name.py @@ -0,0 +1,24 @@ +# Generated by Django 4.2.4 on 2023-09-01 16:07 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + """Migrations for bot.""" + + dependencies = [ + ("core", "0002_alter_region_region_name"), + ] + + operations = [ + migrations.AlterField( + model_name="region", + name="region_name", + field=models.CharField( + help_text="Это название так же будет отображаться на кнопках бота", + max_length=200, + unique=True, + verbose_name="Название региона", + ), + ), + ] diff --git a/src/core/models.py b/src/core/models.py index ea2561d9..7f7e3721 100644 --- a/src/core/models.py +++ b/src/core/models.py @@ -17,7 +17,12 @@ class Meta: class Region(BaseModel): """Region model.""" - region_name = models.CharField(max_length=200, unique=True) + region_name = models.CharField( + max_length=200, + unique=True, + verbose_name="Название региона", + help_text="Это название так же будет отображаться на кнопках бота", + ) region_key = models.CharField(max_length=200, unique=True) def save( diff --git a/ckeditor_add-on/plugins/autocomplete/plugin.js b/src/core/static/ckeditor/ckeditor/plugins/autocomplete/plugin.js similarity index 100% rename from ckeditor_add-on/plugins/autocomplete/plugin.js rename to src/core/static/ckeditor/ckeditor/plugins/autocomplete/plugin.js diff --git a/ckeditor_add-on/plugins/autocomplete/skins/default.css b/src/core/static/ckeditor/ckeditor/plugins/autocomplete/skins/default.css similarity index 100% rename from ckeditor_add-on/plugins/autocomplete/skins/default.css rename to src/core/static/ckeditor/ckeditor/plugins/autocomplete/skins/default.css diff --git a/ckeditor_add-on/plugins/button/plugin.js b/src/core/static/ckeditor/ckeditor/plugins/button/plugin.js similarity index 100% rename from ckeditor_add-on/plugins/button/plugin.js rename to src/core/static/ckeditor/ckeditor/plugins/button/plugin.js diff --git a/ckeditor_add-on/plugins/emoji/assets/iconsall.png b/src/core/static/ckeditor/ckeditor/plugins/emoji/assets/iconsall.png similarity index 100% rename from ckeditor_add-on/plugins/emoji/assets/iconsall.png rename to src/core/static/ckeditor/ckeditor/plugins/emoji/assets/iconsall.png diff --git a/ckeditor_add-on/plugins/emoji/assets/iconsall.svg b/src/core/static/ckeditor/ckeditor/plugins/emoji/assets/iconsall.svg similarity index 100% rename from ckeditor_add-on/plugins/emoji/assets/iconsall.svg rename to src/core/static/ckeditor/ckeditor/plugins/emoji/assets/iconsall.svg diff --git a/ckeditor_add-on/plugins/emoji/emoji.json b/src/core/static/ckeditor/ckeditor/plugins/emoji/emoji.json similarity index 100% rename from ckeditor_add-on/plugins/emoji/emoji.json rename to src/core/static/ckeditor/ckeditor/plugins/emoji/emoji.json diff --git a/ckeditor_add-on/plugins/emoji/icons/emojipanel.png b/src/core/static/ckeditor/ckeditor/plugins/emoji/icons/emojipanel.png similarity index 100% rename from ckeditor_add-on/plugins/emoji/icons/emojipanel.png rename to src/core/static/ckeditor/ckeditor/plugins/emoji/icons/emojipanel.png diff --git a/ckeditor_add-on/plugins/emoji/icons/hidpi/emojipanel.png b/src/core/static/ckeditor/ckeditor/plugins/emoji/icons/hidpi/emojipanel.png similarity index 100% rename from ckeditor_add-on/plugins/emoji/icons/hidpi/emojipanel.png rename to src/core/static/ckeditor/ckeditor/plugins/emoji/icons/hidpi/emojipanel.png diff --git a/ckeditor_add-on/plugins/emoji/lang/cs.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/cs.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/cs.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/cs.js diff --git a/ckeditor_add-on/plugins/emoji/lang/da.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/da.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/da.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/da.js diff --git a/ckeditor_add-on/plugins/emoji/lang/de-ch.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/de-ch.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/de-ch.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/de-ch.js diff --git a/ckeditor_add-on/plugins/emoji/lang/de.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/de.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/de.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/de.js diff --git a/ckeditor_add-on/plugins/emoji/lang/el.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/el.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/el.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/el.js diff --git a/ckeditor_add-on/plugins/emoji/lang/en-au.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/en-au.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/en-au.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/en-au.js diff --git a/ckeditor_add-on/plugins/emoji/lang/en.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/en.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/en.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/en.js diff --git a/ckeditor_add-on/plugins/emoji/lang/et.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/et.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/et.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/et.js diff --git a/ckeditor_add-on/plugins/emoji/lang/fa.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/fa.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/fa.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/fa.js diff --git a/ckeditor_add-on/plugins/emoji/lang/fr.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/fr.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/fr.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/fr.js diff --git a/ckeditor_add-on/plugins/emoji/lang/gl.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/gl.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/gl.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/gl.js diff --git a/ckeditor_add-on/plugins/emoji/lang/hr.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/hr.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/hr.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/hr.js diff --git a/ckeditor_add-on/plugins/emoji/lang/hu.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/hu.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/hu.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/hu.js diff --git a/ckeditor_add-on/plugins/emoji/lang/it.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/it.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/it.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/it.js diff --git a/ckeditor_add-on/plugins/emoji/lang/nl.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/nl.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/nl.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/nl.js diff --git a/ckeditor_add-on/plugins/emoji/lang/pl.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/pl.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/pl.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/pl.js diff --git a/ckeditor_add-on/plugins/emoji/lang/pt-br.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/pt-br.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/pt-br.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/pt-br.js diff --git a/ckeditor_add-on/plugins/emoji/lang/sk.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/sk.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/sk.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/sk.js diff --git a/ckeditor_add-on/plugins/emoji/lang/sr-latn.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/sr-latn.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/sr-latn.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/sr-latn.js diff --git a/ckeditor_add-on/plugins/emoji/lang/sr.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/sr.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/sr.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/sr.js diff --git a/ckeditor_add-on/plugins/emoji/lang/sv.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/sv.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/sv.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/sv.js diff --git a/ckeditor_add-on/plugins/emoji/lang/tr.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/tr.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/tr.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/tr.js diff --git a/ckeditor_add-on/plugins/emoji/lang/uk.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/uk.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/uk.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/uk.js diff --git a/ckeditor_add-on/plugins/emoji/lang/zh-cn.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/zh-cn.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/zh-cn.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/zh-cn.js diff --git a/ckeditor_add-on/plugins/emoji/lang/zh.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/lang/zh.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/lang/zh.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/lang/zh.js diff --git a/ckeditor_add-on/plugins/emoji/plugin.js b/src/core/static/ckeditor/ckeditor/plugins/emoji/plugin.js similarity index 100% rename from ckeditor_add-on/plugins/emoji/plugin.js rename to src/core/static/ckeditor/ckeditor/plugins/emoji/plugin.js diff --git a/ckeditor_add-on/plugins/emoji/samples/emoji.html b/src/core/static/ckeditor/ckeditor/plugins/emoji/samples/emoji.html similarity index 100% rename from ckeditor_add-on/plugins/emoji/samples/emoji.html rename to src/core/static/ckeditor/ckeditor/plugins/emoji/samples/emoji.html diff --git a/ckeditor_add-on/plugins/emoji/skins/default.css b/src/core/static/ckeditor/ckeditor/plugins/emoji/skins/default.css similarity index 100% rename from ckeditor_add-on/plugins/emoji/skins/default.css rename to src/core/static/ckeditor/ckeditor/plugins/emoji/skins/default.css diff --git a/ckeditor_add-on/plugins/panelbutton/plugin.js b/src/core/static/ckeditor/ckeditor/plugins/panelbutton/plugin.js similarity index 100% rename from ckeditor_add-on/plugins/panelbutton/plugin.js rename to src/core/static/ckeditor/ckeditor/plugins/panelbutton/plugin.js diff --git a/ckeditor_add-on/plugins/textmatch/plugin.js b/src/core/static/ckeditor/ckeditor/plugins/textmatch/plugin.js similarity index 100% rename from ckeditor_add-on/plugins/textmatch/plugin.js rename to src/core/static/ckeditor/ckeditor/plugins/textmatch/plugin.js diff --git a/ckeditor_add-on/plugins/textwatcher/plugin.js b/src/core/static/ckeditor/ckeditor/plugins/textwatcher/plugin.js similarity index 100% rename from ckeditor_add-on/plugins/textwatcher/plugin.js rename to src/core/static/ckeditor/ckeditor/plugins/textwatcher/plugin.js diff --git a/ckeditor_add-on/skins/n1theme/dialog.css b/src/core/static/ckeditor/ckeditor/skins/n1theme/dialog.css similarity index 100% rename from ckeditor_add-on/skins/n1theme/dialog.css rename to src/core/static/ckeditor/ckeditor/skins/n1theme/dialog.css diff --git a/ckeditor_add-on/skins/n1theme/dialog_ie.css b/src/core/static/ckeditor/ckeditor/skins/n1theme/dialog_ie.css similarity index 100% rename from ckeditor_add-on/skins/n1theme/dialog_ie.css rename to src/core/static/ckeditor/ckeditor/skins/n1theme/dialog_ie.css diff --git a/ckeditor_add-on/skins/n1theme/dialog_ie8.css b/src/core/static/ckeditor/ckeditor/skins/n1theme/dialog_ie8.css similarity index 100% rename from ckeditor_add-on/skins/n1theme/dialog_ie8.css rename to src/core/static/ckeditor/ckeditor/skins/n1theme/dialog_ie8.css diff --git a/ckeditor_add-on/skins/n1theme/dialog_iequirks.css b/src/core/static/ckeditor/ckeditor/skins/n1theme/dialog_iequirks.css similarity index 100% rename from ckeditor_add-on/skins/n1theme/dialog_iequirks.css rename to src/core/static/ckeditor/ckeditor/skins/n1theme/dialog_iequirks.css diff --git a/ckeditor_add-on/skins/n1theme/editor.css b/src/core/static/ckeditor/ckeditor/skins/n1theme/editor.css similarity index 100% rename from ckeditor_add-on/skins/n1theme/editor.css rename to src/core/static/ckeditor/ckeditor/skins/n1theme/editor.css diff --git a/ckeditor_add-on/skins/n1theme/editor_gecko.css b/src/core/static/ckeditor/ckeditor/skins/n1theme/editor_gecko.css similarity index 100% rename from ckeditor_add-on/skins/n1theme/editor_gecko.css rename to src/core/static/ckeditor/ckeditor/skins/n1theme/editor_gecko.css diff --git a/ckeditor_add-on/skins/n1theme/editor_ie.css b/src/core/static/ckeditor/ckeditor/skins/n1theme/editor_ie.css similarity index 100% rename from ckeditor_add-on/skins/n1theme/editor_ie.css rename to src/core/static/ckeditor/ckeditor/skins/n1theme/editor_ie.css diff --git a/ckeditor_add-on/skins/n1theme/editor_ie8.css b/src/core/static/ckeditor/ckeditor/skins/n1theme/editor_ie8.css similarity index 100% rename from ckeditor_add-on/skins/n1theme/editor_ie8.css rename to src/core/static/ckeditor/ckeditor/skins/n1theme/editor_ie8.css diff --git a/ckeditor_add-on/skins/n1theme/editor_iequirks.css b/src/core/static/ckeditor/ckeditor/skins/n1theme/editor_iequirks.css similarity index 100% rename from ckeditor_add-on/skins/n1theme/editor_iequirks.css rename to src/core/static/ckeditor/ckeditor/skins/n1theme/editor_iequirks.css diff --git a/ckeditor_add-on/skins/n1theme/icons.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/icons.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/icons.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/icons.png diff --git a/ckeditor_add-on/skins/n1theme/icons_hidpi.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/icons_hidpi.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/icons_hidpi.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/icons_hidpi.png diff --git a/ckeditor_add-on/skins/n1theme/images/arrow.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/arrow.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/arrow.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/arrow.png diff --git a/ckeditor_add-on/skins/n1theme/images/close.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/close.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/close.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/close.png diff --git a/ckeditor_add-on/skins/n1theme/images/hidpi/close.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/hidpi/close.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/hidpi/close.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/hidpi/close.png diff --git a/ckeditor_add-on/skins/n1theme/images/hidpi/lock-open.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/hidpi/lock-open.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/hidpi/lock-open.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/hidpi/lock-open.png diff --git a/ckeditor_add-on/skins/n1theme/images/hidpi/lock.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/hidpi/lock.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/hidpi/lock.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/hidpi/lock.png diff --git a/ckeditor_add-on/skins/n1theme/images/hidpi/refresh.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/hidpi/refresh.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/hidpi/refresh.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/hidpi/refresh.png diff --git a/ckeditor_add-on/skins/n1theme/images/lock-open.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/lock-open.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/lock-open.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/lock-open.png diff --git a/ckeditor_add-on/skins/n1theme/images/lock.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/lock.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/lock.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/lock.png diff --git a/ckeditor_add-on/skins/n1theme/images/refresh.png b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/refresh.png similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/refresh.png rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/refresh.png diff --git a/ckeditor_add-on/skins/n1theme/images/spinner.gif b/src/core/static/ckeditor/ckeditor/skins/n1theme/images/spinner.gif similarity index 100% rename from ckeditor_add-on/skins/n1theme/images/spinner.gif rename to src/core/static/ckeditor/ckeditor/skins/n1theme/images/spinner.gif diff --git a/ckeditor_add-on/skins/n1theme/readme.md b/src/core/static/ckeditor/ckeditor/skins/n1theme/readme.md similarity index 100% rename from ckeditor_add-on/skins/n1theme/readme.md rename to src/core/static/ckeditor/ckeditor/skins/n1theme/readme.md diff --git a/ckeditor_add-on/skins/n1theme/skin.js b/src/core/static/ckeditor/ckeditor/skins/n1theme/skin.js similarity index 100% rename from ckeditor_add-on/skins/n1theme/skin.js rename to src/core/static/ckeditor/ckeditor/skins/n1theme/skin.js diff --git a/src/core/views.py b/src/core/views.py deleted file mode 100644 index e69de29b..00000000 diff --git a/src/tests/unit/test_handlers/test_receive_assistance.py b/src/tests/unit/test_handlers/test_receive_assistance.py index 3a85a155..7e2f7ee8 100644 --- a/src/tests/unit/test_handlers/test_receive_assistance.py +++ b/src/tests/unit/test_handlers/test_receive_assistance.py @@ -1,4 +1,4 @@ -from unittest.mock import AsyncMock, patch +from unittest.mock import AsyncMock, Mock, patch import pytest @@ -24,13 +24,19 @@ async def test_get_assistance( "bot.handlers.assistance.BotSettings.objects.aget", AsyncMock(return_value=mocked_message), ), + patch( + "bot.handlers.assistance.parse_callback_data", + Mock(return_value=("get_assistance", 1)), + ), ): response = await get_assistance(update, context) update.callback_query.answer.assert_called_once() - update.callback_query.edit_message_text.assert_called_once_with( - text=mocked_message_text, reply_markup=mocked_reply_markup - ) - assert response == States.REGION, ( - f"Invalid state value, should be {States.REGION}", + + # update.callback_query.edit_message_text.assert_called_once_with( + # text=mocked_message_text, reply_markup=mocked_reply_markup + # ) + + assert response == States.GET_ASSISTANCE, ( + f"Invalid state value, should be {States.GET_ASSISTANCE}", ) diff --git a/src/users/admin.py b/src/users/admin.py index a61d7462..dff58ce4 100644 --- a/src/users/admin.py +++ b/src/users/admin.py @@ -1,6 +1,6 @@ from django.contrib import admin from django.contrib.auth.admin import UserAdmin as BaseUserAdmin -from django.contrib.auth.models import Group +from django.contrib.auth.models import Group, Permission from django.utils.translation import gettext_lazy as _ from utils.emailing.reset_password import send_password_reset_email @@ -46,22 +46,29 @@ class UserAdmin(BaseUserAdmin): ), ) list_display = ( - "id", - "first_name", - "last_name", + "get_fullname", "email", - "region", - "is_staff", "is_active", - "role", + "is_staff", + "is_superuser", + "last_login", ) - list_editable = ("role",) - search_fields = ( - "first_name", - "last_name", - "email", + search_fields = ("first_name", "last_name", "email") + ordering = ("last_name",) + + list_filter = ("is_active",) + + readonly_fields = ( + "date_joined", + "last_login", ) - ordering = ("region",) + + @admin.display(description="Имя и Фамилия") + def get_fullname(self, obj): + """Display fullname user in admin panel.""" + if not obj.first_name: + return "User" + return f"{obj.first_name} {obj.last_name}" @admin.action(description="Сбросить пароль") def reset_password(self, request, queryset): @@ -72,3 +79,23 @@ def reset_password(self, request, queryset): admin.site.register(User, UserAdmin) admin.site.unregister(Group) + + +def permissions_new_unicode(self): + """Translate default permissions.""" + class_name = str(self.content_type) + permissions_name = str(self.name) + + if "Can delete" in permissions_name: + permissions_name = "разрешено удалять" + elif "Can add" in permissions_name: + permissions_name = "разрешено добавлять" + elif "Can change" in permissions_name: + permissions_name = "разрешено изменять" + elif "Can view" in permissions_name: + permissions_name = "разрешено просматривать" + + return "%s - %s" % (class_name.title(), permissions_name) + + +Permission.__str__ = permissions_new_unicode diff --git a/src/users/migrations/0002_remove_user_region_remove_user_role.py b/src/users/migrations/0002_remove_user_region_remove_user_role.py new file mode 100644 index 00000000..80ec2c10 --- /dev/null +++ b/src/users/migrations/0002_remove_user_region_remove_user_role.py @@ -0,0 +1,22 @@ +# Generated by Django 4.2.4 on 2023-08-31 02:41 + +from django.db import migrations + + +class Migration(migrations.Migration): + """Migrations for users.""" + + dependencies = [ + ("users", "0001_initial"), + ] + + operations = [ + migrations.RemoveField( + model_name="user", + name="region", + ), + migrations.RemoveField( + model_name="user", + name="role", + ), + ] diff --git a/src/users/models.py b/src/users/models.py index 4f001a31..5d7a3fa3 100644 --- a/src/users/models.py +++ b/src/users/models.py @@ -2,8 +2,6 @@ from django.db import models from django.utils.translation import gettext_lazy as _ -from core.models import Region - from .manager import UserManager @@ -26,17 +24,6 @@ class UserRole(models.TextChoices): email = models.EmailField( unique=True, verbose_name="Адрес электронной почты" ) - role = models.CharField( - max_length=12, - choices=UserRole.choices, - verbose_name="Роль пользователя", - ) - region = models.ForeignKey( - Region, - on_delete=models.SET_NULL, - null=True, - verbose_name="Регион пользователя", - ) objects = UserManager()