Browse Source

Squash migration

David Burke 2 years ago
parent
commit
9505d207da

+ 40 - 17
alerts/tests/tests.py

@@ -11,7 +11,7 @@ from glitchtip import test_utils  # pylint: disable=unused-import
 from glitchtip.test_utils.test_case import GlitchTipTestCase
 from issues.models import EventStatus, Issue
 from organizations_ext.models import OrganizationUserRole
-from users.models import ProjectAlertStatus
+from projects.models import ProjectAlertStatus
 
 from ..models import Notification
 from ..tasks import process_event_alerts
@@ -83,9 +83,12 @@ class AlertTestCase(GlitchTipTestCase):
         self.assertEqual(Notification.objects.count(), 1)
 
     def test_alert_one_event(self):
-        """ Use same logic to send alert for every new issue """
+        """Use same logic to send alert for every new issue"""
         baker.make(
-            "alerts.ProjectAlert", project=self.project, timespan_minutes=1, quantity=1,
+            "alerts.ProjectAlert",
+            project=self.project,
+            timespan_minutes=1,
+            quantity=1,
         )
         issue = baker.make("issues.Issue", project=self.project)
         baker.make("events.Event", issue=issue)
@@ -94,7 +97,10 @@ class AlertTestCase(GlitchTipTestCase):
 
     def test_alert_on_regression(self):
         baker.make(
-            "alerts.ProjectAlert", project=self.project, timespan_minutes=1, quantity=1,
+            "alerts.ProjectAlert",
+            project=self.project,
+            timespan_minutes=1,
+            quantity=1,
         )
 
         # Make event
@@ -123,9 +129,12 @@ class AlertTestCase(GlitchTipTestCase):
         self.assertEqual(len(mail.outbox), 2)
 
     def test_alert_subscription_default_scope(self):
-        """ Subscribe by default should not result in alert emails for non-team members """
+        """Subscribe by default should not result in alert emails for non-team members"""
         baker.make(
-            "alerts.ProjectAlert", project=self.project, timespan_minutes=1, quantity=1,
+            "alerts.ProjectAlert",
+            project=self.project,
+            timespan_minutes=1,
+            quantity=1,
         )
 
         # user2 is an org member but not in a relevant team, should not receive alerts
@@ -155,12 +164,15 @@ class AlertWithUserProjectAlert(GlitchTipTestCase):
         self.now = timezone.now()
 
     def test_alert_enabled_user_project_alert_disabled(self):
