aboutsummaryrefslogtreecommitdiffstats
path: root/pydis_site/apps
diff options
context:
space:
mode:
authorGravatar ks129 <[email protected]>2021-10-28 19:46:07 +0300
committerGravatar D0rs4n <[email protected]>2021-12-18 18:02:12 +0100
commit25da18321e82f0a3cd18923d59d86b59acec160d (patch)
tree25cc23ed547f6d51a3070f4ff852a3cee32ee0f8 /pydis_site/apps
parentRemove old models from migration (diff)
Update filters API to actually work
Diffstat (limited to 'pydis_site/apps')
-rw-r--r--pydis_site/apps/api/migrations/0070_new_filter_schema.py16
-rw-r--r--pydis_site/apps/api/models/bot/filters.py129
-rw-r--r--pydis_site/apps/api/models/mixins.py5
-rw-r--r--pydis_site/apps/api/serializers.py58
4 files changed, 123 insertions, 85 deletions
diff --git a/pydis_site/apps/api/migrations/0070_new_filter_schema.py b/pydis_site/apps/api/migrations/0070_new_filter_schema.py
index aa114ca1..a595bda2 100644
--- a/pydis_site/apps/api/migrations/0070_new_filter_schema.py
+++ b/pydis_site/apps/api/migrations/0070_new_filter_schema.py
@@ -54,10 +54,10 @@ def forward(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None:
infraction_type=None,
infraction_reason="",
infraction_duration=None,
- disallowed_channels=[],
- disallowed_categories=[],
- allowed_channels=[],
- allowed_categories=[]
+ disallowed_channels=None,
+ disallowed_categories=None,
+ allowed_channels=None,
+ allowed_categories=None
)
new_object.save()
@@ -90,10 +90,10 @@ class Migration(migrations.Migration):
('infraction_type', models.CharField(choices=[('Note', 'Note'), ('Warn', 'Warn'), ('Mute', 'Mute'), ('Kick', 'Kick'), ('Ban', 'Ban')], help_text='The infraction to apply to this user.', max_length=4, null=True)),
('infraction_reason', models.CharField(help_text='The reason to give for the infraction.', max_length=1000)),
('infraction_duration', models.DurationField(help_text='The duration of the infraction. Null if permanent.', null=True)),
- ('disallowed_channels', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=None)),
- ('disallowed_categories', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=None)),
- ('allowed_channels', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=None)),
- ('allowed_categories', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), size=None)),
+ ('disallowed_channels', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)),
+ ('disallowed_categories', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)),
+ ('allowed_channels', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)),
+ ('allowed_categories', django.contrib.postgres.fields.ArrayField(base_field=models.IntegerField(), null=True, size=None)),
],
),
migrations.CreateModel(
diff --git a/pydis_site/apps/api/models/bot/filters.py b/pydis_site/apps/api/models/bot/filters.py
index 365259e7..b9a081e6 100644
--- a/pydis_site/apps/api/models/bot/filters.py
+++ b/pydis_site/apps/api/models/bot/filters.py
@@ -1,4 +1,3 @@
-from abc import abstractmethod
from typing import List
from django.contrib.postgres.fields import ArrayField
@@ -6,8 +5,6 @@ from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import UniqueConstraint
-from pydis_site.apps.api.models.mixins import AbstractModelMeta
-
class FilterListType(models.IntegerChoices):
"""Choice between allow or deny for a list type."""
@@ -43,40 +40,9 @@ def validate_ping_field(value_list: List[str]) -> None:
raise ValidationError(f"{value!r} isn't a valid ping type.")
-class FilterSettingsMixin(models.Model, metaclass=AbstractModelMeta):
- """Mixin for settings of a filter list."""
-
- @staticmethod
- @abstractmethod
- def allow_null() -> bool:
- """Abstract property for allowing null values."""
+class FilterSettingsMixin(models.Model):
+ """Mixin for common settings of a filters and filter lists."""
- ping_type = ArrayField(
- models.CharField(max_length=20),
- validators=(validate_ping_field,),
- help_text="Who to ping when this filter triggers.",
- null=allow_null.__func__()
- )
- filter_dm = models.BooleanField(help_text="Whether DMs should be filtered.", null=True)
- dm_ping_type = ArrayField(
- models.CharField(max_length=20),
- validators=(validate_ping_field,),
- help_text="Who to ping when this filter triggers on a DM.",
- null=allow_null.__func__()
- )
- delete_messages = models.BooleanField(
- help_text="Whether this filter should delete messages triggering it.",
- null=allow_null.__func__()
- )
- bypass_roles = ArrayField(
- models.BigIntegerField(),
- help_text="Roles and users who can bypass this filter.",
- null=allow_null.__func__()
- )
- enabled = models.BooleanField(
- help_text="Whether this filter is currently enabled.",
- null=allow_null.__func__()
- )
dm_content = models.CharField(
max_length=1000,
null=True,
@@ -97,18 +63,6 @@ class FilterSettingsMixin(models.Model, metaclass=AbstractModelMeta):
help_text="The duration of the infraction. Null if permanent."
)
- # Where a filter should apply.
- #
- # The resolution is done in the following order:
- # - disallowed channels
- # - disallowed categories
- # - allowed categories
- # - allowed channels
- disallowed_channels = ArrayField(models.IntegerField())
- disallowed_categories = ArrayField(models.IntegerField())
- allowed_channels = ArrayField(models.IntegerField())
- allowed_categories = ArrayField(models.IntegerField())
-
class Meta:
"""Metaclass for settings mixin."""
@@ -123,11 +77,43 @@ class FilterList(FilterSettingsMixin):
choices=FilterListType.choices,
help_text="Whether this list is an allowlist or denylist"
)
-
- @staticmethod
- def allow_null() -> bool:
- """Do not allow null values for default settings."""
- return False
+ ping_type = ArrayField(
+ models.CharField(max_length=20),
+ validators=(validate_ping_field,),
+ help_text="Who to ping when this filter triggers.",
+ null=False
+ )
+ filter_dm = models.BooleanField(help_text="Whether DMs should be filtered.", null=False)
+ dm_ping_type = ArrayField(
+ models.CharField(max_length=20),
+ validators=(validate_ping_field,),
+ help_text="Who to ping when this filter triggers on a DM.",
+ null=False
+ )
+ delete_messages = models.BooleanField(
+ help_text="Whether this filter should delete messages triggering it.",
+ null=False
+ )
+ bypass_roles = ArrayField(
+ models.BigIntegerField(),
+ help_text="Roles and users who can bypass this filter.",
+ null=False
+ )
+ enabled = models.BooleanField(
+ help_text="Whether this filter is currently enabled.",
+ null=False
+ )
+ # Where a filter should apply.
+ #
+ # The resolution is done in the following order:
+ # - disallowed channels
+ # - disallowed categories
+ # - allowed categories
+ # - allowed channels
+ disallowed_channels = ArrayField(models.IntegerField())
+ disallowed_categories = ArrayField(models.IntegerField())
+ allowed_channels = ArrayField(models.IntegerField())
+ allowed_categories = ArrayField(models.IntegerField())
class Meta:
"""Constrain name and list_type unique."""
@@ -150,11 +136,38 @@ class Filter(FilterSettingsMixin):
FilterList, models.CASCADE, related_name="filters",
help_text="The filter list containing this filter."
)
+ ping_type = ArrayField(
+ models.CharField(max_length=20),
+ validators=(validate_ping_field,),
+ help_text="Who to ping when this filter triggers.",
+ null=True
+ )
+ filter_dm = models.BooleanField(help_text="Whether DMs should be filtered.", null=True)
+ dm_ping_type = ArrayField(
+ models.CharField(max_length=20),
+ validators=(validate_ping_field,),
+ help_text="Who to ping when this filter triggers on a DM.",
+ null=True
+ )
+ delete_messages = models.BooleanField(
+ help_text="Whether this filter should delete messages triggering it.",
+ null=True
+ )
+ bypass_roles = ArrayField(
+ models.BigIntegerField(),
+ help_text="Roles and users who can bypass this filter.",
+ null=True
+ )
+ enabled = models.BooleanField(
+ help_text="Whether this filter is currently enabled.",
+ null=True
+ )
+
+ # Check FilterList model for information about these properties.
+ disallowed_channels = ArrayField(models.IntegerField(), null=True)
+ disallowed_categories = ArrayField(models.IntegerField(), null=True)
+ allowed_channels = ArrayField(models.IntegerField(), null=True)
+ allowed_categories = ArrayField(models.IntegerField(), null=True)
def __str__(self) -> str:
return f"Filter {self.content!r}"
-
- @staticmethod
- def allow_null() -> bool:
- """Allow null values for overrides."""
- return True
diff --git a/pydis_site/apps/api/models/mixins.py b/pydis_site/apps/api/models/mixins.py
index d32e6e72..5d75b78b 100644
--- a/pydis_site/apps/api/models/mixins.py
+++ b/pydis_site/apps/api/models/mixins.py
@@ -1,4 +1,3 @@
-from abc import ABCMeta
from operator import itemgetter
from django.db import models
@@ -30,7 +29,3 @@ class ModelTimestampMixin(models.Model):
"""Metaconfig for the mixin."""
abstract = True
-
-
-class AbstractModelMeta(ABCMeta, type(models.Model)):
- """Metaclass for ABCModel class."""
diff --git a/pydis_site/apps/api/serializers.py b/pydis_site/apps/api/serializers.py
index ff2bd929..4e92b3a0 100644
--- a/pydis_site/apps/api/serializers.py
+++ b/pydis_site/apps/api/serializers.py
@@ -113,6 +113,29 @@ class DocumentationLinkSerializer(ModelSerializer):
fields = ('package', 'base_url', 'inventory_url')
+ALWAYS_OPTIONAL_SETTINGS = (
+ 'dm_content',
+ 'infraction_type',
+ 'infraction_reason',
+ 'infraction_duration',
+)
+
+REQUIRED_FOR_FILTER_LIST_SETTINGS = (
+ 'ping_type',
+ 'filter_dm',
+ 'dm_ping_type',
+ 'delete_messages',
+ 'bypass_roles',
+ 'enabled',
+ 'disallowed_channels',
+ 'disallowed_categories',
+ 'allowed_channels',
+ 'allowed_categories',
+)
+
+SETTINGS_FIELDS = ALWAYS_OPTIONAL_SETTINGS + REQUIRED_FOR_FILTER_LIST_SETTINGS
+
+
class FilterSerializer(ModelSerializer):
"""A class providing (de-)serialization of `Filter` instances."""
@@ -120,7 +143,16 @@ class FilterSerializer(ModelSerializer):
"""Metadata defined for the Django REST Framework."""
model = Filter
- fields = ('id', 'content', 'description', 'additional_field', 'filter_list', 'override')
+ fields = ('id', 'content', 'description', 'additional_field', 'filter_list') + SETTINGS_FIELDS
+ extra_kwargs = {
+ field: {'required': False, 'allow_null': True} for field in SETTINGS_FIELDS
+ } | {
+ 'infraction_reason': {'allow_blank': True, 'allow_null': True, 'required': False},
+ 'disallowed_channels': {'allow_empty': True, 'allow_null': True, 'required': False},
+ 'disallowed_categories': {'allow_empty': True, 'allow_null': True, 'required': False},
+ 'allowed_channels': {'allow_empty': True, 'allow_null': True, 'required': False},
+ 'allowed_categories': {'allow_empty': True, 'allow_null': True, 'required': False},
+ }
class FilterListSerializer(ModelSerializer):
@@ -132,18 +164,16 @@ class FilterListSerializer(ModelSerializer):
"""Metadata defined for the Django REST Framework."""
model = FilterList
- fields = (
- 'id',
- 'name',
- 'list_type',
- 'filters',
- 'ping_type',
- 'filter_dm',
- 'dm_ping_type',
- 'delete_messages',
- 'bypass_roles',
- ''
- )
+ fields = ('id', 'name', 'list_type', 'filters') + SETTINGS_FIELDS
+ extra_kwargs = {
+ field: {'required': False, 'allow_null': True} for field in ALWAYS_OPTIONAL_SETTINGS
+ } | {
+ 'infraction_reason': {'allow_blank': True, 'allow_null': True, 'required': False},
+ 'disallowed_channels': {'allow_empty': True},
+ 'disallowed_categories': {'allow_empty': True},
+ 'allowed_channels': {'allow_empty': True},
+ 'allowed_categories': {'allow_empty': True},
+ }
# Ensure that we can only have one filter list with the same name and field
validators = [
@@ -200,7 +230,7 @@ class InfractionSerializer(ModelSerializer):
if hidden and infr_type in ('superstar', 'warning', 'voice_ban'):
raise ValidationError({'hidden': [f'{infr_type} infractions cannot be hidden.']})
- if not hidden and infr_type in ('note', ):
+ if not hidden and infr_type in ('note',):
raise ValidationError({'hidden': [f'{infr_type} infractions must be hidden.']})
return attrs