From dc2a771c994c5a7dad3b8453270ca2bfb74a414b Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 15 Aug 2019 11:28:25 +0200 Subject: Adding initial staff app to Django --- pydis_site/apps/staff/__init__.py | 0 pydis_site/apps/staff/apps.py | 7 +++++++ pydis_site/apps/staff/migrations/__init__.py | 0 pydis_site/apps/staff/models/__init__.py | 0 pydis_site/apps/staff/tests/__init__.py | 0 pydis_site/apps/staff/urls.py | 12 ++++++++++++ pydis_site/apps/staff/viewsets/__init__.py | 3 +++ pydis_site/apps/staff/viewsets/logs.py | 14 ++++++++++++++ pydis_site/hosts.py | 1 + pydis_site/settings.py | 1 + pydis_site/templates/staff/logs.html | 15 +++++++++++++++ pydis_site/urls.py | 1 + 12 files changed, 54 insertions(+) create mode 100644 pydis_site/apps/staff/__init__.py create mode 100644 pydis_site/apps/staff/apps.py create mode 100644 pydis_site/apps/staff/migrations/__init__.py create mode 100644 pydis_site/apps/staff/models/__init__.py create mode 100644 pydis_site/apps/staff/tests/__init__.py create mode 100644 pydis_site/apps/staff/urls.py create mode 100644 pydis_site/apps/staff/viewsets/__init__.py create mode 100644 pydis_site/apps/staff/viewsets/logs.py create mode 100644 pydis_site/templates/staff/logs.html (limited to 'pydis_site') diff --git a/pydis_site/apps/staff/__init__.py b/pydis_site/apps/staff/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pydis_site/apps/staff/apps.py b/pydis_site/apps/staff/apps.py new file mode 100644 index 00000000..fb8bda03 --- /dev/null +++ b/pydis_site/apps/staff/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class StaffConfig(AppConfig): + """Django AppConfig for the staff app.""" + + name = 'staff' \ No newline at end of file diff --git a/pydis_site/apps/staff/migrations/__init__.py b/pydis_site/apps/staff/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pydis_site/apps/staff/models/__init__.py b/pydis_site/apps/staff/models/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pydis_site/apps/staff/tests/__init__.py b/pydis_site/apps/staff/tests/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/pydis_site/apps/staff/urls.py b/pydis_site/apps/staff/urls.py new file mode 100644 index 00000000..95b3caf3 --- /dev/null +++ b/pydis_site/apps/staff/urls.py @@ -0,0 +1,12 @@ +from django.conf import settings +from django.conf.urls.static import static +from django.contrib import admin +from django.urls import include, path + +from .viewsets import LogView + +app_name = 'staff' +urlpatterns = [ + path('bot/logs//', LogView.as_view(), name="logs"), + path('admin/', admin.site.urls), +] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/pydis_site/apps/staff/viewsets/__init__.py b/pydis_site/apps/staff/viewsets/__init__.py new file mode 100644 index 00000000..ccb57d43 --- /dev/null +++ b/pydis_site/apps/staff/viewsets/__init__.py @@ -0,0 +1,3 @@ +from .logs import LogView + +__all__ = ["LogView"] \ No newline at end of file diff --git a/pydis_site/apps/staff/viewsets/logs.py b/pydis_site/apps/staff/viewsets/logs.py new file mode 100644 index 00000000..38f8bf1d --- /dev/null +++ b/pydis_site/apps/staff/viewsets/logs.py @@ -0,0 +1,14 @@ +from django.core.handlers.wsgi import WSGIRequest +from django.http import HttpResponse +from django.shortcuts import get_object_or_404, render +from django.views import View + +from pydis_site.apps.api.models.bot.message_deletion_context import MessageDeletionContext + + +class LogView(View): + template_name = "staff/logs.html" + + def get(self, request: WSGIRequest, pk: int) -> HttpResponse: + message_context = get_object_or_404(MessageDeletionContext, pk=pk) + return render(request, self.template_name, {"message_context": message_context}) diff --git a/pydis_site/hosts.py b/pydis_site/hosts.py index 0fa4793d..898e8cdc 100644 --- a/pydis_site/hosts.py +++ b/pydis_site/hosts.py @@ -5,5 +5,6 @@ host_patterns = patterns( '', host(r'admin', 'pydis_site.apps.admin.urls', name="admin"), host(r'api', 'pydis_site.apps.api.urls', name='api'), + host(r'staff', 'pydis_site.apps.staff.urls', name='staff'), host(r'.*', 'pydis_site.apps.home.urls', name=settings.DEFAULT_HOST) ) diff --git a/pydis_site/settings.py b/pydis_site/settings.py index 4a4eb94b..d3b371b6 100644 --- a/pydis_site/settings.py +++ b/pydis_site/settings.py @@ -63,6 +63,7 @@ else: INSTALLED_APPS = [ 'pydis_site.apps.api', 'pydis_site.apps.home', + 'pydis_site.apps.staff', 'django.contrib.admin', 'django.contrib.auth', diff --git a/pydis_site/templates/staff/logs.html b/pydis_site/templates/staff/logs.html new file mode 100644 index 00000000..5d7724c0 --- /dev/null +++ b/pydis_site/templates/staff/logs.html @@ -0,0 +1,15 @@ +{% extends 'base/base.html' %} +{% load static %} + +{% block title %}Home{% endblock %} + +{% block content %} +

Message Context

+

+ Actor: {{ message_context.actor }}
+ Date: {{ message_context.creation }}
+

+ {% for message in message_context.deletedmessage_set.all %} +

{{ message.content | safe }}

+ {% endfor %} +{% endblock %} \ No newline at end of file diff --git a/pydis_site/urls.py b/pydis_site/urls.py index c68375da..47cf0ba1 100644 --- a/pydis_site/urls.py +++ b/pydis_site/urls.py @@ -3,4 +3,5 @@ from django.urls import include, path urlpatterns = ( path('', include('pydis_site.apps.home.urls', namespace='home')), + path('staff/', include('pydis_site.apps.staff.urls', namespace='staff')), ) -- cgit v1.2.3 From 4fe7eae214952e9863498231d306a3e3a6323fa2 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Sat, 17 Aug 2019 12:05:55 +0200 Subject: Adding message display to frontend --- pydis_site/apps/staff/viewsets/logs.py | 3 ++- pydis_site/static/css/staff/logs.css | 43 ++++++++++++++++++++++++++++++++++ pydis_site/templates/staff/logs.html | 25 +++++++++++++------- 3 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 pydis_site/static/css/staff/logs.css (limited to 'pydis_site') diff --git a/pydis_site/apps/staff/viewsets/logs.py b/pydis_site/apps/staff/viewsets/logs.py index 38f8bf1d..d59847a3 100644 --- a/pydis_site/apps/staff/viewsets/logs.py +++ b/pydis_site/apps/staff/viewsets/logs.py @@ -11,4 +11,5 @@ class LogView(View): def get(self, request: WSGIRequest, pk: int) -> HttpResponse: message_context = get_object_or_404(MessageDeletionContext, pk=pk) - return render(request, self.template_name, {"message_context": message_context}) + messages = message_context.deletedmessage_set.all() + return render(request, self.template_name, {"message_context": message_context, "messages": messages}) diff --git a/pydis_site/static/css/staff/logs.css b/pydis_site/static/css/staff/logs.css new file mode 100644 index 00000000..ef271e1e --- /dev/null +++ b/pydis_site/static/css/staff/logs.css @@ -0,0 +1,43 @@ +main.site-content { + background-color: hsl(220, 8%, 23%); + color: #dcddde; + font-family: sans-serif; + font-size: 0.9375rem; + font-weight: 400; + line-height: 1.3; + letter-spacing: 0; + text-rendering: optimizeLegibility; +} + +.discord-message { + margin: 1rem; +} + +.discord-message-header { + white-space: nowrap; + letter-spacing: 0; +} + +.discord-username { + font-size: 1rem; + font-weight: 500; +} + +.discord-message-metadata { + color: hsla(0,0%,100%,.2); + font-size: 0.75rem; + font-weight: 400; + margin: 0 .3rem; +} + +.discord-channel { + font-size: 0.9rem; + font-weight: 500; + color: #dcddde; +} + +.discord-id { + color: #dcddde; + font-weight: 300; + margin-left: 0.3rem; +} \ No newline at end of file diff --git a/pydis_site/templates/staff/logs.html b/pydis_site/templates/staff/logs.html index 5d7724c0..66b42f6a 100644 --- a/pydis_site/templates/staff/logs.html +++ b/pydis_site/templates/staff/logs.html @@ -1,15 +1,22 @@ {% extends 'base/base.html' %} {% load static %} -{% block title %}Home{% endblock %} +{% block title %}Logs for Deleted Message Context {{ message_context.id }}{% endblock %} + +{% block head %} + +{% endblock %} {% block content %} -

Message Context

-

- Actor: {{ message_context.actor }}
- Date: {{ message_context.creation }}
-

- {% for message in message_context.deletedmessage_set.all %} -

{{ message.content | safe }}

- {% endfor %} + + {% for message in messages %} +
+
+ {{ message.author.name }}#{{ message.author.discriminator }} +
+
+ {{ message.content|linebreaks }} +
+
+ {% endfor %} {% endblock %} \ No newline at end of file -- cgit v1.2.3 From 8da376b0ebff72db9f72b7026b6f2fef4dff4f13 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 22 Aug 2019 15:44:06 +0200 Subject: Making the deleted-messages-frontend functional with changes, including: - Adding support for embeds to both the template and the css; - Adding Discord fonts to create a Discord-realistic rendering; - Adding Discord color int to html hex filter for use in templates; - Removing unnecessary int -> hex property from role model (see previous point); - Adding support to compute timestamp from snowflake int in the message model; - Forcing the order of deleted messages list view to snowflake `id` to guarantee chronological order. --- pydis_site/apps/api/models/bot/deleted_message.py | 5 + pydis_site/apps/api/models/bot/message.py | 12 ++ pydis_site/apps/staff/apps.py | 2 +- pydis_site/apps/staff/templatetags/__init__.py | 3 + .../staff/templatetags/deletedmessage_filters.py | 17 ++ pydis_site/apps/staff/urls.py | 4 +- pydis_site/apps/staff/viewsets/__init__.py | 2 +- pydis_site/apps/staff/viewsets/logs.py | 20 +- pydis_site/static/css/staff/logs.css | 215 ++++++++++++++++++++- pydis_site/templates/staff/logs.html | 76 +++++++- 10 files changed, 346 insertions(+), 10 deletions(-) create mode 100644 pydis_site/apps/staff/templatetags/__init__.py create mode 100644 pydis_site/apps/staff/templatetags/deletedmessage_filters.py (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/deleted_message.py b/pydis_site/apps/api/models/bot/deleted_message.py index eb7f4c89..1eb4516e 100644 --- a/pydis_site/apps/api/models/bot/deleted_message.py +++ b/pydis_site/apps/api/models/bot/deleted_message.py @@ -12,3 +12,8 @@ class DeletedMessage(Message): help_text="The deletion context this message is part of.", on_delete=models.CASCADE ) + + class Meta: + """Sets the default ordering for list views to oldest first.""" + + ordering = ["id"] diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index 6b566620..0713b9d2 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -1,6 +1,11 @@ +from datetime import datetime + +import pytz from django.contrib.postgres import fields as pgfields from django.core.validators import MinValueValidator from django.db import models +from django.utils import timezone + from pydis_site.apps.api.models.bot.tag import validate_tag_embed from pydis_site.apps.api.models.bot.user import User @@ -49,6 +54,13 @@ class Message(ModelReprMixin, models.Model): help_text="Embeds attached to this message." ) + @property + def timestamp(self) -> datetime: + """Attribute that represents the message timestamp as derived from the snowflake id.""" + tz_naive_datetime = datetime.utcfromtimestamp(((self.id >> 22) + 1420070400000) / 1000) + tz_aware_datetime = timezone.make_aware(tz_naive_datetime, timezone=pytz.timezone("UTC")) + return tz_aware_datetime + class Meta: """Metadata provided for Django's ORM.""" diff --git a/pydis_site/apps/staff/apps.py b/pydis_site/apps/staff/apps.py index fb8bda03..70a15f40 100644 --- a/pydis_site/apps/staff/apps.py +++ b/pydis_site/apps/staff/apps.py @@ -4,4 +4,4 @@ from django.apps import AppConfig class StaffConfig(AppConfig): """Django AppConfig for the staff app.""" - name = 'staff' \ No newline at end of file + name = 'staff' diff --git a/pydis_site/apps/staff/templatetags/__init__.py b/pydis_site/apps/staff/templatetags/__init__.py new file mode 100644 index 00000000..e8b6983a --- /dev/null +++ b/pydis_site/apps/staff/templatetags/__init__.py @@ -0,0 +1,3 @@ +from .deletedmessage_filters import footer_datetime, hex_colour + +__all__ = ["hex_colour", "footer_datetime"] diff --git a/pydis_site/apps/staff/templatetags/deletedmessage_filters.py b/pydis_site/apps/staff/templatetags/deletedmessage_filters.py new file mode 100644 index 00000000..f950870f --- /dev/null +++ b/pydis_site/apps/staff/templatetags/deletedmessage_filters.py @@ -0,0 +1,17 @@ +from datetime import datetime + +from django import template + +register = template.Library() + + +@register.filter +def hex_colour(color: int) -> str: + """Converts an integer representation of a colour to the RGB hex value.""" + return f"#{color:0>6X}" + + +@register.filter +def footer_datetime(timestamp: str) -> datetime: + """Takes an embed timestamp and returns a timezone-aware datetime object.""" + return datetime.fromisoformat(timestamp) diff --git a/pydis_site/apps/staff/urls.py b/pydis_site/apps/staff/urls.py index 95b3caf3..a564d516 100644 --- a/pydis_site/apps/staff/urls.py +++ b/pydis_site/apps/staff/urls.py @@ -1,12 +1,10 @@ from django.conf import settings from django.conf.urls.static import static -from django.contrib import admin -from django.urls import include, path +from django.urls import path from .viewsets import LogView app_name = 'staff' urlpatterns = [ path('bot/logs//', LogView.as_view(), name="logs"), - path('admin/', admin.site.urls), ] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/pydis_site/apps/staff/viewsets/__init__.py b/pydis_site/apps/staff/viewsets/__init__.py index ccb57d43..6b10eb83 100644 --- a/pydis_site/apps/staff/viewsets/__init__.py +++ b/pydis_site/apps/staff/viewsets/__init__.py @@ -1,3 +1,3 @@ from .logs import LogView -__all__ = ["LogView"] \ No newline at end of file +__all__ = ["LogView"] diff --git a/pydis_site/apps/staff/viewsets/logs.py b/pydis_site/apps/staff/viewsets/logs.py index d59847a3..0898d606 100644 --- a/pydis_site/apps/staff/viewsets/logs.py +++ b/pydis_site/apps/staff/viewsets/logs.py @@ -1,3 +1,5 @@ +import logging + from django.core.handlers.wsgi import WSGIRequest from django.http import HttpResponse from django.shortcuts import get_object_or_404, render @@ -5,11 +7,27 @@ from django.views import View from pydis_site.apps.api.models.bot.message_deletion_context import MessageDeletionContext +log = logging.getLogger(__name__) + class LogView(View): + """The default view for the Deleted Messages logs.""" + template_name = "staff/logs.html" def get(self, request: WSGIRequest, pk: int) -> HttpResponse: + """Get method that answers a request with an html response by rendering a template.""" message_context = get_object_or_404(MessageDeletionContext, pk=pk) + + actor = message_context.actor + creation = message_context.creation messages = message_context.deletedmessage_set.all() - return render(request, self.template_name, {"message_context": message_context, "messages": messages}) + + template_fields = { + 'actor': actor, + 'actor_colour': message_context.actor.top_role.colour, + 'creation': creation, + 'messages': messages + } + + return render(request, self.template_name, template_fields) diff --git a/pydis_site/static/css/staff/logs.css b/pydis_site/static/css/staff/logs.css index ef271e1e..a09d33ac 100644 --- a/pydis_site/static/css/staff/logs.css +++ b/pydis_site/static/css/staff/logs.css @@ -1,16 +1,31 @@ main.site-content { background-color: hsl(220, 8%, 23%); color: #dcddde; - font-family: sans-serif; font-size: 0.9375rem; font-weight: 400; line-height: 1.3; letter-spacing: 0; text-rendering: optimizeLegibility; + padding: 1rem; + font-family: Whitney,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif; +} + +.has-small-margin { + margin: 1rem 0; +} + +.deleted-header { + font-weight: 700; + margin-top: 1rem; } .discord-message { - margin: 1rem; + margin-bottom: 15px; +} + +.discord-message:first-child { + border-top: 1px; + } .discord-message-header { @@ -20,7 +35,7 @@ main.site-content { .discord-username { font-size: 1rem; - font-weight: 500; + font-weight: 600; } .discord-message-metadata { @@ -40,4 +55,198 @@ main.site-content { color: #dcddde; font-weight: 300; margin-left: 0.3rem; +} + +.discord-embed { + + position: relative; + margin-top: 5px; + max-width: 520px; + display: flex; +} + +.discord-embed a { + text-decoration: none; + color: hsl(197, 100%, 41%); +} + +.discord-embed a:hover { + text-decoration: underline; + color: hsl(197, 100%, 41%); +} + +.discord-embed-color { + width: 4px; + border-radius: 3px 0 0 3px; + flex-shrink: 0; +} + +.discord-embed-inner { + background-color: #34363b; + padding: 8px 10px; + border-radius: 0 3px 3px 0; + box-sizing: border-box; + border: 1px solid hsla(225, 8%, 20%, 0.6); + display: flex; + flex-direction: column; +} + +.discord-embed-content { + width: 100%; + display: flex; +} + +.discord-embed-main { + flex: 1; +} + +.discord-embed-thumbnail > img { + max-width: 80px; + max-height: 80px; + border-radius: 3px; + width: auto; + object-fit: contain; + margin-left: 20px; + flex-shrink: 0; + border-style: none; +} + +.discord-embed-author { + display: flex; + align-items: center; + margin-bottom: 5px; + font-weight: 600; + font-size: 14px; + line-height: 1.15; +} + +.discord-embed-author-icon { + margin-right: 9px; + width: 20px; + height: 20px; + object-fit: contain; + border-radius: 50%; +} + +.discord-embed-author a { + color: white; +} + +.discord-embed-author a:hover { + color: white; +} + +.discord-embed-title { + margin-bottom: 5px; + font-size: 14px; + display: inline-block; + font-weight: 600; +} + +.discord-embed-description { + margin-bottom: 10px; +} + +.discord-embed-fields { + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin-top: -10px; + margin-bottom: 10px; +} + +.discord-embed-field { + flex: 0; + padding-top: 5px; + min-width: 100%; + max-width: 506px; +} + +.discord-embed-field-name { + margin-bottom: 4px; + font-weight: 600; +} + +.discord-embed-field-value { + font-weight: 500; +} + +.discord-embed-field-inline { + flex: 1; + min-width: 150px; + flex-basis: auto; +} + +.discord-embed-main > :last-child { + margin-bottom: 0 !important; +} + +.discord-embed-image { + position: relative; + display: inline-block; + margin-bottom: 10px; +} + +.discord-embed-image > img { + margin: 0; + vertical-align: bottom; + max-width: 300px; + display: flex; + overflow: hidden; + border-radius: 2px; +} + +.discord-embed-footer-text { + font-size: .70rem !important; + letter-spacing: 0; + display: inline-block; +} + +.discord-embed-footer-icon { + margin-right: 10px; + height: 18px; + width: 18px; + object-fit: contain; + float: left; + border-radius: 50%; +} + +.discord-embed-content { + margin-bottom: 10px; +} + +.discord-embed-inner > :last-child { + margin-bottom: 0 !important; +} + +/* Discord Font definitions */ +@font-face { + font-family: Whitney; + font-style: light; + font-weight:300; + src:url(https://discordapp.com/assets/6c6374bad0b0b6d204d8d6dc4a18d820.woff) format("woff") +} +@font-face { + font-family: Whitney; + font-style: normal; + font-weight:500; + src:url(https://discordapp.com/assets/e8acd7d9bf6207f99350ca9f9e23b168.woff) format("woff") +} +@font-face { + font-family:Whitney; + font-style: medium; + font-weight:600; + src:url(https://discordapp.com/assets/3bdef1251a424500c1b3a78dea9b7e57.woff) format("woff") +} +@font-face { + font-family: WhitneyMedium; + font-style: medium; + font-weight: 600; + src:url(https://discordapp.com/assets/be0060dafb7a0e31d2a1ca17c0708636.woff) format("woff") +} +@font-face { + font-family: Whitney; + font-style: bold; + font-weight: 700; + src:url(https://discordapp.com/assets/8e12fb4f14d9c4592eb8ec9f22337b04.woff) format("woff") } \ No newline at end of file diff --git a/pydis_site/templates/staff/logs.html b/pydis_site/templates/staff/logs.html index 66b42f6a..49d9c368 100644 --- a/pydis_site/templates/staff/logs.html +++ b/pydis_site/templates/staff/logs.html @@ -1,5 +1,6 @@ {% extends 'base/base.html' %} {% load static %} +{% load deletedmessage_filters %} {% block title %}Logs for Deleted Message Context {{ message_context.id }}{% endblock %} @@ -8,15 +9,88 @@ {% endblock %} {% block content %} + +
{% for message in messages %}
- {{ message.author.name }}#{{ message.author.discriminator }} + {{ message.author }}
{{ message.content|linebreaks }}
+ + {% for embed in message.embeds %} +
+
+
+
+
+
+ + {% if embed.author %} +
+ {% if embed.author.icon_url %}Author Icon{% endif %} + {% if embed.author.url %}{% endif %} + {{ embed.author.name }} + {% if embed.author.url %}{% endif %} +
+ {% endif %} + + {% if embed.title %} +
+ {% if embed.url %}{% endif %} + {{ embed.title }} + {% if embed.url %}{% endif %} +
+ {% endif %} + + {% if embed.description %} +
+ {{ embed.description | linebreaksbr }} +
+ {% endif %} + + {% if embed.fields %} +
+ {% for field in embed.fields %} +
+
{{ field.name }}
+
{{ field.value }}
+
+ {% endfor %} +
+ {% endif %} + {% if embed.image %} +
+ Discord Embed Image +
+ {% endif %} +
+ {% if embed.thumbnail %} +
+ Embed thumbnail +
+ {% endif %} +
+ {% if embed.footer or embed.timestamp %} + + {% endif %} +
+
+ {% endfor %}
{% endfor %} {% endblock %} \ No newline at end of file -- cgit v1.2.3 From ea8c1fb0680baa2f1e13a283ea36fffc8e15b0e1 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 22 Aug 2019 15:49:56 +0200 Subject: Adding missing 'inline' parameter to embed fields attribute validator --- pydis_site/apps/api/models/bot/tag.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/tag.py b/pydis_site/apps/api/models/bot/tag.py index 01b49525..0dc4830c 100644 --- a/pydis_site/apps/api/models/bot/tag.py +++ b/pydis_site/apps/api/models/bot/tag.py @@ -1,4 +1,5 @@ from collections.abc import Mapping +from typing import Any from django.contrib.postgres import fields as pgfields from django.core.exceptions import ValidationError @@ -8,11 +9,18 @@ from django.db import models from pydis_site.apps.api.models.utils import ModelReprMixin +def is_bool_validator(value: Any) -> None: + """Validates if a given value is of type bool.""" + if not isinstance(value, bool): + raise ValidationError(f"This field must be of type bool, not {type(value)}.") + + def validate_tag_embed_fields(fields): """Raises a ValidationError if any of the given embed fields is invalid.""" field_validators = { 'name': (MaxLengthValidator(limit_value=256),), - 'value': (MaxLengthValidator(limit_value=1024),) + 'value': (MaxLengthValidator(limit_value=1024),), + 'inline': (is_bool_validator,), } for field in fields: -- cgit v1.2.3 From 8066a3360b23508d3fb2da3d37d2c63f2630faff Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 22 Aug 2019 20:38:51 +0200 Subject: Adding required fields validator and tests to embed fields --- pydis_site/apps/api/models/bot/tag.py | 5 ++++ pydis_site/apps/api/tests/test_validators.py | 39 ++++++++++++++++++++++++++-- 2 files changed, 42 insertions(+), 2 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/tag.py b/pydis_site/apps/api/models/bot/tag.py index 0dc4830c..fc0db4e4 100644 --- a/pydis_site/apps/api/models/bot/tag.py +++ b/pydis_site/apps/api/models/bot/tag.py @@ -23,10 +23,15 @@ def validate_tag_embed_fields(fields): 'inline': (is_bool_validator,), } + required_fields = ('name', 'value') + for field in fields: if not isinstance(field, Mapping): raise ValidationError("Embed fields must be a mapping.") + if not all(required_field in field for required_field in required_fields): + raise ValidationError("Embed fields must contain both a 'name' and a 'value' field.") + for field_name, value in field.items(): if field_name not in field_validators: raise ValidationError(f"Unknown embed field field: {field_name!r}.") diff --git a/pydis_site/apps/api/tests/test_validators.py b/pydis_site/apps/api/tests/test_validators.py index ffa2f61e..4222f0c0 100644 --- a/pydis_site/apps/api/tests/test_validators.py +++ b/pydis_site/apps/api/tests/test_validators.py @@ -119,18 +119,53 @@ class TagEmbedValidatorTests(TestCase): 'value': "LOOK AT ME" }, { + 'name': "Totally valid", + 'value': "LOOK AT ME", 'oh': "what is this key?" } ] }) + def test_rejects_missing_required_field_field(self): + with self.assertRaises(ValidationError): + validate_tag_embed({ + 'fields': [ + { + 'name': "Totally valid", + 'inline': True, + } + ] + }) + + def test_rejects_invalid_inline_field_field(self): + with self.assertRaises(ValidationError): + validate_tag_embed({ + 'fields': [ + { + 'name': "Totally valid", + 'value': "LOOK AT ME", + 'inline': "Totally not a boolean", + } + ] + }) + def test_allows_valid_fields(self): validate_tag_embed({ 'fields': [ { 'name': "valid", - 'value': "field" - } + 'value': "field", + }, + { + 'name': "valid", + 'value': "field", + 'inline': False, + }, + { + 'name': "valid", + 'value': "field", + 'inline': True, + }, ] }) -- cgit v1.2.3 From b016dbd5431180a183cc99d1841506cc3d240e04 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Thu, 22 Aug 2019 20:55:57 +0200 Subject: Adding tests for filters and the deleted message front-end view --- .../staff/tests/test_deletedmessage_filters.py | 21 +++++++ pydis_site/apps/staff/tests/test_logs_view.py | 64 ++++++++++++++++++++++ 2 files changed, 85 insertions(+) create mode 100644 pydis_site/apps/staff/tests/test_deletedmessage_filters.py create mode 100644 pydis_site/apps/staff/tests/test_logs_view.py (limited to 'pydis_site') diff --git a/pydis_site/apps/staff/tests/test_deletedmessage_filters.py b/pydis_site/apps/staff/tests/test_deletedmessage_filters.py new file mode 100644 index 00000000..d064b8d9 --- /dev/null +++ b/pydis_site/apps/staff/tests/test_deletedmessage_filters.py @@ -0,0 +1,21 @@ +from django.test import TestCase +from django.utils import timezone + +from ..templatetags import deletedmessage_filters + + +class DeletedMessageFilterTests(TestCase): + def test_hex_colour_filter(self): + self.assertEqual(deletedmessage_filters.hex_colour(0), "#000000") + self.assertEqual(deletedmessage_filters.hex_colour(255), "#0000FF") + self.assertEqual(deletedmessage_filters.hex_colour(65280), "#00FF00") + self.assertEqual(deletedmessage_filters.hex_colour(16711680), "#FF0000") + self.assertEqual(deletedmessage_filters.hex_colour(16777215), "#FFFFFF") + + def test_footer_datetime_filter(self): + datetime_aware = timezone.now() + iso_string = datetime_aware.isoformat() + + datetime_returned = deletedmessage_filters.footer_datetime(iso_string) + self.assertTrue(timezone.is_aware(datetime_returned)) + self.assertEqual(datetime_aware, datetime_returned) diff --git a/pydis_site/apps/staff/tests/test_logs_view.py b/pydis_site/apps/staff/tests/test_logs_view.py new file mode 100644 index 00000000..b5f7bb24 --- /dev/null +++ b/pydis_site/apps/staff/tests/test_logs_view.py @@ -0,0 +1,64 @@ +from django.test import Client, TestCase +from django.utils import timezone +from django_hosts.resolvers import reverse, reverse_host + +from pydis_site.apps.api.models.bot import DeletedMessage, MessageDeletionContext, Role, User +from ..templatetags.deletedmessage_filters import hex_colour + + +class TestLogsView(TestCase): + @classmethod + def setUpTestData(cls): + cls.developers_role = Role.objects.create( + id=12345678, + name="Developers", + colour=16777215, + permissions=104324673, + position=1, + ) + + cls.author = cls.actor = User.objects.create( + id=12345678901, + name='Alan Turing', + discriminator=1912, + avatar_hash=None + ) + + cls.author.roles.add(cls.developers_role) + + cls.deletion_context = MessageDeletionContext.objects.create( + actor=cls.actor, + creation=timezone.now() + ) + + cls.deleted_message = DeletedMessage.objects.create( + author=cls.author, + id=614125807161573397, + channel_id=1984, + content='I think my tape has run out...', + embeds=[], + deletion_context=cls.deletion_context, + ) + + def setUp(self): + """Sets up a test client that automatically sets the correct HOST header.""" + self.client = Client(HTTP_HOST=reverse_host(host="staff")) + + def test_logs_returns_200_for_existing_logs_pk(self): + url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + response = self.client.get(url) + self.assertEqual(response.status_code, 200) + + def test_logs_returns_404_for_nonexisting_logs_pk(self): + url = reverse('logs', host="staff", args=(self.deletion_context.id + 100,)) + response = self.client.get(url) + self.assertEqual(response.status_code, 404) + + def test_author_color_is_set_in_response(self): + url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + response = self.client.get(url) + role_colour = hex_colour(self.developers_role.colour) + html_needle = ( + f'{self.author}' + ) + self.assertInHTML(html_needle, response.content.decode()) -- cgit v1.2.3 From bf75fd6c450dc1566f9ac0c25bf3db2e4be872a0 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Fri, 23 Aug 2019 15:38:54 +0200 Subject: Adding tests for deleted message view Tests include: - Properly escaping html; - Correct messages have been passed to templates; - Embeds are both rendered by the template. --- pydis_site/apps/staff/tests/test_logs_view.py | 100 +++++++++++++++++++++++++- 1 file changed, 98 insertions(+), 2 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/staff/tests/test_logs_view.py b/pydis_site/apps/staff/tests/test_logs_view.py index b5f7bb24..4fc38c7a 100644 --- a/pydis_site/apps/staff/tests/test_logs_view.py +++ b/pydis_site/apps/staff/tests/test_logs_view.py @@ -31,15 +31,79 @@ class TestLogsView(TestCase): creation=timezone.now() ) - cls.deleted_message = DeletedMessage.objects.create( + cls.deleted_message_one = DeletedMessage.objects.create( author=cls.author, id=614125807161573397, channel_id=1984, - content='I think my tape has run out...', + content='I think my tape has run out...', embeds=[], deletion_context=cls.deletion_context, ) + cls.embed_one = { + "footer": { + "text": "This will be displayed in the footer!", + "icon_url": "https://avatars0.githubusercontent.com/u/33516116?s=460&v=4" + }, + "image": { + "url": "https://avatars0.githubusercontent.com/u/33516116?s=460&v=4" + }, + "thumbnail": { + "url": "https://avatars0.githubusercontent.com/u/33516116?s=460&v=4" + }, + "author": { + "name": "Ves Zappa", + "url": "https://pydis.com", + "icon_url": "https://avatars0.githubusercontent.com/u/33516116?s=460&v=4" + }, + "fields": [ + { + "inline": False, + "name": "Field Name 1", + "value": "Field Value 1" + }, + { + "inline": False, + "name": "Field Name 2", + "value": "Field Value 2" + }, + { + "inline": True, + "name": "Field Name 3", + "value": "Field Value 3" + }, + { + "inline": True, + "name": "Field Name 4", + "value": "Field Value 4" + }, + { + "inline": True, + "name": "Field Name 5", + "value": "Field Value 5" + } + ], + "color": 16711680, + "timestamp": "2019-08-21T13:58:34.480053+00:00", + "type": "rich", + "description": "This embed is way too cool to be seen in public channels.", + "url": "https://pythondiscord.com/", + "title": "Hello, PyDis" + } + + cls.embed_two = { + "description": "This embed is way too cool to be seen in public channels.", + } + + cls.deleted_message_two = DeletedMessage.objects.create( + author=cls.author, + id=614444836291870750, + channel_id=1984, + content='Does that mean this thing will halt?', + embeds=[cls.embed_one, cls.embed_two], + deletion_context=cls.deletion_context, + ) + def setUp(self): """Sets up a test client that automatically sets the correct HOST header.""" self.client = Client(HTTP_HOST=reverse_host(host="staff")) @@ -62,3 +126,35 @@ class TestLogsView(TestCase): f'{self.author}' ) self.assertInHTML(html_needle, response.content.decode()) + + def test_correct_messages_have_been_passed_to_template(self): + url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + response = self.client.get(url) + self.assertIn("messages", response.context) + self.assertListEqual( + [self.deleted_message_one, self.deleted_message_two], + list(response.context["messages"]) + ) + + def test_if_both_embeds_are_included_html_response(self): + url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + response = self.client.get(url) + + html_response = response.content.decode() + embed_colour_needle = ( + '
' + ) + embed_one_colour = hex_colour(self.embed_one["color"]) + embed_two_colour = "#cacbce" + self.assertInHTML(embed_colour_needle.format(colour=embed_one_colour), html_response) + self.assertInHTML(embed_colour_needle.format(colour=embed_two_colour), html_response) + + def test_if_html_in_content_is_properly_escaped(self): + url = reverse('logs', host="staff", args=(self.deletion_context.id,)) + response = self.client.get(url) + + html_response = response.content.decode() + unescaped_content = "I think my tape has run out..." + self.assertInHTML(unescaped_content, html_response, count=0) + escaped_content = "<em>I think my tape has run out...</em>" + self.assertInHTML(escaped_content, html_response, count=1) -- cgit v1.2.3 From 92656a208825527fa1bd6ecb5a2294db9e96c9b0 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Fri, 23 Aug 2019 15:55:34 +0200 Subject: Fixing inconsistent indentation style in logs template and logs css --- pydis_site/static/css/staff/logs.css | 148 ++++++++++++++++++----------------- pydis_site/templates/staff/logs.html | 133 +++++++++++++++---------------- 2 files changed, 143 insertions(+), 138 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/static/css/staff/logs.css b/pydis_site/static/css/staff/logs.css index a09d33ac..d7bb04cf 100644 --- a/pydis_site/static/css/staff/logs.css +++ b/pydis_site/static/css/staff/logs.css @@ -7,7 +7,7 @@ main.site-content { letter-spacing: 0; text-rendering: optimizeLegibility; padding: 1rem; - font-family: Whitney,Helvetica Neue,Helvetica,Arial,Lucida Grande,sans-serif; + font-family: Whitney, Helvetica Neue, Helvetica, Arial, Lucida Grande, sans-serif; } .has-small-margin { @@ -39,7 +39,7 @@ main.site-content { } .discord-message-metadata { - color: hsla(0,0%,100%,.2); + color: hsla(0, 0%, 100%, .2); font-size: 0.75rem; font-weight: 400; margin: 0 .3rem; @@ -59,10 +59,10 @@ main.site-content { .discord-embed { - position: relative; - margin-top: 5px; - max-width: 520px; - display: flex; + position: relative; + margin-top: 5px; + max-width: 520px; + display: flex; } .discord-embed a { @@ -76,24 +76,24 @@ main.site-content { } .discord-embed-color { - width: 4px; - border-radius: 3px 0 0 3px; - flex-shrink: 0; + width: 4px; + border-radius: 3px 0 0 3px; + flex-shrink: 0; } .discord-embed-inner { background-color: #34363b; - padding: 8px 10px; + padding: 8px 10px; border-radius: 0 3px 3px 0; - box-sizing: border-box; - border: 1px solid hsla(225, 8%, 20%, 0.6); - display: flex; - flex-direction: column; + box-sizing: border-box; + border: 1px solid hsla(225, 8%, 20%, 0.6); + display: flex; + flex-direction: column; } .discord-embed-content { - width: 100%; - display: flex; + width: 100%; + display: flex; } .discord-embed-main { @@ -121,11 +121,11 @@ main.site-content { } .discord-embed-author-icon { - margin-right: 9px; - width: 20px; - height: 20px; - object-fit: contain; - border-radius: 50%; + margin-right: 9px; + width: 20px; + height: 20px; + object-fit: contain; + border-radius: 50%; } .discord-embed-author a { @@ -148,33 +148,33 @@ main.site-content { } .discord-embed-fields { - display: flex; - flex-direction: row; - flex-wrap: wrap; - margin-top: -10px; - margin-bottom: 10px; + display: flex; + flex-direction: row; + flex-wrap: wrap; + margin-top: -10px; + margin-bottom: 10px; } .discord-embed-field { - flex: 0; - padding-top: 5px; - min-width: 100%; - max-width: 506px; + flex: 0; + padding-top: 5px; + min-width: 100%; + max-width: 506px; } .discord-embed-field-name { - margin-bottom: 4px; - font-weight: 600; + margin-bottom: 4px; + font-weight: 600; } .discord-embed-field-value { - font-weight: 500; + font-weight: 500; } .discord-embed-field-inline { - flex: 1; - min-width: 150px; - flex-basis: auto; + flex: 1; + min-width: 150px; + flex-basis: auto; } .discord-embed-main > :last-child { @@ -182,33 +182,33 @@ main.site-content { } .discord-embed-image { - position: relative; - display: inline-block; + position: relative; + display: inline-block; margin-bottom: 10px; } .discord-embed-image > img { margin: 0; - vertical-align: bottom; - max-width: 300px; - display: flex; - overflow: hidden; - border-radius: 2px; + vertical-align: bottom; + max-width: 300px; + display: flex; + overflow: hidden; + border-radius: 2px; } .discord-embed-footer-text { - font-size: .70rem !important; - letter-spacing: 0; + font-size: .70rem !important; + letter-spacing: 0; display: inline-block; } .discord-embed-footer-icon { - margin-right: 10px; - height: 18px; - width: 18px; - object-fit: contain; - float: left; - border-radius: 50%; + margin-right: 10px; + height: 18px; + width: 18px; + object-fit: contain; + float: left; + border-radius: 50%; } .discord-embed-content { @@ -221,32 +221,36 @@ main.site-content { /* Discord Font definitions */ @font-face { - font-family: Whitney; - font-style: light; - font-weight:300; - src:url(https://discordapp.com/assets/6c6374bad0b0b6d204d8d6dc4a18d820.woff) format("woff") + font-family: Whitney; + font-style: light; + font-weight: 300; + src: url(https://discordapp.com/assets/6c6374bad0b0b6d204d8d6dc4a18d820.woff) format("woff") } + @font-face { - font-family: Whitney; - font-style: normal; - font-weight:500; - src:url(https://discordapp.com/assets/e8acd7d9bf6207f99350ca9f9e23b168.woff) format("woff") + font-family: Whitney; + font-style: normal; + font-weight: 500; + src: url(https://discordapp.com/assets/e8acd7d9bf6207f99350ca9f9e23b168.woff) format("woff") } + @font-face { - font-family:Whitney; - font-style: medium; - font-weight:600; - src:url(https://discordapp.com/assets/3bdef1251a424500c1b3a78dea9b7e57.woff) format("woff") + font-family: Whitney; + font-style: medium; + font-weight: 600; + src: url(https://discordapp.com/assets/3bdef1251a424500c1b3a78dea9b7e57.woff) format("woff") } + @font-face { - font-family: WhitneyMedium; - font-style: medium; - font-weight: 600; - src:url(https://discordapp.com/assets/be0060dafb7a0e31d2a1ca17c0708636.woff) format("woff") + font-family: WhitneyMedium; + font-style: medium; + font-weight: 600; + src: url(https://discordapp.com/assets/be0060dafb7a0e31d2a1ca17c0708636.woff) format("woff") } + @font-face { - font-family: Whitney; - font-style: bold; - font-weight: 700; - src:url(https://discordapp.com/assets/8e12fb4f14d9c4592eb8ec9f22337b04.woff) format("woff") -} \ No newline at end of file + font-family: Whitney; + font-style: bold; + font-weight: 700; + src: url(https://discordapp.com/assets/8e12fb4f14d9c4592eb8ec9f22337b04.woff) format("woff") +} diff --git a/pydis_site/templates/staff/logs.html b/pydis_site/templates/staff/logs.html index 49d9c368..e5b3627d 100644 --- a/pydis_site/templates/staff/logs.html +++ b/pydis_site/templates/staff/logs.html @@ -13,84 +13,85 @@
  • Deleted by: {{ actor }}
  • Date: {{ creation }}
  • -
    - {% for message in messages %} + {% for message in messages %}
    - {{ message.author }} + {{ message.author }}
    {{ message.content|linebreaks }}
    - - {% for embed in message.embeds %} -
    -
    -
    -
    -
    -
    - - {% if embed.author %} -
    - {% if embed.author.icon_url %}Author Icon{% endif %} - {% if embed.author.url %}{% endif %} - {{ embed.author.name }} - {% if embed.author.url %}{% endif %} -
    - {% endif %} - - {% if embed.title %} -
    - {% if embed.url %}{% endif %} - {{ embed.title }} - {% if embed.url %}{% endif %} -
    - {% endif %} - - {% if embed.description %} -
    - {{ embed.description | linebreaksbr }} -
    - {% endif %} - - {% if embed.fields %} -
    - {% for field in embed.fields %} -
    -
    {{ field.name }}
    -
    {{ field.value }}
    + {% for embed in message.embeds %} +
    +
    +
    +
    +
    + {% if embed.author %} +
    + {% if embed.author.icon_url %} + Author Icon{% endif %} + {% if embed.author.url %}{% endif %} + {{ embed.author.name }} + {% if embed.author.url %}{% endif %}
    - {% endfor %} -
    - {% endif %} - {% if embed.image %} -
    - Discord Embed Image + {% endif %} + {% if embed.title %} +
    + {% if embed.url %}{% endif %} + {{ embed.title }} + {% if embed.url %}{% endif %} +
    + {% endif %} + {% if embed.description %} +
    + {{ embed.description | linebreaksbr }} +
    + {% endif %} + {% if embed.fields %} +
    + {% for field in embed.fields %} +
    +
    {{ field.name }}
    +
    {{ field.value }}
    +
    + {% endfor %} +
    + {% endif %} + {% if embed.image %} +
    + Discord Embed Image +
    + {% endif %}
    + {% if embed.thumbnail %} +
    + Embed thumbnail +
    {% endif %}
    - {% if embed.thumbnail %} -
    - Embed thumbnail -
    - {% endif %} + {% if embed.footer or embed.timestamp %} + + {% endif %}
    - {% if embed.footer or embed.timestamp %} - - {% endif %} -
    -
    - {% endfor %} + {% endfor %}
    {% endfor %} -{% endblock %} \ No newline at end of file +{% endblock %} -- cgit v1.2.3 From c22d092514a01964690d9e7d7888a08a40176d33 Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Sat, 24 Aug 2019 12:16:49 +0200 Subject: Changing LogView to DetailView, as per Volcy's review --- pydis_site/apps/staff/tests/test_logs_view.py | 2 +- pydis_site/apps/staff/viewsets/logs.py | 30 ++++----------------------- pydis_site/templates/staff/logs.html | 6 +++--- 3 files changed, 8 insertions(+), 30 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/staff/tests/test_logs_view.py b/pydis_site/apps/staff/tests/test_logs_view.py index 4fc38c7a..ea6020e1 100644 --- a/pydis_site/apps/staff/tests/test_logs_view.py +++ b/pydis_site/apps/staff/tests/test_logs_view.py @@ -133,7 +133,7 @@ class TestLogsView(TestCase): self.assertIn("messages", response.context) self.assertListEqual( [self.deleted_message_one, self.deleted_message_two], - list(response.context["messages"]) + list(response.context["deletion_context"].deletedmessage_set.all()) ) def test_if_both_embeds_are_included_html_response(self): diff --git a/pydis_site/apps/staff/viewsets/logs.py b/pydis_site/apps/staff/viewsets/logs.py index 0898d606..22dede95 100644 --- a/pydis_site/apps/staff/viewsets/logs.py +++ b/pydis_site/apps/staff/viewsets/logs.py @@ -1,33 +1,11 @@ -import logging - -from django.core.handlers.wsgi import WSGIRequest -from django.http import HttpResponse -from django.shortcuts import get_object_or_404, render -from django.views import View +from django.views.generic.detail import DetailView from pydis_site.apps.api.models.bot.message_deletion_context import MessageDeletionContext -log = logging.getLogger(__name__) - -class LogView(View): +class LogView(DetailView): """The default view for the Deleted Messages logs.""" + model = MessageDeletionContext + context_object_name = "deletion_context" template_name = "staff/logs.html" - - def get(self, request: WSGIRequest, pk: int) -> HttpResponse: - """Get method that answers a request with an html response by rendering a template.""" - message_context = get_object_or_404(MessageDeletionContext, pk=pk) - - actor = message_context.actor - creation = message_context.creation - messages = message_context.deletedmessage_set.all() - - template_fields = { - 'actor': actor, - 'actor_colour': message_context.actor.top_role.colour, - 'creation': creation, - 'messages': messages - } - - return render(request, self.template_name, template_fields) diff --git a/pydis_site/templates/staff/logs.html b/pydis_site/templates/staff/logs.html index e5b3627d..9c8ed7d3 100644 --- a/pydis_site/templates/staff/logs.html +++ b/pydis_site/templates/staff/logs.html @@ -10,11 +10,11 @@ {% block content %}
      -
    • Deleted by: {{ actor }}
    • -
    • Date: {{ creation }}
    • +
    • Deleted by: {{ deletion_context.actor }}
    • +
    • Date: {{ deletion_context.creation }}
    - {% for message in messages %} + {% for message in deletion_context.deletedmessage_set.all %}
    Date: Sat, 24 Aug 2019 12:35:29 +0200 Subject: Apply suggestions from code review Co-Authored-By: Johannes Christ --- pydis_site/apps/api/models/bot/message.py | 3 +-- pydis_site/apps/api/models/bot/tag.py | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index 0713b9d2..ac4b40a3 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -1,6 +1,5 @@ from datetime import datetime -import pytz from django.contrib.postgres import fields as pgfields from django.core.validators import MinValueValidator from django.db import models @@ -58,7 +57,7 @@ class Message(ModelReprMixin, models.Model): def timestamp(self) -> datetime: """Attribute that represents the message timestamp as derived from the snowflake id.""" tz_naive_datetime = datetime.utcfromtimestamp(((self.id >> 22) + 1420070400000) / 1000) - tz_aware_datetime = timezone.make_aware(tz_naive_datetime, timezone=pytz.timezone("UTC")) + tz_aware_datetime = timezone.make_aware(tz_naive_datetime, timezone=timezone.utc) return tz_aware_datetime class Meta: diff --git a/pydis_site/apps/api/models/bot/tag.py b/pydis_site/apps/api/models/bot/tag.py index fc0db4e4..8e7643ce 100644 --- a/pydis_site/apps/api/models/bot/tag.py +++ b/pydis_site/apps/api/models/bot/tag.py @@ -30,7 +30,7 @@ def validate_tag_embed_fields(fields): raise ValidationError("Embed fields must be a mapping.") if not all(required_field in field for required_field in required_fields): - raise ValidationError("Embed fields must contain both a 'name' and a 'value' field.") + raise ValidationError(f"Embed fields must contain the following fields: {', '.join(required_fields)}.") for field_name, value in field.items(): if field_name not in field_validators: -- cgit v1.2.3 From 1432092ae5f2c9f643abb4ff91eb3969195aad3e Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Sat, 24 Aug 2019 12:42:18 +0200 Subject: Removing incorrect double newline between imports and PEP8-ing committed GH code suggestion --- pydis_site/apps/api/models/bot/message.py | 1 - pydis_site/apps/api/models/bot/tag.py | 4 +++- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/api/models/bot/message.py b/pydis_site/apps/api/models/bot/message.py index ac4b40a3..31316a01 100644 --- a/pydis_site/apps/api/models/bot/message.py +++ b/pydis_site/apps/api/models/bot/message.py @@ -5,7 +5,6 @@ from django.core.validators import MinValueValidator from django.db import models from django.utils import timezone - from pydis_site.apps.api.models.bot.tag import validate_tag_embed from pydis_site.apps.api.models.bot.user import User from pydis_site.apps.api.models.utils import ModelReprMixin diff --git a/pydis_site/apps/api/models/bot/tag.py b/pydis_site/apps/api/models/bot/tag.py index 8e7643ce..de4eab30 100644 --- a/pydis_site/apps/api/models/bot/tag.py +++ b/pydis_site/apps/api/models/bot/tag.py @@ -30,7 +30,9 @@ def validate_tag_embed_fields(fields): raise ValidationError("Embed fields must be a mapping.") if not all(required_field in field for required_field in required_fields): - raise ValidationError(f"Embed fields must contain the following fields: {', '.join(required_fields)}.") + raise ValidationError( + f"Embed fields must contain the following fields: {', '.join(required_fields)}." + ) for field_name, value in field.items(): if field_name not in field_validators: -- cgit v1.2.3 From d77dfcb26c5d6366e97e003256183c34abde484e Mon Sep 17 00:00:00 2001 From: Sebastiaan Zeeff <33516116+SebastiaanZ@users.noreply.github.com> Date: Sun, 1 Sep 2019 22:12:30 +0200 Subject: Applying PR Feedback: Colour constants for readability & absolute import --- .../staff/tests/test_deletedmessage_filters.py | 22 +++++++++++++++++----- pydis_site/apps/staff/tests/test_logs_view.py | 2 +- 2 files changed, 18 insertions(+), 6 deletions(-) (limited to 'pydis_site') diff --git a/pydis_site/apps/staff/tests/test_deletedmessage_filters.py b/pydis_site/apps/staff/tests/test_deletedmessage_filters.py index d064b8d9..d9179044 100644 --- a/pydis_site/apps/staff/tests/test_deletedmessage_filters.py +++ b/pydis_site/apps/staff/tests/test_deletedmessage_filters.py @@ -1,16 +1,28 @@ +import enum + from django.test import TestCase from django.utils import timezone from ..templatetags import deletedmessage_filters +class Colour(enum.IntEnum): + """Enumeration of integer colour values for readability.""" + + BLACK = 0 + BLUE = 255 + GREEN = 65280 + RED = 16711680 + WHITE = 16777215 + + class DeletedMessageFilterTests(TestCase): def test_hex_colour_filter(self): - self.assertEqual(deletedmessage_filters.hex_colour(0), "#000000") - self.assertEqual(deletedmessage_filters.hex_colour(255), "#0000FF") - self.assertEqual(deletedmessage_filters.hex_colour(65280), "#00FF00") - self.assertEqual(deletedmessage_filters.hex_colour(16711680), "#FF0000") - self.assertEqual(deletedmessage_filters.hex_colour(16777215), "#FFFFFF") + self.assertEqual(deletedmessage_filters.hex_colour(Colour.BLACK), "#000000") + self.assertEqual(deletedmessage_filters.hex_colour(Colour.BLUE), "#0000FF") + self.assertEqual(deletedmessage_filters.hex_colour(Colour.GREEN), "#00FF00") + self.assertEqual(deletedmessage_filters.hex_colour(Colour.RED), "#FF0000") + self.assertEqual(deletedmessage_filters.hex_colour(Colour.WHITE), "#FFFFFF") def test_footer_datetime_filter(self): datetime_aware = timezone.now() diff --git a/pydis_site/apps/staff/tests/test_logs_view.py b/pydis_site/apps/staff/tests/test_logs_view.py index ea6020e1..32cb6bbf 100644 --- a/pydis_site/apps/staff/tests/test_logs_view.py +++ b/pydis_site/apps/staff/tests/test_logs_view.py @@ -3,7 +3,7 @@ from django.utils import timezone from django_hosts.resolvers import reverse, reverse_host from pydis_site.apps.api.models.bot import DeletedMessage, MessageDeletionContext, Role, User -from ..templatetags.deletedmessage_filters import hex_colour +from pydis_site.apps.staff.templatetags.deletedmessage_filters import hex_colour class TestLogsView(TestCase): -- cgit v1.2.3