-        """ A user should be able to disable their own notifications """
+        """A user should be able to disable their own notifications"""
         baker.make(
-            "alerts.ProjectAlert", project=self.project, timespan_minutes=1, quantity=1,
+            "alerts.ProjectAlert",
+            project=self.project,
+            timespan_minutes=1,
+            quantity=1,
         )
         baker.make(
-            "users.UserProjectAlert",
+            "projects.UserProjectAlert",
             user=self.user,
             project=self.project,
             status=ProjectAlertStatus.OFF,
@@ -170,14 +182,16 @@ class AlertWithUserProjectAlert(GlitchTipTestCase):
         org_user2 = self.organization.add_user(user2, OrganizationUserRole.ADMIN)
         self.team.members.add(org_user2)
         baker.make(
-            "users.UserProjectAlert", user=user2, status=ProjectAlertStatus.ON,
+            "projects.UserProjectAlert",
+            user=user2,
+            status=ProjectAlertStatus.ON,
         )
 
         user3 = baker.make("users.user")
         org_user3 = self.organization.add_user(user3, OrganizationUserRole.ADMIN)
         self.team.members.add(org_user3)
         baker.make(
-            "users.UserProjectAlert",
+            "projects.UserProjectAlert",
             user=user3,
             project=self.project,
             status=ProjectAlertStatus.ON,
@@ -192,7 +206,10 @@ class AlertWithUserProjectAlert(GlitchTipTestCase):
         self.user.subscribe_by_default = False
         self.user.save()
         baker.make(
-            "alerts.ProjectAlert", project=self.project, timespan_minutes=1, quantity=1,
+            "alerts.ProjectAlert",
+            project=self.project,
+            timespan_minutes=1,
+            quantity=1,
         )
 
         baker.make("events.Event", issue__project=self.project)
@@ -203,10 +220,13 @@ class AlertWithUserProjectAlert(GlitchTipTestCase):
         self.user.subscribe_by_default = False
         self.user.save()
         baker.make(
-            "alerts.ProjectAlert", project=self.project, timespan_minutes=1, quantity=1,
+            "alerts.ProjectAlert",
+            project=self.project,
+            timespan_minutes=1,
+            quantity=1,
         )
         baker.make(
-            "users.UserProjectAlert",
+            "projects.UserProjectAlert",
             user=self.user,
             project=self.project,
             status=ProjectAlertStatus.ON,
@@ -216,16 +236,19 @@ class AlertWithUserProjectAlert(GlitchTipTestCase):
         self.assertEqual(len(mail.outbox), 1)
 
     def test_user_project_alert_scope(self):
-        """ User project alert should not result in alert emails for non-team members """
+        """User project alert should not result in alert emails for non-team members"""
         baker.make(
-            "alerts.ProjectAlert", project=self.project, timespan_minutes=1, quantity=1,
+            "alerts.ProjectAlert",
+            project=self.project,
+            timespan_minutes=1,
+            quantity=1,
         )
 
         user2 = baker.make("users.user")
         self.organization.add_user(user2, OrganizationUserRole.MEMBER)
 
         baker.make(
-            "users.UserProjectAlert",
+            "projects.UserProjectAlert",
             user=user2,
             project=self.project,
             status=ProjectAlertStatus.ON,

+ 23 - 0
files/migrations/0008_alter_file_type_alter_fileblob_blob.py

@@ -0,0 +1,23 @@
+# Generated by Django 4.1.3 on 2022-12-12 22:08
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("files", "0001_squashed_0007_remove_file_blobs_file_blob"),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name="file",
+            name="type",
+            field=models.CharField(max_length=64),
+        ),
+        migrations.AlterField(
+            model_name="fileblob",
+            name="blob",
+            field=models.FileField(upload_to="uploads/file_blobs"),
+        ),
+    ]

+ 9 - 2
glitchtip/uptime/serializers.py

@@ -7,9 +7,16 @@ from rest_framework.fields import ChoiceField
 from .models import Monitor, MonitorCheck, MonitorType
 
 
-class MonitorCheckSerializer(serializers.ModelSerializer):
+class MonitorCheckRelatedSerializer(serializers.ModelSerializer):
     isUp = serializers.BooleanField(source="is_up")
     startCheck = serializers.DateTimeField(source="start_check")
+
+    class Meta:
+        model = MonitorCheck
+        fields = ("isUp", "startCheck", "reason")
+
+
+class MonitorCheckSerializer(MonitorCheckRelatedSerializer):
     responseTime = serializers.DurationField(source="response_time")
 
     class Meta:
@@ -35,7 +42,7 @@ class MonitorSerializer(serializers.ModelSerializer):
     heartbeatEndpoint = serializers.SerializerMethodField()
     projectName = serializers.SerializerMethodField()
     envName = serializers.SerializerMethodField()
-    checks = MonitorCheckSerializer(many=True, read_only=True)
+    checks = MonitorCheckRelatedSerializer(many=True, read_only=True)
 
     def get_isUp(self, obj):
         if hasattr(obj, "latest_is_up"):

+ 2 - 2
glitchtip/uptime/tests/tests.py

@@ -9,7 +9,7 @@ from model_bakery import baker
 
 from glitchtip.test_utils.test_case import GlitchTipTestCase
 from organizations_ext.models import OrganizationUserRole
-from users.models import ProjectAlertStatus
+from projects.models import ProjectAlertStatus
 
 from ..constants import MonitorType
 from ..models import Monitor, MonitorCheck
@@ -168,7 +168,7 @@ class UptimeTestCase(GlitchTipTestCase):
         self.organization.add_user(user2, OrganizationUserRole.MEMBER)
 
         baker.make(
-            "users.UserProjectAlert",
+            "projects.UserProjectAlert",
             user=user2,
             project=self.project,
             status=ProjectAlertStatus.ON,

+ 12 - 1
projects/admin.py

@@ -2,7 +2,7 @@ from django.contrib import admin
 from import_export import resources
 from import_export.admin import ImportExportModelAdmin
 
-from .models import Project, ProjectKey
+from .models import Project, ProjectKey, UserProjectAlert
 
 
 class ProjectKeyResource(resources.ModelResource):
@@ -37,4 +37,15 @@ class ProjectAdmin(ImportExportModelAdmin):
     resource_class = ProjectResource
 
 
+class UserProjectAlertAdmin(admin.ModelAdmin):
+    list_display = ("user", "project", "status")
+    list_filter = ("status",)
+    search_fields = ("project__name", "user__email")
+    raw_id_fields = (
+        "user",
+        "project",
+    )
+
+
 admin.site.register(Project, ProjectAdmin)
+admin.site.register(UserProjectAlert, UserProjectAlertAdmin)

+ 169 - 0
projects/migrations/0001_squashed_0009_alter_project_id_alter_projectcounter_id_and_more.py

@@ -0,0 +1,169 @@
+# Generated by Django 4.1.3 on 2022-12-12 21:45
+
+from django.db import migrations, models
+from django.conf import settings
+import django.db.models.deletion
+import django_extensions.db.fields
+import uuid
+
+
+class Migration(migrations.Migration):
+
+    replaces = [
+        ("projects", "0001_initial"),
+        ("projects", "0002_auto_20200429_2329"),
+        ("projects", "0003_projectcounter"),
+        ("projects", "0004_auto_20200805_0107"),
+        ("projects", "0005_project_first_event"),
+        ("projects", "0006_project_scrub_ip_addresses"),
+        ("projects", "0007_auto_20201026_2354"),
+        ("projects", "0008_alter_projectkey_created"),
+        ("projects", "0009_alter_project_id_alter_projectcounter_id_and_more"),
+        ("projects", "0010_rename_useralert"),
+    ]
+
+    initial = True
+
+    dependencies = [
+        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
+        ("organizations_ext", "0001_squashed_0009_organization_scrub_ip_addresses"),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="Project",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                (
+                    "slug",
+                    django_extensions.db.fields.AutoSlugField(
+                        blank=True,
+                        editable=False,
+                        populate_from=["name", "organization_id"],
+                    ),
+                ),
+                ("name", models.CharField(max_length=64)),
+                ("created", models.DateTimeField(auto_now_add=True, db_index=True)),
+                ("platform", models.CharField(blank=True, max_length=64, null=True)),
+                (
+                    "organization",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        related_name="projects",
+                        to="organizations_ext.organization",
+                    ),
+                ),
+                ("first_event", models.DateTimeField(null=True)),
+                (
+                    "scrub_ip_addresses",
+                    models.BooleanField(
+                        default=True, help_text="Should project anonymize IP Addresses"
+                    ),
+                ),
+            ],
+            options={
+                "unique_together": {("organization", "slug")},
+            },
+        ),
+        migrations.CreateModel(
+            name="ProjectCounter",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("value", models.PositiveIntegerField()),
+                (
+                    "project",
+                    models.OneToOneField(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="projects.project",
+                    ),
+                ),
+            ],
+        ),
+        migrations.CreateModel(
+            name="ProjectKey",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("label", models.CharField(blank=True, max_length=64)),
+                (
+                    "public_key",
+                    models.UUIDField(default=uuid.uuid4, editable=False, unique=True),
+                ),
+                ("created", models.DateTimeField(auto_now_add=True, db_index=True)),
+                (
+                    "rate_limit_count",
+                    models.PositiveSmallIntegerField(blank=True, null=True),
+                ),
+                (
+                    "rate_limit_window",
+                    models.PositiveSmallIntegerField(blank=True, null=True),
+                ),
+                ("data", models.JSONField(blank=True, null=True)),
+                (
+                    "project",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="projects.project",
+                    ),
+                ),
+            ],
+        ),
+        migrations.CreateModel(
+            name="UserProjectAlert",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                (
+                    "status",
+                    models.PositiveSmallIntegerField(choices=[(0, "off"), (1, "on")]),
+                ),
+                (
+                    "project",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="projects.project",
+                    ),
+                ),
+                (
+                    "user",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to=settings.AUTH_USER_MODEL,
+                    ),
+                ),
+            ],
+            options={
+                "unique_together": {("user", "project")},
+            },
+        ),
+    ]

