is_recurring boolean, WorkflowRun model

This commit is contained in:
shamoon
2024-10-23 16:55:52 -07:00
parent ae9bf6d286
commit a5f0f7efeb
10 changed files with 280 additions and 76 deletions

View File

@@ -1,6 +1,7 @@
# Generated by Django 5.1.1 on 2024-10-23 20:54
# Generated by Django 5.1.1 on 2024-10-24 04:03
import django.db.models.deletion
import django.utils.timezone
from django.db import migrations
from django.db import models
@@ -49,6 +50,15 @@ class Migration(migrations.Migration):
verbose_name="schedule delay field",
),
),
migrations.AddField(
model_name="workflowtrigger",
name="schedule_is_recurring",
field=models.BooleanField(
default=False,
help_text="If the schedule should be recurring.",
verbose_name="schedule is recurring",
),
),
migrations.AlterField(
model_name="workflowtrigger",
name="type",
@@ -63,4 +73,49 @@ class Migration(migrations.Migration):
verbose_name="Workflow Trigger Type",
),
),
migrations.CreateModel(
name="WorkflowRun",
fields=[
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
(
"run_at",
models.DateTimeField(
db_index=True,
default=django.utils.timezone.now,
verbose_name="date run",
),
),
(
"document",
models.ForeignKey(
null=True,
on_delete=django.db.models.deletion.CASCADE,
related_name="workflow_runs",
to="documents.document",
verbose_name="document",
),
),
(
"workflow",
models.ForeignKey(
on_delete=django.db.models.deletion.CASCADE,
related_name="runs",
to="documents.workflow",
verbose_name="workflow",
),
),
],
options={
"verbose_name": "workflow run",
"verbose_name_plural": "workflow runs",
},
),
]

View File

@@ -1115,6 +1115,14 @@ class WorkflowTrigger(models.Model):
),
)
schedule_is_recurring = models.BooleanField(
_("schedule is recurring"),
default=False,
help_text=_(
"If the schedule should be recurring.",
),
)
schedule_delay_field = models.CharField(
_("schedule delay field"),
max_length=20,
@@ -1383,3 +1391,33 @@ class Workflow(models.Model):
def __str__(self):
return f"Workflow: {self.name}"
class WorkflowRun(models.Model):
workflow = models.ForeignKey(
Workflow,
on_delete=models.CASCADE,
related_name="runs",
verbose_name=_("workflow"),
)
document = models.ForeignKey(
Document,
null=True,
on_delete=models.CASCADE,
related_name="workflow_runs",
verbose_name=_("document"),
)
run_at = models.DateTimeField(
_("date run"),
default=timezone.now,
db_index=True,
)
class Meta:
verbose_name = _("workflow run")
verbose_name_plural = _("workflow runs")
def __str__(self):
return f"WorkflowRun {self.pk}"

View File

@@ -1738,10 +1738,6 @@ class WorkflowTriggerSerializer(serializers.ModelSerializer):
label="Trigger Type",
)
schedule_delay = serializers.CharField(
required=False,
)
class Meta:
model = WorkflowTrigger
fields = [
@@ -1758,6 +1754,7 @@ class WorkflowTriggerSerializer(serializers.ModelSerializer):
"filter_has_correspondent",
"filter_has_document_type",
"schedule_delay",
"schedule_is_recurring",
"schedule_delay_field",
"schedule_delay_custom_field",
]

View File

@@ -37,6 +37,7 @@ from documents.models import PaperlessTask
from documents.models import Tag
from documents.models import Workflow
from documents.models import WorkflowAction
from documents.models import WorkflowRun
from documents.models import WorkflowTrigger
from documents.permissions import get_objects_for_user_owner_aware
from documents.permissions import set_permissions_for_object
@@ -915,6 +916,11 @@ def run_workflows(
document.save()
document.tags.set(doc_tag_ids)
WorkflowRun.objects.create(
workflow=workflow,
document=document if not use_overrides else None,
)
if use_overrides:
return overrides, "\n".join(messages)

View File

@@ -38,6 +38,7 @@ from documents.models import DocumentType
from documents.models import StoragePath
from documents.models import Tag
from documents.models import Workflow
from documents.models import WorkflowRun
from documents.models import WorkflowTrigger
from documents.parsers import DocumentParser
from documents.parsers import get_parser_class_for_mime_type
@@ -382,6 +383,26 @@ def check_scheduled_workflows():
f"Found {documents.count()} documents for trigger {trigger}",
)
for document in documents:
workflow_runs = WorkflowRun.objects.filter(
document=document,
workflow=workflow,
)
if not trigger.schedule_is_recurring and workflow_runs.exists():
# schedule is non-recurring and the workflow has already been run
logger.debug(
f"Skipping document {document} for non-recurring workflow {workflow} as it has already been run",
)
continue
elif (
trigger.schedule_is_recurring
and workflow_runs.exists()
and workflow_runs.last().run_at > timezone.now() - delay_td
):
# schedule is recurring but the last run was within the delay
logger.debug(
f"Skipping document {document} for recurring workflow {workflow} as the last run was within the delay",
)
continue
run_workflows(
WorkflowTrigger.WorkflowTriggerType.SCHEDULED,
document,

View File

@@ -1354,7 +1354,7 @@ class TestWorkflows(DirectoriesMixin, FileSystemAssertsMixin, APITestCase):
def test_new_trigger_type_raises_exception(self):
trigger = WorkflowTrigger.objects.create(
type=4,
type=99,
)
action = WorkflowAction.objects.create(
assign_title="Doc assign owner",