From 43c04df22b9e6cfaf2c7922a0f4cfb2724cadf44 Mon Sep 17 00:00:00 2001 From: Md Eamin Hossain Date: Sun, 14 Jul 2024 12:48:49 +0600 Subject: [PATCH] Clean up redundant comments, commented code, and redundant imports --- interactive/admin.py | 4 +- interactive/api/permissions.py | 6 ++- interactive/api/serializers.py | 2 +- interactive/api/urls.py | 6 ++- interactive/api/views.py | 59 ++++++++++++---------- interactive/apps.py | 4 +- interactive/forms.py | 5 +- interactive/models.py | 89 ++++++++++++++------------------ interactive/services.py | 65 ++++++++++++------------ interactive/shortcode.py | 92 +++++++++++++++++----------------- interactive/tests/tests.py | 35 ++++++++----- interactive/wagtail_hooks.py | 15 +++--- 12 files changed, 198 insertions(+), 184 deletions(-) diff --git a/interactive/admin.py b/interactive/admin.py index 84a3e14f8..d9f2d3533 100644 --- a/interactive/admin.py +++ b/interactive/admin.py @@ -1,10 +1,8 @@ from django.contrib import admin -from django.contrib import admin - from .models import InteractiveChannel @admin.register(InteractiveChannel) class InteractiveChannelAdmin(admin.ModelAdmin): - list_display = ('id', 'display_name', 'request_url') + list_display = ("id", "display_name", "request_url") diff --git a/interactive/api/permissions.py b/interactive/api/permissions.py index 3d4400010..bcffff910 100644 --- a/interactive/api/permissions.py +++ b/interactive/api/permissions.py @@ -3,7 +3,9 @@ class IsRapidProGroupUser(permissions.BasePermission): - message = 'User is not allowed to access the webhook' + message = "User is not allowed to access the webhook" def has_permission(self, request, view): - return request.user.groups.filter(name=settings.RAPIDPRO_BOT_GROUP_NAME).exists() + return request.user.groups.filter( + name=settings.RAPIDPRO_BOT_GROUP_NAME + ).exists() diff --git a/interactive/api/serializers.py b/interactive/api/serializers.py index c0e8fc695..2d9c40d1c 100644 --- a/interactive/api/serializers.py +++ b/interactive/api/serializers.py @@ -11,5 +11,5 @@ class RapidProMessageSerializer(serializers.Serializer): def get_fields(self): fields = super().get_fields() - fields['from'] = fields.pop('from_') + fields["from"] = fields.pop("from_") return fields diff --git a/interactive/api/urls.py b/interactive/api/urls.py index 6fbf8d315..b2b574efa 100644 --- a/interactive/api/urls.py +++ b/interactive/api/urls.py @@ -2,8 +2,10 @@ from .views import RapidProWebhook -app_name = 'interactive_api' +app_name = "interactive_api" urlpatterns = [ - path('rapidpro-webhook/', RapidProWebhook.as_view(), name='rapidpro_message_webhook') + path( + "rapidpro-webhook/", RapidProWebhook.as_view(), name="rapidpro_message_webhook" + ) ] diff --git a/interactive/api/views.py b/interactive/api/views.py index cde660a21..5195e3be5 100644 --- a/interactive/api/views.py +++ b/interactive/api/views.py @@ -1,11 +1,13 @@ from django.utils.timezone import now +from rest_framework import status +from rest_framework.permissions import IsAuthenticated from rest_framework.response import Response from rest_framework.views import APIView + from interactive.models import Message -from rest_framework import status -from .serializers import RapidProMessageSerializer -from rest_framework.permissions import IsAuthenticated + from .permissions import IsRapidProGroupUser +from .serializers import RapidProMessageSerializer class RapidProWebhook(APIView): @@ -15,45 +17,50 @@ def post(self, request): serializer = RapidProMessageSerializer(data=request.data) serializer.is_valid(raise_exception=True) - # Extract validated data from serializer - rapidpro_message_id = serializer.validated_data.get('id') - to = serializer.validated_data.get('to') - text = serializer.validated_data.get('text') - quick_replies = serializer.validated_data.get('quick_replies') - from_field = serializer.validated_data.get('from') - channel = serializer.validated_data.get('channel') + rapidpro_message_id = serializer.validated_data.get("id") + to = serializer.validated_data.get("to") + text = serializer.validated_data.get("text") + quick_replies = serializer.validated_data.get("quick_replies") + from_field = serializer.validated_data.get("from") + channel = serializer.validated_data.get("channel") # Get the latest message for the 'to' recipient - prev_msg = Message.objects.filter(to=to, channel=channel).order_by('-created_at').first() + prev_msg = ( + Message.objects.filter(to=to, channel=channel) + .order_by("-created_at") + .first() + ) # Update or create a new message if prev_msg: prev_msg_text = prev_msg.text.strip() - if prev_msg_text.endswith('[CONTINUE]'): + if prev_msg_text.endswith("[CONTINUE]"): text = prev_msg_text + text else: text = text fields_to_update = { - 'rapidpro_message_id': rapidpro_message_id, - 'text': text, - 'quick_replies': quick_replies, - 'updated_at': now(), + "rapidpro_message_id": rapidpro_message_id, + "text": text, + "quick_replies": quick_replies, + "updated_at": now(), } - Message.objects.filter(rapidpro_message_id=prev_msg.rapidpro_message_id).update(**fields_to_update) + Message.objects.filter( + rapidpro_message_id=prev_msg.rapidpro_message_id + ).update(**fields_to_update) else: # Create a new message message_data = { - 'rapidpro_message_id': rapidpro_message_id, - 'text': text, - 'quick_replies': quick_replies, - 'to': to, - 'from_field': from_field, - 'channel': channel, - 'created_at': now(), - 'updated_at': now() + "rapidpro_message_id": rapidpro_message_id, + "text": text, + "quick_replies": quick_replies, + "to": to, + "from_field": from_field, + "channel": channel, + "created_at": now(), + "updated_at": now(), } Message.objects.create(**message_data) - return Response(data='ok', status=status.HTTP_200_OK) + return Response(data="ok", status=status.HTTP_200_OK) diff --git a/interactive/apps.py b/interactive/apps.py index 3a6e9a79f..9baa39c0a 100644 --- a/interactive/apps.py +++ b/interactive/apps.py @@ -2,5 +2,5 @@ class InteractiveConfig(AppConfig): - default_auto_field = 'django.db.models.BigAutoField' - name = 'interactive' + default_auto_field = "django.db.models.BigAutoField" + name = "interactive" diff --git a/interactive/forms.py b/interactive/forms.py index e7da56b8b..e1e48f361 100644 --- a/interactive/forms.py +++ b/interactive/forms.py @@ -2,4 +2,7 @@ class MessageSendForm(forms.Form): - text = forms.CharField(widget=forms.TextInput(attrs={'class': 'btn btn-outline-secondary'}), min_length=1) + text = forms.CharField( + widget=forms.TextInput(attrs={"class": "btn btn-outline-secondary"}), + min_length=1, + ) diff --git a/interactive/models.py b/interactive/models.py index 3b958f26c..ad6cca69f 100644 --- a/interactive/models.py +++ b/interactive/models.py @@ -1,34 +1,34 @@ import re -from time import sleep import time import uuid + +import requests +from django.contrib.auth import get_user_model from django.db import models -from django.http import HttpResponse from django.shortcuts import redirect, render from django.utils.translation import gettext_lazy as _ -import requests -from wagtail.models import Page from wagtail.admin.panels import FieldPanel -from home.mixins import PageUtilsMixin, TitleIconMixin -from django.contrib.auth import get_user_model +from wagtail.models import Page +from home.mixins import PageUtilsMixin, TitleIconMixin from interactive.forms import MessageSendForm from interactive.services import ShortCodeService -# from interactive.services import ShortCodeService - - class InteractiveChannel(models.Model): display_name = models.CharField( max_length=80, - help_text=_('Name for the interactive bot that the user will seen when interacting with it'), + help_text=_( + "Name for the interactive bot that the user will seen when interacting with it" + ), ) request_url = models.URLField( max_length=200, - help_text=_('To set up a interactive bot channel on your RapidPro server and get a request URL, ' - 'follow the steps outline in the Section "Setting up a Chatbot channel" ' - 'here: https://github.com/unicef/iogt/blob/develop/messaging/README.md'), + help_text=_( + "To set up a interactive bot channel on your RapidPro server and get a request URL, " + 'follow the steps outline in the Section "Setting up a Chatbot channel" ' + "here: https://github.com/unicef/iogt/blob/develop/messaging/README.md" + ), ) def __str__(self): @@ -47,23 +47,16 @@ class Message(models.Model): class InteractivePage(Page, PageUtilsMixin, TitleIconMixin): - parent_page_types = [ - "home.HomePage", "home.Section", 'home.FooterIndexPage' - ] + parent_page_types = ["home.HomePage", "home.Section", "home.FooterIndexPage"] subpage_types = [] - template = 'interactive/interactive_game.html' + template = "interactive/interactive_game.html" - # button_text = models.CharField(max_length=255) trigger_string = models.CharField(max_length=255) - channel = models.ForeignKey( - InteractiveChannel, - on_delete=models.PROTECT - ) + channel = models.ForeignKey(InteractiveChannel, on_delete=models.PROTECT) content_panels = Page.content_panels + [ - # FieldPanel('button_text'), - FieldPanel('trigger_string'), - FieldPanel('channel'), + FieldPanel("trigger_string"), + FieldPanel("channel"), ] def __str__(self): @@ -77,7 +70,7 @@ def serve(self, request, *args, **kwargs): user = self.get_user_identifier(request) if not user: - return redirect('/') + return redirect("/") if request.method == "POST": form = MessageSendForm(request.POST) @@ -85,28 +78,25 @@ def serve(self, request, *args, **kwargs): if form.is_valid(): channel_url = self.channel.request_url - data = { - 'from': user, - 'text': form.cleaned_data['text'] - } + data = {"from": user, "text": form.cleaned_data["text"]} try: response = requests.post(url=channel_url, data=data) response.raise_for_status() except requests.exceptions.RequestException as e: - return redirect('/') + return redirect("/") return redirect(self.get_url(request)) - return redirect(request.META.get('HTTP_REFERER', '/')) + return redirect(request.META.get("HTTP_REFERER", "/")) self.send_message_on_language_switch(request, user) context = self.get_context(request) - context['db_data'] = self.get_message_from_db(user=user) + context["db_data"] = self.get_message_from_db(user=user) - if not context['db_data']: - return redirect('/') + if not context["db_data"]: + return redirect("/") return render(request, self.template, context) @@ -114,7 +104,7 @@ def get_user_identifier(self, request): if not request.session.session_key: request.session.save() - user_uuid = request.session.setdefault('interactive_uuid', str(uuid.uuid4())) + user_uuid = request.session.setdefault("interactive_uuid", str(uuid.uuid4())) # Get the authenticated user user = request.user @@ -144,14 +134,14 @@ def get_message_from_db(self, user): if elapsed_time >= 5: break - chat = Message.objects.filter(to=user).order_by('-created_at').first() + chat = Message.objects.filter(to=user).order_by("-created_at").first() if not chat: return None text = chat.text.strip() # Check if the message has a next message indicator - if text.endswith('[CONTINUE]'): + if text.endswith("[CONTINUE]"): time.sleep(1) else: break # Exit the loop if the message does not end with '[CONTINUE]' @@ -165,33 +155,30 @@ def get_message_from_db(self, user): # Search for matches in the input string match = re.search(pattern, text) - bg_color = '' + bg_color = "" # Check if a match is found if match: # Extract the bg_color attributes - bg_color = match.group('bg_color') + bg_color = match.group("bg_color") # Remove the [bg_color] tag from the input string - text = re.sub(pattern, '', text) + text = re.sub(pattern, "", text) return { - 'message': text, - 'buttons': chat.quick_replies, - 'bg_color': bg_color, + "message": text, + "buttons": chat.quick_replies, + "bg_color": bg_color, } def send_message_on_language_switch(self, request, user): - current_lang = request.build_absolute_uri().split('/')[3] + current_lang = request.build_absolute_uri().split("/")[3] - if request.META.get('HTTP_REFERER'): - referer_lang = request.META.get('HTTP_REFERER').split('/')[3] + if request.META.get("HTTP_REFERER"): + referer_lang = request.META.get("HTTP_REFERER").split("/")[3] else: referer_lang = current_lang if referer_lang != current_lang: - data = { - 'from': user, - 'text': self.trigger_string - } + data = {"from": user, "text": self.trigger_string} response = requests.post(url=self.channel.request_url, data=data) diff --git a/interactive/services.py b/interactive/services.py index 9a79e3de7..9de34e84e 100644 --- a/interactive/services.py +++ b/interactive/services.py @@ -6,18 +6,19 @@ class ShortCodeService: def apply_shortcode(self, content): shortcode_service = Shortcode() - shortcode_service.add_shortcode('insert_message', self.message_callback) - shortcode_service.add_shortcode('insert_button', self.button_callback) - shortcode_service.add_shortcode('insert_form_field', self.form_callback) - shortcode_service.add_shortcode('insert_image', self.image_callback) - # shortcode_service.add_shortcode('insert_attachment_image', attachment_callback) - shortcode_service.add_shortcode('insert_header', self.header_callback) - shortcode_service.add_shortcode('insert_navbar', self.navbar_callback) - shortcode_service.add_shortcode('insert_html', self.html_callback) - shortcode_service.add_shortcode('insert_bg_color', self.header_callback) - shortcode_service.add_shortcode('CONTINUE', lambda attrs, content=None: '') - shortcode_service.add_shortcode('insert_trick_button', self.trick_button_callback) - shortcode_service.add_shortcode('insert_progress', self.progress_callback) + shortcode_service.add_shortcode("insert_message", self.message_callback) + shortcode_service.add_shortcode("insert_button", self.button_callback) + shortcode_service.add_shortcode("insert_form_field", self.form_callback) + shortcode_service.add_shortcode("insert_image", self.image_callback) + shortcode_service.add_shortcode("insert_header", self.header_callback) + shortcode_service.add_shortcode("insert_navbar", self.navbar_callback) + shortcode_service.add_shortcode("insert_html", self.html_callback) + shortcode_service.add_shortcode("insert_bg_color", self.header_callback) + shortcode_service.add_shortcode("CONTINUE", lambda attrs, content=None: "") + shortcode_service.add_shortcode( + "insert_trick_button", self.trick_button_callback + ) + shortcode_service.add_shortcode("insert_progress", self.progress_callback) return shortcode_service.do_shortcode(content) @@ -25,26 +26,26 @@ def message_callback(self, attrs, content): return f'