+ 43 - 0
projects/migrations/0010_eventprojecthourlystatistic.py

@@ -0,0 +1,43 @@
+# Generated by Django 4.1.3 on 2022-12-12 22:08
+
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        (
+            "projects",
+            "0001_squashed_0009_alter_project_id_alter_projectcounter_id_and_more",
+        ),
+    ]
+
+    operations = [
+        migrations.CreateModel(
+            name="EventProjectHourlyStatistic",
+            fields=[
+                (
+                    "id",
+                    models.BigAutoField(
+                        auto_created=True,
+                        primary_key=True,
+                        serialize=False,
+                        verbose_name="ID",
+                    ),
+                ),
+                ("date", models.DateTimeField()),
+                ("count", models.PositiveIntegerField()),
+                (
+                    "project",
+                    models.ForeignKey(
+                        on_delete=django.db.models.deletion.CASCADE,
+                        to="projects.project",
+                    ),
+                ),
+            ],
+            options={
+                "unique_together": {("project", "date")},
+            },
+        ),
+    ]

+ 17 - 0
projects/migrations/0010_rename_useralert.py

@@ -0,0 +1,17 @@
+# Generated by Django 4.1.3 on 2022-11-29 15:48
+
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ("projects", "0009_alter_project_id_alter_projectcounter_id_and_more"),
+    ]
+
+    operations = [
+        migrations.RunSQL(
+            "ALTER TABLE users_userprojectalert RENAME TO projects_userprojectalert;",
+            "ALTER TABLE projects_userprojectalert RENAME TO users_userprojectalert;",
+        )
+    ]

