Compare commits
	
		
			2 Commits
		
	
	
		
			version/20
			...
			enterprise
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 74deedc9d4 | |||
| 3c8232b9a5 | 
							
								
								
									
										0
									
								
								authentik/enterprise/reporting/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								authentik/enterprise/reporting/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										0
									
								
								authentik/enterprise/reporting/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								authentik/enterprise/reporting/api/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										12
									
								
								authentik/enterprise/reporting/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								authentik/enterprise/reporting/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
				
			|||||||
 | 
					"""Reporting app config"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.enterprise.apps import EnterpriseConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AuthentikEnterpriseReporting(EnterpriseConfig):
 | 
				
			||||||
 | 
					    """authentik enterprise reporting app config"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = "authentik.enterprise.reporting"
 | 
				
			||||||
 | 
					    label = "authentik_reporting"
 | 
				
			||||||
 | 
					    verbose_name = "authentik Enterprise.Reporting"
 | 
				
			||||||
 | 
					    default = True
 | 
				
			||||||
							
								
								
									
										22
									
								
								authentik/enterprise/reporting/executor.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								authentik/enterprise/reporting/executor.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,22 @@
 | 
				
			|||||||
 | 
					from structlog.stdlib import get_logger
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.enterprise.reporting.models import Report
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReportExecutor:
 | 
				
			||||||
 | 
					    """Execute a report"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, report: Report) -> None:
 | 
				
			||||||
 | 
					        self.report = report
 | 
				
			||||||
 | 
					        self.logger = get_logger().bind(report=self.report)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def execute(self):
 | 
				
			||||||
 | 
					        # 1. Run through policies bound to report itself
 | 
				
			||||||
 | 
					        # 2. Get all bound components by running through ReportComponentBinding,
 | 
				
			||||||
 | 
					        #   while evaluating policies bound to each
 | 
				
			||||||
 | 
					        # 3. render the actual components
 | 
				
			||||||
 | 
					        # 4. Store the final data...somewhere??
 | 
				
			||||||
 | 
					        # 5. Optionally render PDF via chromedriver (special frontend that uses API)
 | 
				
			||||||
 | 
					        #   (not required for MVP)
 | 
				
			||||||
 | 
					        # 6. Send out link to CSV/PDF or attach to email via delivery
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
							
								
								
									
										131
									
								
								authentik/enterprise/reporting/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								authentik/enterprise/reporting/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,131 @@
 | 
				
			|||||||
 | 
					# Generated by Django 5.0.4 on 2024-04-18 21:47
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import authentik.lib.models
 | 
				
			||||||
 | 
					import django.db.models.deletion
 | 
				
			||||||
 | 
					import uuid
 | 
				
			||||||
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					from django.db import migrations, models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Migration(migrations.Migration):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    initial = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    dependencies = [
 | 
				
			||||||
 | 
					        ("authentik_events", "0007_event_authentik_e_action_9a9dd9_idx_and_more"),
 | 
				
			||||||
 | 
					        ("authentik_policies", "0011_policybinding_failure_result_and_more"),
 | 
				
			||||||
 | 
					        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    operations = [
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name="ReportComponent",
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "widget_uuid",
 | 
				
			||||||
 | 
					                    models.UUIDField(
 | 
				
			||||||
 | 
					                        default=uuid.uuid4, editable=False, primary_key=True, serialize=False
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                "verbose_name": "Report Component",
 | 
				
			||||||
 | 
					                "verbose_name_plural": "Report Components",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name="Report",
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "policybindingmodel_ptr",
 | 
				
			||||||
 | 
					                    models.OneToOneField(
 | 
				
			||||||
 | 
					                        auto_created=True,
 | 
				
			||||||
 | 
					                        on_delete=django.db.models.deletion.CASCADE,
 | 
				
			||||||
 | 
					                        parent_link=True,
 | 
				
			||||||
 | 
					                        primary_key=True,
 | 
				
			||||||
 | 
					                        serialize=False,
 | 
				
			||||||
 | 
					                        to="authentik_policies.policybindingmodel",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                ("name", models.TextField()),
 | 
				
			||||||
 | 
					                ("schedule", models.TextField()),
 | 
				
			||||||
 | 
					                ("output_type", models.TextField(choices=[("csv", "Csv"), ("pdf", "Pdf")])),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "delivery",
 | 
				
			||||||
 | 
					                    models.ForeignKey(
 | 
				
			||||||
 | 
					                        default=None,
 | 
				
			||||||
 | 
					                        null=True,
 | 
				
			||||||
 | 
					                        on_delete=django.db.models.deletion.SET_DEFAULT,
 | 
				
			||||||
 | 
					                        to="authentik_events.notificationtransport",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "run_as",
 | 
				
			||||||
 | 
					                    models.ForeignKey(
 | 
				
			||||||
 | 
					                        default=None,
 | 
				
			||||||
 | 
					                        null=True,
 | 
				
			||||||
 | 
					                        on_delete=django.db.models.deletion.SET_DEFAULT,
 | 
				
			||||||
 | 
					                        to=settings.AUTH_USER_MODEL,
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                "verbose_name": "Report",
 | 
				
			||||||
 | 
					                "verbose_name_plural": "Reports",
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            bases=("authentik_policies.policybindingmodel", models.Model),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.CreateModel(
 | 
				
			||||||
 | 
					            name="ReportComponentBinding",
 | 
				
			||||||
 | 
					            fields=[
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "policybindingmodel_ptr",
 | 
				
			||||||
 | 
					                    models.OneToOneField(
 | 
				
			||||||
 | 
					                        auto_created=True,
 | 
				
			||||||
 | 
					                        on_delete=django.db.models.deletion.CASCADE,
 | 
				
			||||||
 | 
					                        parent_link=True,
 | 
				
			||||||
 | 
					                        to="authentik_policies.policybindingmodel",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "binding_uuid",
 | 
				
			||||||
 | 
					                    models.UUIDField(
 | 
				
			||||||
 | 
					                        default=uuid.uuid4, editable=False, primary_key=True, serialize=False
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                ("enabled", models.BooleanField(default=True)),
 | 
				
			||||||
 | 
					                ("layout_x", models.PositiveIntegerField(default=0)),
 | 
				
			||||||
 | 
					                ("layout_y", models.PositiveIntegerField(default=0)),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "target",
 | 
				
			||||||
 | 
					                    models.ForeignKey(
 | 
				
			||||||
 | 
					                        on_delete=django.db.models.deletion.CASCADE, to="authentik_reporting.report"
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					                (
 | 
				
			||||||
 | 
					                    "widget",
 | 
				
			||||||
 | 
					                    authentik.lib.models.InheritanceForeignKey(
 | 
				
			||||||
 | 
					                        on_delete=django.db.models.deletion.CASCADE,
 | 
				
			||||||
 | 
					                        related_name="+",
 | 
				
			||||||
 | 
					                        to="authentik_reporting.reportcomponent",
 | 
				
			||||||
 | 
					                    ),
 | 
				
			||||||
 | 
					                ),
 | 
				
			||||||
 | 
					            ],
 | 
				
			||||||
 | 
					            options={
 | 
				
			||||||
 | 
					                "verbose_name": "Report Component Binding",
 | 
				
			||||||
 | 
					                "verbose_name_plural": "Report Component Bindings",
 | 
				
			||||||
 | 
					                "unique_together": {("target", "widget")},
 | 
				
			||||||
 | 
					            },
 | 
				
			||||||
 | 
					            bases=("authentik_policies.policybindingmodel", models.Model),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					        migrations.AddField(
 | 
				
			||||||
 | 
					            model_name="report",
 | 
				
			||||||
 | 
					            name="components",
 | 
				
			||||||
 | 
					            field=models.ManyToManyField(
 | 
				
			||||||
 | 
					                blank=True,
 | 
				
			||||||
 | 
					                related_name="bindings",
 | 
				
			||||||
 | 
					                through="authentik_reporting.ReportComponentBinding",
 | 
				
			||||||
 | 
					                to="authentik_reporting.reportcomponent",
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        ),
 | 
				
			||||||
 | 
					    ]
 | 
				
			||||||
							
								
								
									
										87
									
								
								authentik/enterprise/reporting/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								authentik/enterprise/reporting/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,87 @@
 | 
				
			|||||||
 | 
					"""Reporting models"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from uuid import uuid4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from celery.schedules import crontab
 | 
				
			||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.utils.translation import gettext_lazy as _
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.events.models import NotificationTransport
 | 
				
			||||||
 | 
					from authentik.lib.models import InheritanceForeignKey, SerializerModel
 | 
				
			||||||
 | 
					from authentik.policies.models import PolicyBindingModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OutputType(models.TextChoices):
 | 
				
			||||||
 | 
					    """Different choices in which a report can be 'rendered'"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    csv = "csv"
 | 
				
			||||||
 | 
					    pdf = "pdf"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Report(SerializerModel, PolicyBindingModel):
 | 
				
			||||||
 | 
					    """A report with a defined list of components, which can run on a schedule"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    name = models.TextField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    schedule = models.TextField()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # User under which permissions the queries are run,
 | 
				
			||||||
 | 
					    # when no user is selected the report is inactive
 | 
				
			||||||
 | 
					    run_as = models.ForeignKey(
 | 
				
			||||||
 | 
					        "authentik_core.user", on_delete=models.SET_DEFAULT, default=None, null=True
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    components = models.ManyToManyField(
 | 
				
			||||||
 | 
					        "ReportComponent", through="ReportComponentBinding", related_name="bindings", blank=True
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    output_type = models.TextField(choices=OutputType.choices)
 | 
				
			||||||
 | 
					    # Use notification transport to send report result (either link for webhook based?
 | 
				
			||||||
 | 
					    # maybe send full csv?) or fully rendered PDF via Email
 | 
				
			||||||
 | 
					    # when no transport is selected, reports are not sent anywhere but can be retrieved in authentik
 | 
				
			||||||
 | 
					    delivery = models.ForeignKey(
 | 
				
			||||||
 | 
					        NotificationTransport, on_delete=models.SET_DEFAULT, default=None, null=True
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self) -> str:
 | 
				
			||||||
 | 
					        return self.name
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_celery_schedule(self) -> crontab:
 | 
				
			||||||
 | 
					        return crontab(*self.schedule.split())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("Report")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("Reports")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReportComponentBinding(SerializerModel, PolicyBindingModel):
 | 
				
			||||||
 | 
					    """Binding of a component to a report"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    binding_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    enabled = models.BooleanField(default=True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    layout_x = models.PositiveIntegerField(default=0)
 | 
				
			||||||
 | 
					    layout_y = models.PositiveIntegerField(default=0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    target = models.ForeignKey("Report", on_delete=models.CASCADE)
 | 
				
			||||||
 | 
					    widget = InheritanceForeignKey("ReportComponent", on_delete=models.CASCADE, related_name="+")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self) -> str:
 | 
				
			||||||
 | 
					        return f"Binding from {self.report.name} to {self.widget}"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("Report Component Binding")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("Report Component Bindings")
 | 
				
			||||||
 | 
					        unique_together = ("target", "widget")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ReportComponent(SerializerModel):
 | 
				
			||||||
 | 
					    """An individual component of a report, a query or graph, etc"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    widget_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __str__(self) -> str:
 | 
				
			||||||
 | 
					        return super().__str__()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _("Report Component")
 | 
				
			||||||
 | 
					        verbose_name_plural = _("Report Components")
 | 
				
			||||||
							
								
								
									
										38
									
								
								authentik/enterprise/reporting/signals.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										38
									
								
								authentik/enterprise/reporting/signals.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,38 @@
 | 
				
			|||||||
 | 
					from json import dumps
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.db.models.signals import post_save, pre_delete
 | 
				
			||||||
 | 
					from django.dispatch import receiver
 | 
				
			||||||
 | 
					from django_celery_beat.models import CrontabSchedule, PeriodicTask
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from authentik.enterprise.reporting.models import Report
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@receiver(post_save, sender=Report)
 | 
				
			||||||
 | 
					def report_post_save(sender, instance: Report, **_):
 | 
				
			||||||
 | 
					    if instance.schedule == "":
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    schedule = CrontabSchedule.from_schedule(instance.get_celery_schedule())
 | 
				
			||||||
 | 
					    schedule.save()
 | 
				
			||||||
 | 
					    PeriodicTask.objects.update_or_create(
 | 
				
			||||||
 | 
					        name=str(instance.pk),
 | 
				
			||||||
 | 
					        defaults={
 | 
				
			||||||
 | 
					            "crontab": schedule,
 | 
				
			||||||
 | 
					            "task": "authentik.enterprise.reporting.tasks.process_report",
 | 
				
			||||||
 | 
					            "queue": "authentik_reporting",
 | 
				
			||||||
 | 
					            "description": f"Report {instance.name}",
 | 
				
			||||||
 | 
					            "kwargs": dumps(
 | 
				
			||||||
 | 
					                {
 | 
				
			||||||
 | 
					                    "report_uuid": str(instance.pk),
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            ),
 | 
				
			||||||
 | 
					        },
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@receiver(pre_delete, sender=Report)
 | 
				
			||||||
 | 
					def report_pre_delete(sender, instance: Report, **_):
 | 
				
			||||||
 | 
					    if instance.schedule == "":
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    PeriodicTask.objects.filter(name=str(instance.pk)).delete()
 | 
				
			||||||
 | 
					    # Cleanup schedules without any tasks
 | 
				
			||||||
 | 
					    CrontabSchedule.objects.filter(periodictask__isnull=True).delete()
 | 
				
			||||||
							
								
								
									
										11
									
								
								authentik/enterprise/reporting/tasks.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								authentik/enterprise/reporting/tasks.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					from authentik.enterprise.reporting.executor import ReportExecutor
 | 
				
			||||||
 | 
					from authentik.enterprise.reporting.models import Report
 | 
				
			||||||
 | 
					from authentik.root.celery import CELERY_APP
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@CELERY_APP.task()
 | 
				
			||||||
 | 
					def process_report(report_uuid: str):
 | 
				
			||||||
 | 
					    report = Report.objects.filter(pk=report_uuid).first()
 | 
				
			||||||
 | 
					    if not report or not report.run_as:
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					    ReportExecutor(report).execute()
 | 
				
			||||||
@ -17,6 +17,7 @@ TENANT_APPS = [
 | 
				
			|||||||
    "authentik.enterprise.providers.google_workspace",
 | 
					    "authentik.enterprise.providers.google_workspace",
 | 
				
			||||||
    "authentik.enterprise.providers.microsoft_entra",
 | 
					    "authentik.enterprise.providers.microsoft_entra",
 | 
				
			||||||
    "authentik.enterprise.providers.ssf",
 | 
					    "authentik.enterprise.providers.ssf",
 | 
				
			||||||
 | 
					    "authentik.enterprise.reporting",
 | 
				
			||||||
    "authentik.enterprise.stages.authenticator_endpoint_gdtc",
 | 
					    "authentik.enterprise.stages.authenticator_endpoint_gdtc",
 | 
				
			||||||
    "authentik.enterprise.stages.source",
 | 
					    "authentik.enterprise.stages.source",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
				
			|||||||
@ -125,6 +125,7 @@ TENANT_APPS = [
 | 
				
			|||||||
    "authentik.brands",
 | 
					    "authentik.brands",
 | 
				
			||||||
    "authentik.blueprints",
 | 
					    "authentik.blueprints",
 | 
				
			||||||
    "guardian",
 | 
					    "guardian",
 | 
				
			||||||
 | 
					    "django_celery_beat",
 | 
				
			||||||
]
 | 
					]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TENANT_MODEL = "authentik_tenants.Tenant"
 | 
					TENANT_MODEL = "authentik_tenants.Tenant"
 | 
				
			||||||
 | 
				
			|||||||
@ -1,14 +1,18 @@
 | 
				
			|||||||
"""Tenant-aware Celery beat scheduler"""
 | 
					"""Tenant-aware Celery beat scheduler"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from tenant_schemas_celery.scheduler import (
 | 
					from django_celery_beat.schedulers import DatabaseScheduler, ModelEntry
 | 
				
			||||||
    TenantAwarePersistentScheduler as BaseTenantAwarePersistentScheduler,
 | 
					from tenant_schemas_celery.scheduler import TenantAwareScheduleEntry, TenantAwareSchedulerMixin
 | 
				
			||||||
)
 | 
					 | 
				
			||||||
from tenant_schemas_celery.scheduler import TenantAwareScheduleEntry
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class TenantAwarePersistentScheduler(BaseTenantAwarePersistentScheduler):
 | 
					class SchedulerEntry(ModelEntry, TenantAwareScheduleEntry):
 | 
				
			||||||
 | 
					    pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TenantAwarePersistentScheduler(TenantAwareSchedulerMixin, DatabaseScheduler):
 | 
				
			||||||
    """Tenant-aware Celery beat scheduler"""
 | 
					    """Tenant-aware Celery beat scheduler"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    Entry = SchedulerEntry
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @classmethod
 | 
					    @classmethod
 | 
				
			||||||
    def get_queryset(cls):
 | 
					    def get_queryset(cls):
 | 
				
			||||||
        return super().get_queryset().filter(ready=True)
 | 
					        return super().get_queryset().filter(ready=True)
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										322
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										322
									
								
								poetry.lock
									
									
									
										generated
									
									
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -92,6 +92,7 @@ dacite = "*"
 | 
				
			|||||||
deepmerge = "*"
 | 
					deepmerge = "*"
 | 
				
			||||||
defusedxml = "*"
 | 
					defusedxml = "*"
 | 
				
			||||||
django = "*"
 | 
					django = "*"
 | 
				
			||||||
 | 
					django-celery-beat = "*"
 | 
				
			||||||
django-countries = "*"
 | 
					django-countries = "*"
 | 
				
			||||||
django-cte = "*"
 | 
					django-cte = "*"
 | 
				
			||||||
django-filter = "*"
 | 
					django-filter = "*"
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user