{content}

' def button_callback(self, attrs, content): - type = attrs.get("type", "submit") + button_type = attrs.get("type", "submit") message = attrs.get("message", content) - return f'' + return f'' def form_callback(self, attrs, content): - if attrs['type'] == 'input': + if attrs["type"] == "input": return f'' - return '' + return "" def image_callback(self, attrs, content): - class_value = attrs.get('class', '') - width_value = attrs.get('width', '150') - height_value = attrs.get('height', '') + class_value = attrs.get("class", "") + width_value = attrs.get("width", "150") + height_value = attrs.get("height", "") return f'' def header_callback(self, attrs, content=None): - button_html = '' - trigger_string = attrs.get('trigger_string', '') - button_text = attrs.get('button_text', '') + button_html = "" + trigger_string = attrs.get("trigger_string", "") + button_text = attrs.get("button_text", "") if button_text: button_html = f'' @@ -54,14 +55,14 @@ def html_callback(self, attrs, content): return content def navbar_callback(self, attrs, content=None): - button_group = '' + button_group = "" for x, y in attrs.items(): - button = '''''' + """ button_group += button.format(y, x.capitalize()) @@ -71,17 +72,17 @@ def trick_button_callback(self, attrs, content=None): image = attrs.get("img", "") hint = attrs.get("hint", "") status = attrs.get("status", "unlocked") - lock_img = attrs.get('lock_img', '') + lock_img = attrs.get("lock_img", "") message = attrs.get("message", content) - if status == 'locked': + if status == "locked": lock_btn_html = f'''' + """ if hint: hint = f'

