fix: merge main branch and renumber migration 027→037

Resolve 3 merge conflicts and renumber the automation_hooks migration
to follow main's migration chain (036_add_document_translation_fields).

Conflicts resolved:
- app/api/__init__.py: add automation_router alongside main's new routers
- app/utils/settings_service.py: add automation_hooks_enabled alongside compliance_enabled
- tests/conftest.py: add AutomationHook alongside AuditLog/ComplianceTemplate imports

Migration renumbered:
- 027_add_automation_hooks → 037_add_automation_hooks
- down_revision: 026_add_scheduled_jobs → 036_add_document_translation_fields

Co-authored-by: christianlouis <361235+christianlouis@users.noreply.github.com>
This commit is contained in:
copilot-swe-agent[bot]
2026-03-16 22:40:15 +00:00
parent 6a83d51d88
commit 204000aabc
318 changed files with 317516 additions and 2858 deletions
+42 -42
View File
@@ -1,5 +1,5 @@
{% extends "base.html" %}
{% block title %}Processing Pipelines DocuElevate{% endblock %}
{% block title %}{{ _("pipelines.title") }} DocuElevate{% endblock %}
{% block content %}
<div class="container mx-auto px-4 py-8" x-data="pipelinesApp()" x-init="init()">
@@ -9,13 +9,13 @@
<div>
<h1 class="text-3xl font-bold text-gray-900 dark:text-white flex items-center gap-2">
<i class="fas fa-project-diagram text-blue-500" aria-hidden="true"></i>
Processing Pipelines
{{ _("pipelines.page_title") }}
</h1>
<p class="text-gray-500 dark:text-gray-400 text-sm mt-1">
Define and manage custom document processing workflows.
System pipelines (created by admins) are shown with a
<span class="text-xs font-semibold text-purple-700 bg-purple-50 border border-purple-200 rounded px-1">System</span>
badge and are visible to all users.
{{ _("pipelines.subtitle_intro") }}
{{ _("pipelines.subtitle_system_pre") }}
<span class="text-xs font-semibold text-purple-700 bg-purple-50 border border-purple-200 rounded px-1">{{ _("pipelines.system_label") }}</span>
{{ _("pipelines.subtitle_system_post") }}
</p>
</div>
<button
@@ -24,7 +24,7 @@
class="inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-md shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
style="min-height:44px;"
>
<i class="fas fa-plus mr-2" aria-hidden="true"></i> New Pipeline
<i class="fas fa-plus mr-2" aria-hidden="true"></i> {{ _("pipelines.new_pipeline_btn") }}
</button>
</div>
@@ -51,7 +51,7 @@
<template x-if="loading">
<div class="text-center py-12 text-gray-400">
<i class="fas fa-spinner fa-spin text-3xl mb-3" aria-hidden="true"></i>
<p>Loading pipelines</p>
<p>{{ _("pipelines.loading") }}</p>
</div>
</template>
@@ -59,15 +59,15 @@
<template x-if="!loading && pipelines.length === 0">
<div class="text-center py-16 bg-white dark:bg-gray-800 rounded-lg shadow">
<i class="fas fa-project-diagram text-5xl text-gray-300 mb-4" aria-hidden="true"></i>
<h2 class="text-xl font-semibold text-gray-700 dark:text-gray-300 mb-2">No pipelines yet</h2>
<p class="text-gray-500 dark:text-gray-400 mb-6">Create your first pipeline to define custom document processing workflows.</p>
<h2 class="text-xl font-semibold text-gray-700 dark:text-gray-300 mb-2">{{ _("pipelines.empty_state") }}</h2>
<p class="text-gray-500 dark:text-gray-400 mb-6">{{ _("pipelines.empty_state_hint") }}</p>
<button
type="button"
@click="openCreatePipelineModal()"
class="inline-flex items-center px-5 py-2.5 bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium rounded-md"
style="min-height:44px;"
>
<i class="fas fa-plus mr-2" aria-hidden="true"></i> Create Pipeline
<i class="fas fa-plus mr-2" aria-hidden="true"></i> {{ _("pipelines.create") }}
</button>
</div>
</template>
@@ -84,13 +84,13 @@
<h2 class="text-lg font-semibold text-gray-900 dark:text-white" x-text="pipeline.name"></h2>
<!-- Badges -->
<template x-if="pipeline.owner_id === null">
<span class="text-xs font-semibold text-purple-700 bg-purple-50 border border-purple-200 rounded px-1.5 py-0.5">System</span>
<span class="text-xs font-semibold text-purple-700 bg-purple-50 border border-purple-200 rounded px-1.5 py-0.5">{{ _("pipelines.system_label") }}</span>
</template>
<template x-if="pipeline.is_default">
<span class="text-xs font-semibold text-green-700 bg-green-50 border border-green-200 rounded px-1.5 py-0.5">Default</span>
<span class="text-xs font-semibold text-green-700 bg-green-50 border border-green-200 rounded px-1.5 py-0.5">{{ _("pipelines.default_label") }}</span>
</template>
<template x-if="!pipeline.is_active">
<span class="text-xs font-semibold text-gray-500 bg-gray-100 border border-gray-200 rounded px-1.5 py-0.5">Inactive</span>
<span class="text-xs font-semibold text-gray-500 bg-gray-100 border border-gray-200 rounded px-1.5 py-0.5">{{ _("pipelines.inactive_label") }}</span>
</template>
</div>
@@ -115,7 +115,7 @@
style="min-height:36px;"
:aria-label="`Edit pipeline ${pipeline.name}`"
>
<i class="fas fa-pencil-alt mr-1" aria-hidden="true"></i> Edit
<i class="fas fa-pencil-alt mr-1" aria-hidden="true"></i> {{ _("common.edit") }}
</button>
<button
type="button"
@@ -124,7 +124,7 @@
style="min-height:36px;"
:aria-label="`Delete pipeline ${pipeline.name}`"
>
<i class="fas fa-trash mr-1" aria-hidden="true"></i> Delete
<i class="fas fa-trash mr-1" aria-hidden="true"></i> {{ _("common.delete") }}
</button>
</div>
</div>
@@ -139,7 +139,7 @@
<!-- Step list -->
<template x-if="(pipeline.steps || []).length === 0">
<p class="text-sm text-gray-400 italic mb-3">No steps defined. Add a step to start building your pipeline.</p>
<p class="text-sm text-gray-400 italic mb-3">{{ _("pipelines.no_steps") }}</p>
</template>
<template x-if="(pipeline.steps || []).length > 0">
@@ -153,7 +153,7 @@
<p class="text-xs text-gray-400 truncate" x-text="step.step_type"></p>
</div>
<template x-if="!step.enabled">
<span class="text-xs text-gray-400 bg-gray-200 rounded px-1.5 py-0.5 flex-shrink-0">Disabled</span>
<span class="text-xs text-gray-400 bg-gray-200 rounded px-1.5 py-0.5 flex-shrink-0">{{ _("pipelines.disabled_label") }}</span>
</template>
</div>
<div class="flex items-center gap-1.5 flex-shrink-0">
@@ -201,7 +201,7 @@
style="min-height:36px;"
:aria-label="`Add step to ${pipeline.name}`"
>
<i class="fas fa-plus mr-1" aria-hidden="true"></i> Add Step
<i class="fas fa-plus mr-1" aria-hidden="true"></i> {{ _("pipelines.add_step_btn") }}
</button>
</div>
@@ -241,7 +241,7 @@
<!-- Name -->
<div>
<label for="pipelineName" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Name <span aria-hidden="true" class="text-red-500">*</span>
{{ _("common.name") }} <span aria-hidden="true" class="text-red-500">*</span>
</label>
<input
id="pipelineName"
@@ -250,21 +250,21 @@
maxlength="255"
required
class="w-full border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 dark:bg-gray-700 dark:text-white"
placeholder="My pipeline"
placeholder="{{ _('pipelines.name_placeholder') }}"
autocomplete="off"
/>
</div>
<!-- Description -->
<div>
<label for="pipelineDesc" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">Description</label>
<label for="pipelineDesc" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">{{ _("common.description") }}</label>
<textarea
id="pipelineDesc"
x-model="pipelineModal.form.description"
rows="2"
maxlength="4096"
class="w-full border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 dark:bg-gray-700 dark:text-white resize-none"
placeholder="Optional description"
placeholder="{{ _('pipelines.description_placeholder') }}"
></textarea>
</div>
@@ -276,7 +276,7 @@
x-model="pipelineModal.form.is_default"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-400"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">Set as my default pipeline</span>
<span class="text-sm text-gray-700 dark:text-gray-300">{{ _("pipelines.set_default") }}</span>
</label>
<label class="inline-flex items-center gap-2 cursor-pointer">
<input
@@ -284,7 +284,7 @@
x-model="pipelineModal.form.is_active"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-400"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">Active</span>
<span class="text-sm text-gray-700 dark:text-gray-300">{{ _("pipelines.active_label") }}</span>
</label>
</div>
@@ -296,7 +296,7 @@
x-model="pipelineModal.form.system"
class="rounded border-gray-300 text-purple-600 focus:ring-purple-400"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">System pipeline (visible to all users)</span>
<span class="text-sm text-gray-700 dark:text-gray-300">{{ _("pipelines.system_pipeline_label") }}</span>
</label>
</template>
@@ -310,16 +310,16 @@
@click="closePipelineModal()"
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50"
style="min-height:44px;"
>Cancel</button>
>{{ _("common.cancel") }}</button>
<button
type="submit"
:disabled="pipelineModal.saving"
class="px-4 py-2 text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 disabled:opacity-60 rounded-md"
style="min-height:44px;"
>
<template x-if="pipelineModal.saving">
<i class="fas fa-spinner fa-spin mr-1" aria-hidden="true"></i>
</template>
<template x-if="pipelineModal.saving">
<i class="fas fa-spinner fa-spin mr-1" aria-hidden="true"></i>
</template>
<span x-text="pipelineModal.editMode ? 'Save Changes' : 'Create'"></span>
</button>
</div>
@@ -358,7 +358,7 @@
<!-- Step type -->
<div>
<label for="stepType" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Step Type <span aria-hidden="true" class="text-red-500">*</span>
{{ _("pipelines.step_type_label") }} <span aria-hidden="true" class="text-red-500">*</span>
</label>
<select
id="stepType"
@@ -366,7 +366,7 @@
:disabled="stepModal.editMode"
class="w-full border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 dark:bg-gray-700 dark:text-white"
>
<option value="">— select —</option>
<option value="">{{ _("common.select_placeholder") }}</option>
<template x-for="[key, meta] in Object.entries(stepTypes)" :key="key">
<option :value="key" x-text="meta.label"></option>
</template>
@@ -379,7 +379,7 @@
<!-- Custom label -->
<div>
<label for="stepLabel" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
Custom Label <span class="text-gray-400 font-normal">(optional)</span>
{{ _("pipelines.custom_label_label") }} <span class="text-gray-400 font-normal">{{ _("pipelines.optional_suffix") }}</span>
</label>
<input
id="stepLabel"
@@ -387,7 +387,7 @@
x-model="stepModal.form.label"
maxlength="255"
class="w-full border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 dark:bg-gray-700 dark:text-white"
placeholder="Override the default step name"
placeholder="{{ _('pipelines.step_label_placeholder') }}"
autocomplete="off"
/>
</div>
@@ -400,7 +400,7 @@
x-model="stepModal.form.config.force_cloud_ocr"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-400"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">Force cloud OCR (skip local text extraction)</span>
<span class="text-sm text-gray-700 dark:text-gray-300">{{ _("pipelines.force_cloud_ocr_label") }}</span>
</label>
</template>
@@ -408,14 +408,14 @@
<template x-if="stepModal.form.step_type === 'ocr'">
<div>
<label for="ocrLanguage" class="block text-sm font-medium text-gray-700 dark:text-gray-300 mb-1">
OCR Language
{{ _("pipelines.ocr_language_label") }}
</label>
<select
id="ocrLanguage"
x-model="stepModal.form.config.ocr_language"
class="w-full border border-gray-300 dark:border-gray-600 rounded-md px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-blue-400 dark:bg-gray-700 dark:text-white"
>
<option value="auto">Auto (use system default)</option>
<option value="auto">{{ _("pipelines.ocr_auto") }}</option>
<option value="ara">Arabic</option>
<option value="chi_sim">Chinese (Simplified)</option>
<option value="chi_tra">Chinese (Traditional)</option>
@@ -446,7 +446,7 @@
<option value="vie">Vietnamese</option>
</select>
<p class="mt-1 text-xs text-gray-400">
Overrides the global language setting for Tesseract/EasyOCR. Azure and Mistral auto-detect the language.
{{ _("pipelines.ocr_language_hint") }}
</p>
</div>
</template>
@@ -458,7 +458,7 @@
x-model="stepModal.form.enabled"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-400"
/>
<span class="text-sm text-gray-700 dark:text-gray-300">Enabled</span>
<span class="text-sm text-gray-700 dark:text-gray-300">{{ _("pipelines.enabled_label") }}</span>
</label>
<!-- Error -->
@@ -471,7 +471,7 @@
@click="closeStepModal()"
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50"
style="min-height:44px;"
>Cancel</button>
>{{ _("common.cancel") }}</button>
<button
type="submit"
:disabled="stepModal.saving"
@@ -515,13 +515,13 @@
@click="confirmModal.open = false"
class="px-4 py-2 text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 border border-gray-300 dark:border-gray-600 rounded-md hover:bg-gray-50"
style="min-height:44px;"
>Cancel</button>
>{{ _("common.cancel") }}</button>
<button
type="button"
@click="confirmModal.action(); confirmModal.open = false"
class="px-4 py-2 text-sm font-medium text-white bg-red-600 hover:bg-red-700 rounded-md"
style="min-height:44px;"
>Delete</button>
>{{ _("common.delete") }}</button>
</div>
</div>
</div>