+ 28 - 0
projects/models.py

@@ -134,3 +134,31 @@ class ProjectKey(CreatedModel):
             self.project_id,
             self.public_key_hex,
         )
+
+
+class EventProjectHourlyStatistic(models.Model):
+    project = models.ForeignKey("projects.Project", on_delete=models.CASCADE)
+    date = models.DateTimeField()
+    count = models.PositiveIntegerField()
+
+    class Meta:
+        unique_together = (("project", "date"),)
+
+
+class ProjectAlertStatus(models.IntegerChoices):
+    OFF = 0, "off"
+    ON = 1, "on"
+
+
+class UserProjectAlert(models.Model):
+    """
+    Determine if user alert notifications should always happen, never, or defer to default
+    Default is stored as the lack of record.
+    """
+
+    user = models.ForeignKey("users.User", on_delete=models.CASCADE)
+    project = models.ForeignKey("projects.Project", on_delete=models.CASCADE)
+    status = models.PositiveSmallIntegerField(choices=ProjectAlertStatus.choices)
+
+    class Meta:
+        unique_together = ("user", "project")

+ 1 - 12
users/admin.py

@@ -4,7 +4,7 @@ from django.utils.translation import gettext_lazy as _
 from import_export import resources
 from import_export.admin import ImportExportModelAdmin
 
-from .models import User, UserProjectAlert
+from .models import User
 
 
 class UserResource(resources.ModelResource):
@@ -82,15 +82,4 @@ class UserAdmin(BaseUserAdmin, ImportExportModelAdmin):
         return ", ".join([org.name for org in obj.organizations_ext_organization.all()])
 
 
-class UserProjectAlertAdmin(admin.ModelAdmin):
-    list_display = ("user", "project", "status")
-    list_filter = ("status",)
-    search_fields = ("project__name", "user__email")
-    raw_id_fields = (
-        "user",
-        "project",
-    )
-
-
 admin.site.register(User, UserAdmin)
-admin.site.register(UserProjectAlert, UserProjectAlertAdmin)

Some files were not shown because too many files changed in this diff