Hint: {hint}

' @@ -97,6 +98,6 @@ def trick_button_callback(self, attrs, content=None): return button + hint def progress_callback(self, attrs, content): - return f'''
+ return f"""
-
''' +
""" diff --git a/interactive/shortcode.py b/interactive/shortcode.py index 045f9d97f..23e637f6d 100644 --- a/interactive/shortcode.py +++ b/interactive/shortcode.py @@ -6,19 +6,21 @@ def __init__(self): self.shortcode_tags = {} def add_shortcode(self, tag, callback): - if tag.strip() == '': - raise Exception('Invalid shortcode name: Empty name given.') + if tag.strip() == "": + raise Exception("Invalid shortcode name: Empty name given.") - if re.search(r'[<>&/\[\]\x00-\x20=]', tag): - reserved_chars = '& / < > [ ] =' - raise Exception(f"Invalid shortcode name: {tag}. Do not use spaces or reserved characters: {reserved_chars}") + if re.search(r"[<>&/\[\]\x00-\x20=]", tag): + reserved_chars = "& / < > [ ] =" + raise Exception( + f"Invalid shortcode name: {tag}. Do not use spaces or reserved characters: {reserved_chars}" + ) self.shortcode_tags[tag] = callback def do_shortcode(self, content, ignore_html=False): shortcode_tags = self.shortcode_tags - if '[' not in content: + if "[" not in content: return content if not shortcode_tags or not isinstance(shortcode_tags, dict): @@ -31,9 +33,9 @@ def do_shortcode_tag(m): output = m.group(1) + shortcode_tags[tag](attr, content) + m.group(6) return output - matches = re.findall(r'\[([^<>&/\[\]\x00-\x20=]+)', content) + matches = re.findall(r"\[([^<>&/\[\]\x00-\x20=]+)", content) tagnames = [tag for tag in matches if tag in shortcode_tags] - + pattern = self.get_shortcode_regex(tagnames) content = re.sub(pattern, do_shortcode_tag, content) @@ -45,47 +47,47 @@ def get_shortcode_regex(self, tagnames=None): if not tagnames: tagnames = list(shortcode_tags.keys()) - tagregexp = '|'.join(map(re.escape, tagnames)) - + tagregexp = "|".join(map(re.escape, tagnames)) + return ( - r'\[' # Opening bracket. - r'(\[?)' # 1: Optional second opening bracket for escaping shortcodes: [[tag]]. - f'({tagregexp})' # 2: Shortcode name. - r'(?![\w-])' # Not followed by word character or hyphen. - r'(' # 3: Unroll the loop: Inside the opening shortcode tag. - r'[^\]\/]*' # Not a closing bracket or forward slash. - r'(?:' - r'\/(?!])' # A forward slash not followed by a closing bracket. - r'[^\]\/]*' # Not a closing bracket or forward slash. - r')*?' - r')' - r'(?:' - r'(\/)' # 4: Self closing tag... - r'\]' # ...and closing bracket. - r'|' - r'\]' # Closing bracket. - r'(?:' - r'(' # 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags. - r'[^[]*' # Not an opening bracket. - r'(?:' - r'\[(?!\/\2\])' # An opening bracket not followed by the closing shortcode tag. - r'[^[]*' # Not an opening bracket. - r')*' - r')' - r'\[\/\2\]' # Closing shortcode tag. - r')?' - r')' - r'(\]?)' # 6: Optional second closing bracket for escaping shortcodes: [[tag]]. + r"\[" # Opening bracket. + r"(\[?)" # 1: Optional second opening bracket for escaping shortcodes: [[tag]]. + f"({tagregexp})" # 2: Shortcode name. + r"(?![\w-])" # Not followed by word character or hyphen. + r"(" # 3: Unroll the loop: Inside the opening shortcode tag. + r"[^\]\/]*" # Not a closing bracket or forward slash. + r"(?:" + r"\/(?!])" # A forward slash not followed by a closing bracket. + r"[^\]\/]*" # Not a closing bracket or forward slash. + r")*?" + r")" + r"(?:" + r"(\/)" # 4: Self closing tag... + r"\]" # ...and closing bracket. + r"|" + r"\]" # Closing bracket. + r"(?:" + r"(" # 5: Unroll the loop: Optionally, anything between the opening and closing shortcode tags. + r"[^[]*" # Not an opening bracket. + r"(?:" + r"\[(?!\/\2\])" # An opening bracket not followed by the closing shortcode tag. + r"[^[]*" # Not an opening bracket. + r")*" + r")" + r"\[\/\2\]" # Closing shortcode tag. + r")?" + r")" + r"(\]?)" # 6: Optional second closing bracket for escaping shortcodes: [[tag]]. ) def get_shortcode_atts_regex(self): return ( r'([\w-]+)\s*=\s*"([^"]*)"(?:\s|$)' - r'|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)' + r"|([\w-]+)\s*=\s*\'([^\']*)\'(?:\s|$)" r'|([\w-]+)\s*=\s*([^\s\'"]+)(?:\s|$)' r'|"([^"]*)"(?:\s|$)' - r'|\'([^\']*)\'(?:\s|$)' - r'|(\S+)(?:\s|$)' + r"|\'([^\']*)\'(?:\s|$)" + r"|(\S+)(?:\s|$)" ) def shortcode_parse_atts(self, text): @@ -93,7 +95,7 @@ def shortcode_parse_atts(self, text): pattern = self.get_shortcode_atts_regex() # text = re.sub(r'[\x00a0\x200b]+', ' ', text) matches = re.findall(pattern, text) - + for m in matches: if m[0]: atts[m[0].lower()] = m[1] @@ -108,7 +110,7 @@ def shortcode_parse_atts(self, text): elif m[8]: atts[m[8].lower()] = m[8] for key, value in atts.items(): - if '<' in value: - if not re.match(r'^[^<]*+(?:<[^>]*+>[^<]*+)*+$', value): - atts[key] = '' + if "<" in value: + if not re.match(r"^[^<]*+(?:<[^>]*+>[^<]*+)*+$", value): + atts[key] = "" return atts diff --git a/interactive/tests/tests.py b/interactive/tests/tests.py index 9c35b8e51..006adc699 100644 --- a/interactive/tests/tests.py +++ b/interactive/tests/tests.py @@ -1,11 +1,11 @@ -from django.test import TestCase from uuid import uuid4 -from rest_framework.test import APITestCase, APIClient + from django.core import management -from home.models import User -from rest_framework_simplejwt.tokens import RefreshToken from django.urls import reverse +from rest_framework.test import APIClient, APITestCase +from rest_framework_simplejwt.tokens import RefreshToken +from home.models import User from interactive.models import Message @@ -13,10 +13,12 @@ class RapidProWebhookTest(APITestCase): def setUp(self) -> None: username = uuid4() - management.call_command('sync_rapidpro_bot_user', username=username) + management.call_command("sync_rapidpro_bot_user", username=username) self.bot_user = User.objects.get(username=username) self.client = APIClient() - self.client.credentials(HTTP_AUTHORIZATION=f'Bearer {RefreshToken.for_user(self.bot_user).access_token}') + self.client.credentials( + HTTP_AUTHORIZATION=f"Bearer {RefreshToken.for_user(self.bot_user).access_token}" + ) def test_webhook_stitches_messages(self): @@ -28,8 +30,10 @@ def test_webhook_stitches_messages(self): "from": "abcd", "channel": "bd3577c6-65b1-4bb7-9611-306c11b1dcc5", "quick_replies": [ - "Baby (0 to 23 months old)", "Young child (2 to 9 years)", "Teenager (10 to 17 years)" - ] + "Baby (0 to 23 months old)", + "Young child (2 to 9 years)", + "Teenager (10 to 17 years)", + ], }, { "id": "1", @@ -38,19 +42,24 @@ def test_webhook_stitches_messages(self): "from": "abcd", "channel": "bd3577c6-65b1-4bb7-9611-306c11b1dcc5", "quick_replies": [ - "Baby (0 to 23 months old)", "Young child (2 to 9 years)", "Teenager (10 to 17 years)" - ] + "Baby (0 to 23 months old)", + "Young child (2 to 9 years)", + "Teenager (10 to 17 years)", + ], }, - ] for data in rapidpro_data_list: - response = self.client.post(path=reverse('interactive_api:rapidpro_message_webhook'), data=data, format='json') + response = self.client.post( + path=reverse("interactive_api:rapidpro_message_webhook"), + data=data, + format="json", + ) message = Message.objects.first() self.assertEqual(response.status_code, 200) self.assertEqual(Message.objects.count(), 1) - self.assertEqual(message.text, 'The second part starts here') + self.assertEqual(message.text, "The second part starts here") self.assertEqual(len(message.quick_replies), 3) diff --git a/interactive/wagtail_hooks.py b/interactive/wagtail_hooks.py index e4d7b2c43..dd0d8329b 100644 --- a/interactive/wagtail_hooks.py +++ b/interactive/wagtail_hooks.py @@ -1,19 +1,22 @@ from wagtail.contrib.modeladmin.options import ( - ModelAdminGroup, ModelAdmin, modeladmin_register + ModelAdmin, + ModelAdminGroup, + modeladmin_register, ) + from .models import InteractiveChannel class InteractiveChannelAdmin(ModelAdmin): model = InteractiveChannel - menu_label = 'Interactive RapidPro Channels' - menu_icon = 'tag' - index_template_name='interactive/interactivechannel/index.html' + menu_label = "Interactive RapidPro Channels" + menu_icon = "tag" + index_template_name = "interactive/interactivechannel/index.html" class InteractiveGroup(ModelAdminGroup): - menu_label = 'Interactive' - menu_icon = 'tag' + menu_label = "Interactive" + menu_icon = "tag" items = (InteractiveChannelAdmin,)