From 6516eff584065ed121109b7874cb080f72c5e3cc Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Mon, 3 Aug 2020 22:16:27 +0200 Subject: Add a validator for package names. Package names are used for stats in the bot and are restricted to the a-z_ char set, a validator is added to accommodate this restriction at the site admin side. --- pydis_site/apps/api/models/bot/documentation_link.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/documentation_link.py b/pydis_site/apps/api/models/bot/documentation_link.py index 5a46460b..f77d6f38 100644 --- a/pydis_site/apps/api/models/bot/documentation_link.py +++ b/pydis_site/apps/api/models/bot/documentation_link.py @@ -1,3 +1,4 @@ +from django.core.validators import RegexValidator from django.db import models from pydis_site.apps.api.models.mixins import ModelReprMixin @@ -9,6 +10,12 @@ class DocumentationLink(ModelReprMixin, models.Model): package = models.CharField( primary_key=True, max_length=50, + validators=( + RegexValidator( + regex=r"^[a-z_]+$", + message="Package names can only consist of lowercase a-z letters and underscores." + ), + ), help_text="The Python package name that this documentation link belongs to." ) base_url = models.URLField( -- cgit v1.2.3 From b7f302e4e7afefb16a652d3b0524f4cf4ee835e9 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Tue, 4 Aug 2020 11:29:26 +0200 Subject: Add ascii digits to the validator. Some packages can contain them and are good for stats. --- pydis_site/apps/api/models/bot/documentation_link.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/documentation_link.py b/pydis_site/apps/api/models/bot/documentation_link.py index f77d6f38..e093af59 100644 --- a/pydis_site/apps/api/models/bot/documentation_link.py +++ b/pydis_site/apps/api/models/bot/documentation_link.py @@ -12,8 +12,8 @@ class DocumentationLink(ModelReprMixin, models.Model): max_length=50, validators=( RegexValidator( - regex=r"^[a-z_]+$", - message="Package names can only consist of lowercase a-z letters and underscores." + regex=r"^[a-z0-9_]+$", + message="Package names can only consist of lowercase a-z letters, digits, and underscores." ), ), help_text="The Python package name that this documentation link belongs to." -- cgit v1.2.3 From b242f6d4893dd47c8f07df7dd7bf97f8f1c6631c Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Tue, 4 Aug 2020 12:20:52 +0200 Subject: Move package name validator definition. The move prevents it going through the line limit and deeper nesting of parentheses from splitting up the string. --- pydis_site/apps/api/models/bot/documentation_link.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/documentation_link.py b/pydis_site/apps/api/models/bot/documentation_link.py index e093af59..56b47ae6 100644 --- a/pydis_site/apps/api/models/bot/documentation_link.py +++ b/pydis_site/apps/api/models/bot/documentation_link.py @@ -3,6 +3,11 @@ from django.db import models from pydis_site.apps.api.models.mixins import ModelReprMixin +package_name_validator = RegexValidator( + regex=r"^[a-z0-9_]+$", + message="Package names can only consist of lowercase a-z letters, digits, and underscores." +) + class DocumentationLink(ModelReprMixin, models.Model): """A documentation link used by the `!docs` command of the bot.""" @@ -10,12 +15,7 @@ class DocumentationLink(ModelReprMixin, models.Model): package = models.CharField( primary_key=True, max_length=50, - validators=( - RegexValidator( - regex=r"^[a-z0-9_]+$", - message="Package names can only consist of lowercase a-z letters, digits, and underscores." - ), - ), + validators=package_name_validator, help_text="The Python package name that this documentation link belongs to." ) base_url = models.URLField( -- cgit v1.2.3 From 62cf63427e51f2a03eb37d726ca9a6a12fed7374 Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Tue, 4 Aug 2020 16:00:43 +0200 Subject: Fix package name validator definition. The validators kwarg expects an iterable of validators, while a validator directly was being supplied. --- pydis_site/apps/api/models/bot/documentation_link.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/documentation_link.py b/pydis_site/apps/api/models/bot/documentation_link.py index 56b47ae6..4f2bd2ab 100644 --- a/pydis_site/apps/api/models/bot/documentation_link.py +++ b/pydis_site/apps/api/models/bot/documentation_link.py @@ -3,9 +3,11 @@ from django.db import models from pydis_site.apps.api.models.mixins import ModelReprMixin -package_name_validator = RegexValidator( - regex=r"^[a-z0-9_]+$", - message="Package names can only consist of lowercase a-z letters, digits, and underscores." +package_name_validator = ( + RegexValidator( + regex=r"^[a-z0-9_]+$", + message="Package names can only consist of lowercase a-z letters, digits, and underscores." + ), ) -- cgit v1.2.3 From 9ac477385d5ed26d2d8e4f711b2c927cfaf35461 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Mon, 22 Feb 2021 08:21:18 +0200 Subject: Split nomination model to 2 tables and create migrations --- .../api/migrations/0068_split_nomination_tables.py | 60 ++++++++++++++++++++++ .../0069_change_nomination_entry_plural.py | 17 ++++++ pydis_site/apps/api/models/bot/nomination.py | 47 ++++++++++++----- 3 files changed, 112 insertions(+), 12 deletions(-) create mode 100644 pydis_site/apps/api/migrations/0068_split_nomination_tables.py create mode 100644 pydis_site/apps/api/migrations/0069_change_nomination_entry_plural.py (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/migrations/0068_split_nomination_tables.py b/pydis_site/apps/api/migrations/0068_split_nomination_tables.py new file mode 100644 index 00000000..2e2313ee --- /dev/null +++ b/pydis_site/apps/api/migrations/0068_split_nomination_tables.py @@ -0,0 +1,60 @@ +# Generated by Django 3.0.11 on 2021-02-21 15:32 + +from django.apps.registry import Apps +from django.db import backends, migrations, models +from django.db.backends.base.schema import BaseDatabaseSchemaEditor +import django.db.models.deletion +import pydis_site.apps.api.models.mixins + + +def migrate_nominations(apps: Apps, schema_editor: BaseDatabaseSchemaEditor) -> None: + Nomination = apps.get_model("api", "Nomination") + NominationEntry = apps.get_model("api", "NominationEntry") + + for nomination in Nomination.objects.all(): + nomination_entry = NominationEntry( + nomination=nomination, + actor=nomination.actor, + reason=nomination.reason, + inserted_at=nomination.inserted_at + ) + nomination_entry.save() + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0067_add_voice_ban_infraction_type'), + ] + + operations = [ + migrations.CreateModel( + name='NominationEntry', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('reason', models.TextField(blank=True, help_text='Why the actor nominated this user.', null=True)), + ('inserted_at', + models.DateTimeField(auto_now_add=True, help_text='The creation date of this nomination entry.')), + ('actor', models.ForeignKey(help_text='The staff member that nominated this user.', + on_delete=django.db.models.deletion.CASCADE, related_name='nomination_set', + to='api.User')), + ('nomination', models.ForeignKey(help_text='Nomination to what this entry belongs.', + on_delete=django.db.models.deletion.CASCADE, to='api.Nomination')), + ], + bases=(pydis_site.apps.api.models.mixins.ModelReprMixin, models.Model), + ), + migrations.RunPython(migrate_nominations), + migrations.RemoveField( + model_name='nomination', + name='actor', + ), + migrations.RemoveField( + model_name='nomination', + name='reason', + ), + migrations.AddField( + model_name='nomination', + name='reviewed', + field=models.BooleanField(default=False, help_text='Whether voting message have been made.'), + ), + ] diff --git a/pydis_site/apps/api/migrations/0069_change_nomination_entry_plural.py b/pydis_site/apps/api/migrations/0069_change_nomination_entry_plural.py new file mode 100644 index 00000000..6bf4ac8c --- /dev/null +++ b/pydis_site/apps/api/migrations/0069_change_nomination_entry_plural.py @@ -0,0 +1,17 @@ +# Generated by Django 3.0.11 on 2021-02-21 16:44 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0068_split_nomination_tables'), + ] + + operations = [ + migrations.AlterModelOptions( + name='nominationentry', + options={'verbose_name_plural': 'nomination entries'}, + ), + ] diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py index 11b9e36e..ed6f7d81 100644 --- a/pydis_site/apps/api/models/bot/nomination.py +++ b/pydis_site/apps/api/models/bot/nomination.py @@ -5,23 +5,12 @@ from pydis_site.apps.api.models.mixins import ModelReprMixin class Nomination(ModelReprMixin, models.Model): - """A helper nomination created by staff.""" + """A general helper nomination information created by staff.""" active = models.BooleanField( default=True, help_text="Whether this nomination is still relevant." ) - actor = models.ForeignKey( - User, - on_delete=models.CASCADE, - help_text="The staff member that nominated this user.", - related_name='nomination_set' - ) - reason = models.TextField( - help_text="Why this user was nominated.", - null=True, - blank=True - ) user = models.ForeignKey( User, on_delete=models.CASCADE, @@ -42,6 +31,10 @@ class Nomination(ModelReprMixin, models.Model): help_text="When the nomination was ended.", null=True ) + reviewed = models.BooleanField( + default=False, + help_text="Whether voting message have been made." + ) def __str__(self): """Representation that makes the target and state of the nomination immediately evident.""" @@ -52,3 +45,33 @@ class Nomination(ModelReprMixin, models.Model): """Set the ordering of nominations to most recent first.""" ordering = ("-inserted_at",) + + +class NominationEntry(ModelReprMixin, models.Model): + """A nomination entry created by single staff.""" + + nomination = models.ForeignKey( + Nomination, + on_delete=models.CASCADE, + help_text="Nomination to what this entry belongs." + ) + actor = models.ForeignKey( + User, + on_delete=models.CASCADE, + help_text="The staff member that nominated this user.", + related_name='nomination_set' + ) + reason = models.TextField( + help_text="Why the actor nominated this user.", + null=True, + blank=True + ) + inserted_at = models.DateTimeField( + auto_now_add=True, + help_text="The creation date of this nomination entry." + ) + + class Meta: + """Meta options for NominationEntry model.""" + + verbose_name_plural = "nomination entries" -- cgit v1.2.3 From 6591270f832e93a3eaf941aa1aeeddb6af7bce36 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Mon, 22 Feb 2021 11:36:25 +0200 Subject: Import NominationEntry to models __init__.py --- pydis_site/apps/api/models/__init__.py | 1 + pydis_site/apps/api/models/bot/__init__.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/__init__.py b/pydis_site/apps/api/models/__init__.py index 0a8c90f6..fd5bf220 100644 --- a/pydis_site/apps/api/models/__init__.py +++ b/pydis_site/apps/api/models/__init__.py @@ -8,6 +8,7 @@ from .bot import ( Message, MessageDeletionContext, Nomination, + NominationEntry, OffensiveMessage, OffTopicChannelName, Reminder, diff --git a/pydis_site/apps/api/models/bot/__init__.py b/pydis_site/apps/api/models/bot/__init__.py index 1673b434..ac864de3 100644 --- a/pydis_site/apps/api/models/bot/__init__.py +++ b/pydis_site/apps/api/models/bot/__init__.py @@ -6,7 +6,7 @@ from .documentation_link import DocumentationLink from .infraction import Infraction from .message import Message from .message_deletion_context import MessageDeletionContext -from .nomination import Nomination +from .nomination import Nomination, NominationEntry from .off_topic_channel_name import OffTopicChannelName from .offensive_message import OffensiveMessage from .reminder import Reminder -- cgit v1.2.3 From f731e4dd31f9eba8e8c9916329a5c08578055077 Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Wed, 24 Feb 2021 08:27:36 +0200 Subject: Set related_name option of nomination field of NominationEntry In order to use entries in serializer without manually setting entries key we have to use related_name option to automatically fetch all related entries. --- pydis_site/apps/api/models/bot/nomination.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py index ed6f7d81..a813855d 100644 --- a/pydis_site/apps/api/models/bot/nomination.py +++ b/pydis_site/apps/api/models/bot/nomination.py @@ -53,7 +53,8 @@ class NominationEntry(ModelReprMixin, models.Model): nomination = models.ForeignKey( Nomination, on_delete=models.CASCADE, - help_text="Nomination to what this entry belongs." + help_text="Nomination to what this entry belongs.", + related_name="entries" ) actor = models.ForeignKey( User, -- cgit v1.2.3 From 1fc5470a0af18aab427a48faf132bea2712330aa Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Wed, 24 Feb 2021 08:28:54 +0200 Subject: Set default ordering of NominationEntry to inserted_at decreasing Set it here so we don't have to set it every place where we fetch entries. --- pydis_site/apps/api/models/bot/nomination.py | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py index a813855d..e72a18ca 100644 --- a/pydis_site/apps/api/models/bot/nomination.py +++ b/pydis_site/apps/api/models/bot/nomination.py @@ -76,3 +76,7 @@ class NominationEntry(ModelReprMixin, models.Model): """Meta options for NominationEntry model.""" verbose_name_plural = "nomination entries" + + # Set default ordering here to latest first + # so we don't need to define it everywhere + ordering = ("-inserted_at",) -- cgit v1.2.3 From cd03f7448ae4c96c23a80c4c46344bc35b8c603a Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Wed, 24 Feb 2021 08:44:22 +0200 Subject: Make default value of nomination entry reason to empty string For string fields NULL as default is not suggested, so use empty string instead. --- pydis_site/apps/api/migrations/0068_split_nomination_tables.py | 2 +- pydis_site/apps/api/models/bot/nomination.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/migrations/0068_split_nomination_tables.py b/pydis_site/apps/api/migrations/0068_split_nomination_tables.py index 107e3a56..27c29017 100644 --- a/pydis_site/apps/api/migrations/0068_split_nomination_tables.py +++ b/pydis_site/apps/api/migrations/0068_split_nomination_tables.py @@ -45,7 +45,7 @@ class Migration(migrations.Migration): name='NominationEntry', fields=[ ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('reason', models.TextField(blank=True, help_text='Why the actor nominated this user.', null=True)), + ('reason', models.TextField(blank=True, help_text='Why the actor nominated this user.', default="")), ('inserted_at', models.DateTimeField(auto_now_add=True, help_text='The creation date of this nomination entry.')), ('actor', models.ForeignKey(help_text='The staff member that nominated this user.', diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py index e72a18ca..443200ff 100644 --- a/pydis_site/apps/api/models/bot/nomination.py +++ b/pydis_site/apps/api/models/bot/nomination.py @@ -64,7 +64,7 @@ class NominationEntry(ModelReprMixin, models.Model): ) reason = models.TextField( help_text="Why the actor nominated this user.", - null=True, + default="", blank=True ) inserted_at = models.DateTimeField( -- cgit v1.2.3 From 53a9f5282adfdd3a2a4ebc819fcc9cd568a632ea Mon Sep 17 00:00:00 2001 From: ks129 <45097959+ks129@users.noreply.github.com> Date: Sat, 6 Mar 2021 14:09:38 +0200 Subject: Fix grammar of nomination models --- pydis_site/apps/api/migrations/0068_split_nomination_tables.py | 4 ++-- pydis_site/apps/api/models/bot/nomination.py | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/migrations/0068_split_nomination_tables.py b/pydis_site/apps/api/migrations/0068_split_nomination_tables.py index 27c29017..79825ed7 100644 --- a/pydis_site/apps/api/migrations/0068_split_nomination_tables.py +++ b/pydis_site/apps/api/migrations/0068_split_nomination_tables.py @@ -51,7 +51,7 @@ class Migration(migrations.Migration): ('actor', models.ForeignKey(help_text='The staff member that nominated this user.', on_delete=django.db.models.deletion.CASCADE, related_name='nomination_set', to='api.User')), - ('nomination', models.ForeignKey(help_text='Nomination to what this entry belongs.', + ('nomination', models.ForeignKey(help_text='The nomination this entry belongs to.', on_delete=django.db.models.deletion.CASCADE, to='api.Nomination', related_name='entries')), ], @@ -70,6 +70,6 @@ class Migration(migrations.Migration): migrations.AddField( model_name='nomination', name='reviewed', - field=models.BooleanField(default=False, help_text='Whether voting message have been made.'), + field=models.BooleanField(default=False, help_text='Whether a review was made.'), ), ] diff --git a/pydis_site/apps/api/models/bot/nomination.py b/pydis_site/apps/api/models/bot/nomination.py index 443200ff..221d8534 100644 --- a/pydis_site/apps/api/models/bot/nomination.py +++ b/pydis_site/apps/api/models/bot/nomination.py @@ -33,7 +33,7 @@ class Nomination(ModelReprMixin, models.Model): ) reviewed = models.BooleanField( default=False, - help_text="Whether voting message have been made." + help_text="Whether a review was made." ) def __str__(self): @@ -48,12 +48,12 @@ class Nomination(ModelReprMixin, models.Model): class NominationEntry(ModelReprMixin, models.Model): - """A nomination entry created by single staff.""" + """A nomination entry created by a single staff member.""" nomination = models.ForeignKey( Nomination, on_delete=models.CASCADE, - help_text="Nomination to what this entry belongs.", + help_text="The nomination this entry belongs to.", related_name="entries" ) actor = models.ForeignKey( -- cgit v1.2.3 From 4f9c088f6b0458eb0ebb52ef899cdfdc57f2c43c Mon Sep 17 00:00:00 2001 From: Boris Muratov <8bee278@gmail.com> Date: Sun, 7 Mar 2021 00:59:41 +0200 Subject: Add route to get a member's data for helper review Added route for getting a user's join date, total messages, and top 3 channels by activity. This information will be used to auto-review nominees. --- postgres/init.sql | 18 ++++++++++++- pydis_site/apps/api/models/bot/metricity.py | 39 +++++++++++++++++++++++++++++ pydis_site/apps/api/viewsets/bot/user.py | 15 +++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) (limited to 'pydis_site/apps/api/models') diff --git a/postgres/init.sql b/postgres/init.sql index 740063e7..ae86fca0 100644 --- a/postgres/init.sql +++ b/postgres/init.sql @@ -13,12 +13,28 @@ INSERT INTO users VALUES ( current_timestamp ); +CREATE TABLE channels ( + id varchar, + name varchar, + primary key(id) +); + +INSERT INTO channels VALUES( + '267659945086812160', + 'python-general' +); + +INSERT INTO channels VALUES( + '1234', + 'zebra' +); + CREATE TABLE messages ( id varchar, author_id varchar references users(id), is_deleted boolean, created_at timestamp, - channel_id varchar, + channel_id varchar references channels(id), primary key(id) ); diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py index cae630f1..af5e1f3b 100644 --- a/pydis_site/apps/api/models/bot/metricity.py +++ b/pydis_site/apps/api/models/bot/metricity.py @@ -89,3 +89,42 @@ class Metricity: raise NotFound() return values[0] + + def top_channel_activity(self, user_id: str) -> int: + """ + Query the top three channels in which the user is most active. + + Help channels are grouped under "the help channels", + and off-topic channels are grouped under "off-topic". + """ + self.cursor.execute( + """ + SELECT + CASE + WHEN channels.name ILIKE 'help-%%' THEN 'the help channels' + WHEN channels.name ILIKE 'ot%%' THEN 'off-topic' + ELSE channels.name + END, + COUNT(1) + FROM + messages + LEFT JOIN channels ON channels.id = messages.channel_id + WHERE + author_id = '%s' + GROUP BY + 1 + ORDER BY + 2 DESC + LIMIT + 3; + """, + [user_id] + ) + + values = self.cursor.fetchall() + print(values) + + if not values: + raise NotFound() + + return values diff --git a/pydis_site/apps/api/viewsets/bot/user.py b/pydis_site/apps/api/viewsets/bot/user.py index 829e2694..5e1f8775 100644 --- a/pydis_site/apps/api/viewsets/bot/user.py +++ b/pydis_site/apps/api/viewsets/bot/user.py @@ -262,3 +262,18 @@ class UserViewSet(ModelViewSet): except NotFound: return Response(dict(detail="User not found in metricity"), status=status.HTTP_404_NOT_FOUND) + + @action(detail=True) + def metricity_review_data(self, request: Request, pk: str = None) -> Response: + """Request handler for metricity_review_data endpoint.""" + user = self.get_object() + + with Metricity() as metricity: + try: + data = metricity.user(user.id) + data["total_messages"] = metricity.total_messages(user.id) + data["top_channel_activity"] = metricity.top_channel_activity(user.id) + return Response(data, status=status.HTTP_200_OK) + except NotFound: + return Response(dict(detail="User not found in metricity"), + status=status.HTTP_404_NOT_FOUND) -- cgit v1.2.3 From d2690bbedb5f5ef221cbfaa42ad78ff8fcc263f2 Mon Sep 17 00:00:00 2001 From: Boris Muratov <8bee278@gmail.com> Date: Sun, 7 Mar 2021 01:05:09 +0200 Subject: Amend top_channel_activity return type --- pydis_site/apps/api/models/bot/metricity.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py index af5e1f3b..29a43513 100644 --- a/pydis_site/apps/api/models/bot/metricity.py +++ b/pydis_site/apps/api/models/bot/metricity.py @@ -1,3 +1,5 @@ +from typing import List, Tuple + from django.db import connections BLOCK_INTERVAL = 10 * 60 # 10 minute blocks @@ -90,7 +92,7 @@ class Metricity: return values[0] - def top_channel_activity(self, user_id: str) -> int: + def top_channel_activity(self, user_id: str) -> List[Tuple[str, int]]: """ Query the top three channels in which the user is most active. -- cgit v1.2.3 From 07847e959c9b2ac6a79ab38b900abc2d179d0478 Mon Sep 17 00:00:00 2001 From: Boris Muratov <8bee278@gmail.com> Date: Sun, 7 Mar 2021 11:58:30 +0200 Subject: Get rid of stray print Oops. --- pydis_site/apps/api/models/bot/metricity.py | 1 - 1 file changed, 1 deletion(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py index 29a43513..7e2a68f2 100644 --- a/pydis_site/apps/api/models/bot/metricity.py +++ b/pydis_site/apps/api/models/bot/metricity.py @@ -124,7 +124,6 @@ class Metricity: ) values = self.cursor.fetchall() - print(values) if not values: raise NotFound() -- cgit v1.2.3 From fdc1be68a90d6ebd9dfe29369bc8a974bcaa8214 Mon Sep 17 00:00:00 2001 From: Boris Muratov <8bee278@gmail.com> Date: Thu, 11 Mar 2021 02:37:44 +0200 Subject: Ignore deleted messaages in message counts Co-authored-by: Joe Banks --- pydis_site/apps/api/models/bot/metricity.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py index 7e2a68f2..db975d4e 100644 --- a/pydis_site/apps/api/models/bot/metricity.py +++ b/pydis_site/apps/api/models/bot/metricity.py @@ -112,7 +112,7 @@ class Metricity: messages LEFT JOIN channels ON channels.id = messages.channel_id WHERE - author_id = '%s' + author_id = '%s' AND NOT messages.is_deleted GROUP BY 1 ORDER BY -- cgit v1.2.3 From cd797f801abaff8874e75b1ed093e17f67572a37 Mon Sep 17 00:00:00 2001 From: Boris Muratov <8bee278@gmail.com> Date: Fri, 12 Mar 2021 16:00:14 +0200 Subject: Add case in query for voice chat activity --- pydis_site/apps/api/models/bot/metricity.py | 1 + 1 file changed, 1 insertion(+) (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/models/bot/metricity.py b/pydis_site/apps/api/models/bot/metricity.py index db975d4e..5daa5c66 100644 --- a/pydis_site/apps/api/models/bot/metricity.py +++ b/pydis_site/apps/api/models/bot/metricity.py @@ -105,6 +105,7 @@ class Metricity: CASE WHEN channels.name ILIKE 'help-%%' THEN 'the help channels' WHEN channels.name ILIKE 'ot%%' THEN 'off-topic' + WHEN channels.name ILIKE '%%voice%%' THEN 'voice chats' ELSE channels.name END, COUNT(1) -- cgit v1.2.3 From 163460967916463485a8193dde092eef639c9bea Mon Sep 17 00:00:00 2001 From: Numerlor <25886452+Numerlor@users.noreply.github.com> Date: Fri, 26 Mar 2021 19:30:03 +0100 Subject: Ensure the base url ends with a slash URLs without a trailing slash won't get properly joined by the bot Adds compatibility with python-discord/bot@bc25bfdf42cdaaba924a7ad6de1dc06a9b381285's changes The styling of how the regex decorator is constructed was changed to be consisted with the function validator --- ...0069_documentationlink_packagename_validator.py | 19 ---------------- .../0069_documentationlink_validators.py | 25 ++++++++++++++++++++++ .../apps/api/models/bot/documentation_link.py | 20 +++++++++++------ 3 files changed, 38 insertions(+), 26 deletions(-) delete mode 100644 pydis_site/apps/api/migrations/0069_documentationlink_packagename_validator.py create mode 100644 pydis_site/apps/api/migrations/0069_documentationlink_validators.py (limited to 'pydis_site/apps/api/models') diff --git a/pydis_site/apps/api/migrations/0069_documentationlink_packagename_validator.py b/pydis_site/apps/api/migrations/0069_documentationlink_packagename_validator.py deleted file mode 100644 index 4234e633..00000000 --- a/pydis_site/apps/api/migrations/0069_documentationlink_packagename_validator.py +++ /dev/null @@ -1,19 +0,0 @@ -# Generated by Django 3.0.11 on 2021-03-14 23:22 - -import django.core.validators -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('api', '0068_split_nomination_tables'), - ] - - operations = [ - migrations.AlterField( - model_name='documentationlink', - name='package', - field=models.CharField(help_text='The Python package name that this documentation link belongs to.', max_length=50, primary_key=True, serialize=False, validators=[django.core.validators.RegexValidator(message='Package names can only consist of lowercase a-z letters, digits, and underscores.', regex='^[a-z0-9_]+$')]), - ), - ] diff --git a/pydis_site/apps/api/migrations/0069_documentationlink_validators.py b/pydis_site/apps/api/migrations/0069_documentationlink_validators.py new file mode 100644 index 00000000..347c0e1a --- /dev/null +++ b/pydis_site/apps/api/migrations/0069_documentationlink_validators.py @@ -0,0 +1,25 @@ +# Generated by Django 3.0.11 on 2021-03-26 18:21 + +import django.core.validators +from django.db import migrations, models +import pydis_site.apps.api.models.bot.documentation_link + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0068_split_nomination_tables'), + ] + + operations = [ + migrations.AlterField( + model_name='documentationlink', + name='base_url', + field=models.URLField(help_text='The base URL from which documentation will be available for this project. Used to generate links to various symbols within this package.', validators=[pydis_site.apps.api.models.bot.documentation_link.ends_with_slash_validator]), + ), + migrations.AlterField( + model_name='documentationlink', + name='package', + field=models.CharField(help_text='The Python package name that this documentation link belongs to.', max_length=50, primary_key=True, serialize=False, validators=[django.core.validators.RegexValidator(message='Package names can only consist of lowercase a-z letters, digits, and underscores.', regex='^[a-z0-9_]+$')]), + ), + ] diff --git a/pydis_site/apps/api/models/bot/documentation_link.py b/pydis_site/apps/api/models/bot/documentation_link.py index 529d26d1..3dcc71fc 100644 --- a/pydis_site/apps/api/models/bot/documentation_link.py +++ b/pydis_site/apps/api/models/bot/documentation_link.py @@ -1,30 +1,36 @@ +from django.core.exceptions import ValidationError from django.core.validators import RegexValidator from django.db import models from pydis_site.apps.api.models.mixins import ModelReprMixin -package_name_validator = ( - RegexValidator( - regex=r"^[a-z0-9_]+$", - message="Package names can only consist of lowercase a-z letters, digits, and underscores." - ), +package_name_validator = RegexValidator( + regex=r"^[a-z0-9_]+$", + message="Package names can only consist of lowercase a-z letters, digits, and underscores." ) +def ends_with_slash_validator(string: str) -> None: + """Raise a ValidationError if `string` does not end with a slash.""" + if not string.endswith("/"): + raise ValidationError("The entered URL must end with a slash.") + + class DocumentationLink(ModelReprMixin, models.Model): """A documentation link used by the `!docs` command of the bot.""" package = models.CharField( primary_key=True, max_length=50, - validators=package_name_validator, + validators=(package_name_validator,), help_text="The Python package name that this documentation link belongs to." ) base_url = models.URLField( help_text=( "The base URL from which documentation will be available for this project. " "Used to generate links to various symbols within this package." - ) + ), + validators=(ends_with_slash_validator,) ) inventory_url = models.URLField( help_text="The URL at which the Sphinx inventory is available for this package." -- cgit v1.2.3