2026-04-27 14:57:33 +02:00
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
-- ORDS REST Services: Dokumenten-Check
|
|
|
|
|
|
-- Modul: frigosped.dc
|
|
|
|
|
|
-- Base-Path: /api/dc/
|
|
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
-- Voraussetzungen:
|
|
|
|
|
|
-- - Oracle Database 23c mit installiertem ORDS (>= 23.x)
|
|
|
|
|
|
-- - Script wird als Schema-Owner ausgeführt (hat ORDS_METADATA-Rolle)
|
|
|
|
|
|
-- - Datenbankschema bereits angelegt via "Datenmodell DocumentCheck.sql"
|
|
|
|
|
|
--
|
|
|
|
|
|
-- Deployment:
|
|
|
|
|
|
-- sqlplus user/pass@db @"Scripts/ORDS REST Services.sql"
|
|
|
|
|
|
--
|
|
|
|
|
|
-- Das Skript ist idempotent: bestehende Modul-Definition wird gelöscht und
|
|
|
|
|
|
-- neu erstellt. Bestehende Daten bleiben unberührt.
|
|
|
|
|
|
--
|
|
|
|
|
|
-- Endpunkt-Übersicht:
|
|
|
|
|
|
-- 1. GET /api/dc/catalogs/ Alle Fragenkataloge
|
2026-04-28 14:32:45 +02:00
|
|
|
|
-- 2. GET /api/dc/catalogs/:id/questions/ Fragen eines Katalogs (flach, nach order_nr sortiert)
|
|
|
|
|
|
-- 3. GET /api/dc/projects/:id Projektdetail (inkl. Detailstatus + ETA)
|
|
|
|
|
|
-- 4. POST /api/dc/projects/:id/start → IN_PROGRESS (setzt processing_started_at)
|
|
|
|
|
|
-- 5. PUT /api/dc/projects/:id/progress Fortschritt + Phase/Doc/Question/ETA
|
|
|
|
|
|
-- 6. POST /api/dc/projects/:id/complete → COMPLETED (setzt current_phase=COMPLETED)
|
2026-04-27 14:57:33 +02:00
|
|
|
|
-- 7. GET /api/dc/projects/:id/documents/ Dokument-Liste (kein BLOB)
|
2026-04-28 14:32:45 +02:00
|
|
|
|
-- 8. GET /api/dc/documents/:id/file BLOB-Download (PLSQL-Workaround)
|
|
|
|
|
|
-- 9. GET /api/dc/documents/:id/texts OCR-Text + Übersetzung lesen (für Resume)
|
|
|
|
|
|
-- 9b. PUT /api/dc/documents/:id/texts OCR-Text + Übersetzung schreiben
|
2026-04-27 14:57:33 +02:00
|
|
|
|
-- 10. POST /api/dc/projects/:id/results Prüfergebnis einfügen
|
|
|
|
|
|
-- 11. DELETE /api/dc/projects/:id/results Alle Ergebnisse löschen
|
|
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
|
|
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
-- BLOCK 1: Modul, Templates und Handler
|
|
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
|
|
|
|
|
|
-- -------------------------------------------------------------------------
|
|
|
|
|
|
-- Idempotenz: Modul löschen falls vorhanden
|
|
|
|
|
|
-- -------------------------------------------------------------------------
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
ORDS.DELETE_MODULE(p_module_name => 'frigosped.dc');
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN OTHERS THEN NULL;
|
|
|
|
|
|
END;
|
|
|
|
|
|
|
|
|
|
|
|
-- -------------------------------------------------------------------------
|
|
|
|
|
|
-- Modul definieren
|
|
|
|
|
|
-- -------------------------------------------------------------------------
|
|
|
|
|
|
ORDS.DEFINE_MODULE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_base_path => '/api/dc/',
|
|
|
|
|
|
p_items_per_page => 0, -- Pagination deaktiviert (Quarkus steuert selbst)
|
|
|
|
|
|
p_status => 'PUBLISHED',
|
|
|
|
|
|
p_comments => 'Dokumenten-Check Backend-API fuer den Quarkus-Verarbeitungsserver'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 1: catalogs/
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'catalogs/',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_etag_type => 'HASH',
|
|
|
|
|
|
p_comments => 'Alle Fragenkataloge mit Dokumenttyp-Name'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 1. GET /api/dc/catalogs/
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'catalogs/',
|
|
|
|
|
|
p_method => 'GET',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_COLLECTION_FEED,
|
|
|
|
|
|
p_items_per_page => 0,
|
|
|
|
|
|
p_comments => 'Gibt alle Kataloge mit Dokumenttyp-Name zurueck',
|
|
|
|
|
|
p_source => q'[
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
c.id,
|
|
|
|
|
|
c.name,
|
|
|
|
|
|
c.description,
|
|
|
|
|
|
c.document_type_id,
|
|
|
|
|
|
dt.name AS document_type_name,
|
|
|
|
|
|
c.row_version,
|
|
|
|
|
|
c.created,
|
|
|
|
|
|
c.updated
|
|
|
|
|
|
FROM dc_question_catalogs c
|
|
|
|
|
|
JOIN dc_document_types dt ON dt.id = c.document_type_id
|
|
|
|
|
|
ORDER BY c.name
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 2: catalogs/:catalog_id/questions/
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'catalogs/:catalog_id/questions/',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_etag_type => 'HASH',
|
|
|
|
|
|
p_comments => 'Alle Fragen eines Katalogs (flach mit Kategorie-Info)'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 2. GET /api/dc/catalogs/:catalog_id/questions/
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'catalogs/:catalog_id/questions/',
|
|
|
|
|
|
p_method => 'GET',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_COLLECTION_FEED,
|
|
|
|
|
|
p_items_per_page => 0,
|
|
|
|
|
|
p_comments => 'Flacher Join: Kategorie + Frage-Spalten, nach Kategorie/ID sortiert',
|
|
|
|
|
|
p_source => q'[
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
q.id AS question_id,
|
|
|
|
|
|
cat.id AS category_id,
|
|
|
|
|
|
cat.name AS category_name,
|
|
|
|
|
|
cat.description AS category_description,
|
2026-04-28 14:32:45 +02:00
|
|
|
|
cat.order_nr AS category_order_nr,
|
2026-04-27 14:57:33 +02:00
|
|
|
|
q.question_text,
|
|
|
|
|
|
q.evaluation_type,
|
|
|
|
|
|
q.threshold,
|
|
|
|
|
|
q.result_handling,
|
|
|
|
|
|
q.example_0_percent,
|
|
|
|
|
|
q.example_100_percent,
|
2026-04-28 14:32:45 +02:00
|
|
|
|
q.order_nr,
|
2026-04-27 14:57:33 +02:00
|
|
|
|
q.row_version
|
|
|
|
|
|
FROM dc_questions q
|
|
|
|
|
|
JOIN dc_question_categories cat ON cat.id = q.category_id
|
|
|
|
|
|
WHERE cat.catalog_id = :catalog_id
|
2026-04-28 14:32:45 +02:00
|
|
|
|
ORDER BY cat.order_nr, q.order_nr, q.id
|
2026-04-27 14:57:33 +02:00
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 3: projects/:project_id (kein Trailing-Slash = Einzel-Ressource)
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_etag_type => 'HASH',
|
|
|
|
|
|
p_comments => 'Einzelnes Projekt'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 3. GET /api/dc/projects/:project_id
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id',
|
|
|
|
|
|
p_method => 'GET',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_COLLECTION_ITEM,
|
|
|
|
|
|
p_items_per_page => 1,
|
2026-04-28 14:32:45 +02:00
|
|
|
|
p_comments => 'Projektdetail mit Status-Details und ETA; 404 wenn nicht gefunden',
|
2026-04-27 14:57:33 +02:00
|
|
|
|
p_source => q'[
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
p.id,
|
|
|
|
|
|
p.name,
|
|
|
|
|
|
p.description,
|
|
|
|
|
|
p.catalog_id,
|
|
|
|
|
|
p.created_by_user,
|
|
|
|
|
|
p.status,
|
|
|
|
|
|
p.progress,
|
|
|
|
|
|
p.completed_at,
|
|
|
|
|
|
p.notification_email,
|
|
|
|
|
|
p.row_version,
|
|
|
|
|
|
p.created,
|
2026-04-28 14:32:45 +02:00
|
|
|
|
p.updated,
|
|
|
|
|
|
p.processing_started_at,
|
|
|
|
|
|
p.current_phase,
|
|
|
|
|
|
p.current_doc_id,
|
|
|
|
|
|
p.current_question_id,
|
|
|
|
|
|
p.estimated_completion_at
|
2026-04-27 14:57:33 +02:00
|
|
|
|
FROM dc_projects p
|
|
|
|
|
|
WHERE p.id = :project_id
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 4: projects/:project_id/start
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/start',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_comments => 'Projekt-Status auf IN_PROGRESS setzen'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 4. POST /api/dc/projects/:project_id/start
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/start',
|
|
|
|
|
|
p_method => 'POST',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
2026-04-28 14:32:45 +02:00
|
|
|
|
p_comments => '200 OK; 409 wenn IN_PROGRESS/COMPLETED; 404 wenn Projekt fehlt',
|
2026-04-27 14:57:33 +02:00
|
|
|
|
p_source => q'[
|
|
|
|
|
|
DECLARE
|
|
|
|
|
|
v_status dc_projects.status%TYPE;
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
SELECT status
|
|
|
|
|
|
INTO v_status
|
|
|
|
|
|
FROM dc_projects
|
|
|
|
|
|
WHERE id = :project_id
|
|
|
|
|
|
FOR UPDATE NOWAIT;
|
|
|
|
|
|
|
2026-04-28 14:32:45 +02:00
|
|
|
|
IF v_status IN ('IN_PROGRESS', 'COMPLETED') THEN
|
2026-04-27 14:57:33 +02:00
|
|
|
|
:status_code := 409;
|
2026-04-28 14:32:45 +02:00
|
|
|
|
HTP.P('{"error":"Projekt ist bereits ' || v_status || '",'
|
2026-04-27 14:57:33 +02:00
|
|
|
|
|| '"current_status":"' || v_status || '"}');
|
|
|
|
|
|
RETURN;
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
|
|
UPDATE dc_projects
|
2026-04-28 14:32:45 +02:00
|
|
|
|
SET status = 'IN_PROGRESS',
|
|
|
|
|
|
progress = 0,
|
|
|
|
|
|
processing_started_at = SYSTIMESTAMP,
|
|
|
|
|
|
current_phase = 'OCR',
|
|
|
|
|
|
current_doc_id = NULL,
|
|
|
|
|
|
current_question_id = NULL,
|
|
|
|
|
|
estimated_completion_at = NULL
|
2026-04-27 14:57:33 +02:00
|
|
|
|
WHERE id = :project_id;
|
|
|
|
|
|
|
|
|
|
|
|
:status_code := 200;
|
|
|
|
|
|
HTP.P('{"project_id":' || :project_id
|
|
|
|
|
|
|| ',"status":"IN_PROGRESS"}');
|
|
|
|
|
|
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN NO_DATA_FOUND THEN
|
|
|
|
|
|
:status_code := 404;
|
|
|
|
|
|
HTP.P('{"error":"Projekt nicht gefunden","project_id":' || :project_id || '}');
|
|
|
|
|
|
WHEN OTHERS THEN
|
|
|
|
|
|
ROLLBACK;
|
|
|
|
|
|
:status_code := 500;
|
|
|
|
|
|
HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
|
|
|
|
|
|
END;
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 5: projects/:project_id/progress
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/progress',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_comments => 'Verarbeitungsfortschritt aktualisieren'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 5. PUT /api/dc/projects/:project_id/progress
|
2026-04-28 14:32:45 +02:00
|
|
|
|
-- Body: {"progress":45,"current_phase":"QUESTIONS","current_doc_id":2,
|
|
|
|
|
|
-- "current_question_id":15,"estimated_completion_at":"2026-04-27T16:30:00"}
|
2026-04-27 14:57:33 +02:00
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/progress',
|
|
|
|
|
|
p_method => 'PUT',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
2026-04-28 14:32:45 +02:00
|
|
|
|
p_comments => 'Setzt progress (0-100) + Phase/Dokument/Frage/ETA; 400 bei ungueltigem Wert',
|
2026-04-27 14:57:33 +02:00
|
|
|
|
p_source => q'[
|
|
|
|
|
|
DECLARE
|
2026-04-28 14:32:45 +02:00
|
|
|
|
v_body CLOB;
|
|
|
|
|
|
v_progress NUMBER;
|
|
|
|
|
|
v_phase VARCHAR2(30);
|
|
|
|
|
|
v_doc_id NUMBER;
|
|
|
|
|
|
v_question_id NUMBER;
|
|
|
|
|
|
v_eta_str VARCHAR2(30);
|
|
|
|
|
|
v_eta TIMESTAMP;
|
2026-04-27 14:57:33 +02:00
|
|
|
|
BEGIN
|
2026-04-28 14:32:45 +02:00
|
|
|
|
v_body := :body_text;
|
|
|
|
|
|
v_progress := TO_NUMBER(JSON_VALUE(v_body, '$.progress'));
|
|
|
|
|
|
v_phase := JSON_VALUE(v_body, '$.current_phase');
|
|
|
|
|
|
v_doc_id := TO_NUMBER(JSON_VALUE(v_body, '$.current_doc_id'));
|
|
|
|
|
|
v_question_id := TO_NUMBER(JSON_VALUE(v_body, '$.current_question_id'));
|
|
|
|
|
|
v_eta_str := JSON_VALUE(v_body, '$.estimated_completion_at');
|
2026-04-27 14:57:33 +02:00
|
|
|
|
|
|
|
|
|
|
IF v_progress IS NULL OR v_progress < 0 OR v_progress > 100 THEN
|
|
|
|
|
|
:status_code := 400;
|
|
|
|
|
|
HTP.P('{"error":"progress muss eine Zahl zwischen 0 und 100 sein"}');
|
|
|
|
|
|
RETURN;
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
2026-04-28 14:32:45 +02:00
|
|
|
|
IF v_eta_str IS NOT NULL THEN
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
v_eta := TO_TIMESTAMP(v_eta_str, 'YYYY-MM-DD"T"HH24:MI:SS');
|
|
|
|
|
|
EXCEPTION WHEN OTHERS THEN
|
|
|
|
|
|
v_eta := NULL;
|
|
|
|
|
|
END;
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
2026-04-27 14:57:33 +02:00
|
|
|
|
UPDATE dc_projects
|
2026-04-28 14:32:45 +02:00
|
|
|
|
SET progress = v_progress,
|
|
|
|
|
|
current_phase = NVL(v_phase, current_phase),
|
|
|
|
|
|
current_doc_id = v_doc_id,
|
|
|
|
|
|
current_question_id = v_question_id,
|
|
|
|
|
|
estimated_completion_at = NVL(v_eta, estimated_completion_at)
|
|
|
|
|
|
WHERE id = :project_id;
|
2026-04-27 14:57:33 +02:00
|
|
|
|
|
|
|
|
|
|
IF SQL%ROWCOUNT = 0 THEN
|
|
|
|
|
|
:status_code := 404;
|
|
|
|
|
|
HTP.P('{"error":"Projekt nicht gefunden","project_id":' || :project_id || '}');
|
|
|
|
|
|
RETURN;
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
|
|
:status_code := 200;
|
|
|
|
|
|
HTP.P('{"project_id":' || :project_id
|
|
|
|
|
|
|| ',"progress":' || v_progress || '}');
|
|
|
|
|
|
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN VALUE_ERROR THEN
|
|
|
|
|
|
:status_code := 400;
|
|
|
|
|
|
HTP.P('{"error":"progress muss eine gueltige Zahl sein"}');
|
|
|
|
|
|
WHEN OTHERS THEN
|
|
|
|
|
|
ROLLBACK;
|
|
|
|
|
|
:status_code := 500;
|
|
|
|
|
|
HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
|
|
|
|
|
|
END;
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 6: projects/:project_id/complete
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/complete',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_comments => 'Projekt auf COMPLETED setzen'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 6. POST /api/dc/projects/:project_id/complete
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/complete',
|
|
|
|
|
|
p_method => 'POST',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
2026-04-28 14:32:45 +02:00
|
|
|
|
p_comments => 'Setzt status=COMPLETED und erstellt Markdown-Bericht',
|
2026-04-27 14:57:33 +02:00
|
|
|
|
p_source => q'[
|
|
|
|
|
|
DECLARE
|
|
|
|
|
|
v_status dc_projects.status%TYPE;
|
|
|
|
|
|
v_now DATE := SYSDATE;
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
SELECT status
|
|
|
|
|
|
INTO v_status
|
|
|
|
|
|
FROM dc_projects
|
|
|
|
|
|
WHERE id = :project_id
|
|
|
|
|
|
FOR UPDATE NOWAIT;
|
|
|
|
|
|
|
|
|
|
|
|
IF v_status = 'COMPLETED' THEN
|
|
|
|
|
|
:status_code := 409;
|
|
|
|
|
|
HTP.P('{"error":"Projekt ist bereits COMPLETED","project_id":' || :project_id || '}');
|
|
|
|
|
|
RETURN;
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
|
|
UPDATE dc_projects
|
2026-04-28 14:32:45 +02:00
|
|
|
|
SET status = 'COMPLETED',
|
|
|
|
|
|
completed_at = v_now,
|
|
|
|
|
|
progress = 100,
|
|
|
|
|
|
current_phase = 'COMPLETED',
|
|
|
|
|
|
current_doc_id = NULL,
|
|
|
|
|
|
current_question_id = NULL,
|
|
|
|
|
|
estimated_completion_at = NULL
|
2026-04-27 14:57:33 +02:00
|
|
|
|
WHERE id = :project_id;
|
|
|
|
|
|
|
2026-04-28 14:32:45 +02:00
|
|
|
|
-- Markdown-Bericht generieren (Fehler ignoriert, Projekt bleibt COMPLETED)
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
dc_generate_report(:project_id);
|
|
|
|
|
|
EXCEPTION WHEN OTHERS THEN NULL;
|
|
|
|
|
|
END;
|
|
|
|
|
|
|
2026-04-27 14:57:33 +02:00
|
|
|
|
:status_code := 200;
|
|
|
|
|
|
HTP.P('{"project_id":' || :project_id
|
|
|
|
|
|
|| ',"status":"COMPLETED"'
|
|
|
|
|
|
|| ',"completed_at":"' || TO_CHAR(v_now, 'YYYY-MM-DD"T"HH24:MI:SS') || '"'
|
|
|
|
|
|
|| '}');
|
|
|
|
|
|
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN NO_DATA_FOUND THEN
|
|
|
|
|
|
:status_code := 404;
|
|
|
|
|
|
HTP.P('{"error":"Projekt nicht gefunden","project_id":' || :project_id || '}');
|
|
|
|
|
|
WHEN OTHERS THEN
|
|
|
|
|
|
ROLLBACK;
|
|
|
|
|
|
:status_code := 500;
|
|
|
|
|
|
HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
|
|
|
|
|
|
END;
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-04-28 14:32:45 +02:00
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 12: projects/:project_id/report → Bericht generieren / abrufen
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/report',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_comments => 'Markdown-Bericht eines Projekts'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 12. POST /api/dc/projects/:project_id/report – (neu) generieren
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/report',
|
|
|
|
|
|
p_method => 'POST',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
|
|
|
|
|
p_comments => 'Generiert report_markdown neu (z.B. nach Konfigurationsänderung)',
|
|
|
|
|
|
p_source => q'[
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
dc_generate_report(:project_id);
|
|
|
|
|
|
:status_code := 200;
|
|
|
|
|
|
HTP.P('{"project_id":' || :project_id || ',"report_generated":true}');
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN OTHERS THEN
|
|
|
|
|
|
ROLLBACK;
|
|
|
|
|
|
:status_code := 500;
|
|
|
|
|
|
HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
|
|
|
|
|
|
END;
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-04-27 14:57:33 +02:00
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 7: projects/:project_id/documents/
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/documents/',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_etag_type => 'HASH',
|
|
|
|
|
|
p_comments => 'Dokument-Liste eines Projekts (ohne BLOB-Payload)'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 7. GET /api/dc/projects/:project_id/documents/
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/documents/',
|
|
|
|
|
|
p_method => 'GET',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_COLLECTION_FEED,
|
|
|
|
|
|
p_items_per_page => 0,
|
|
|
|
|
|
p_comments => 'Metadaten aller Dokumente; has_original_text/has_translated_text als 0/1',
|
|
|
|
|
|
p_source => q'[
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
d.id,
|
|
|
|
|
|
d.filename,
|
|
|
|
|
|
d.mime_type,
|
|
|
|
|
|
d.document_type_id,
|
|
|
|
|
|
d.catalog_id,
|
|
|
|
|
|
d.uploaded_at,
|
|
|
|
|
|
CASE WHEN d.original_text IS NOT NULL THEN 1 ELSE 0 END AS has_original_text,
|
|
|
|
|
|
CASE WHEN d.translated_text IS NOT NULL THEN 1 ELSE 0 END AS has_translated_text,
|
|
|
|
|
|
d.row_version,
|
|
|
|
|
|
d.created,
|
|
|
|
|
|
d.updated
|
|
|
|
|
|
FROM dc_project_documents d
|
|
|
|
|
|
WHERE d.project_id = :project_id
|
|
|
|
|
|
ORDER BY d.uploaded_at
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 8: documents/:doc_id/file
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'documents/:doc_id/file',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_comments => 'BLOB-Download: original_file mit korrektem Content-Type'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 8. GET /api/dc/documents/:doc_id/file
|
2026-04-28 14:32:45 +02:00
|
|
|
|
-- PLSQL-Workaround: SOURCE_TYPE_MEDIA ruft getString() auf BLOB auf → ORA-17004 in dieser ORDS-Version
|
2026-04-27 14:57:33 +02:00
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'documents/:doc_id/file',
|
|
|
|
|
|
p_method => 'GET',
|
2026-04-28 14:32:45 +02:00
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
|
|
|
|
|
p_comments => 'Streamt original_file BLOB mit korrektem Content-Type (PLSQL-Workaround fuer ORDS-Bug)',
|
2026-04-27 14:57:33 +02:00
|
|
|
|
p_source => q'[
|
2026-04-28 14:32:45 +02:00
|
|
|
|
DECLARE
|
|
|
|
|
|
v_blob BLOB;
|
|
|
|
|
|
v_mime VARCHAR2(200);
|
|
|
|
|
|
v_filename VARCHAR2(500);
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
SELECT original_file, mime_type, filename
|
|
|
|
|
|
INTO v_blob, v_mime, v_filename
|
|
|
|
|
|
FROM dc_project_documents
|
|
|
|
|
|
WHERE id = :doc_id;
|
|
|
|
|
|
|
|
|
|
|
|
IF v_blob IS NULL THEN
|
|
|
|
|
|
:status_code := 404;
|
|
|
|
|
|
HTP.P('{"error":"Datei nicht vorhanden","doc_id":' || :doc_id || '}');
|
|
|
|
|
|
RETURN;
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
|
|
OWA_UTIL.MIME_HEADER(NVL(v_mime, 'application/octet-stream'), FALSE);
|
|
|
|
|
|
HTP.P('Content-Disposition: attachment; filename="' || v_filename || '"');
|
|
|
|
|
|
OWA_UTIL.HTTP_HEADER_CLOSE;
|
|
|
|
|
|
WPG_DOCLOAD.DOWNLOAD_FILE(v_blob);
|
|
|
|
|
|
:status_code := 200;
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN NO_DATA_FOUND THEN
|
|
|
|
|
|
:status_code := 404;
|
|
|
|
|
|
HTP.P('{"error":"Dokument nicht gefunden","doc_id":' || :doc_id || '}');
|
|
|
|
|
|
WHEN OTHERS THEN
|
|
|
|
|
|
:status_code := 500;
|
|
|
|
|
|
HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
|
|
|
|
|
|
END;
|
2026-04-27 14:57:33 +02:00
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
2026-04-28 14:32:45 +02:00
|
|
|
|
-- TEMPLATE 9: documents/:doc_id/texts (GET + PUT)
|
2026-04-27 14:57:33 +02:00
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'documents/:doc_id/texts',
|
|
|
|
|
|
p_priority => 0,
|
2026-04-28 14:32:45 +02:00
|
|
|
|
p_comments => 'OCR-Ergebnis und Uebersetzung lesen/schreiben'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 9a. GET /api/dc/documents/:doc_id/texts (fuer Resume nach Phase A)
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'documents/:doc_id/texts',
|
|
|
|
|
|
p_method => 'GET',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_COLLECTION_ITEM,
|
|
|
|
|
|
p_items_per_page => 1,
|
|
|
|
|
|
p_comments => 'Gibt original_text und translated_text zurueck',
|
|
|
|
|
|
p_source => q'[
|
|
|
|
|
|
SELECT
|
|
|
|
|
|
d.id,
|
|
|
|
|
|
d.original_text,
|
|
|
|
|
|
d.translated_text,
|
|
|
|
|
|
CASE WHEN d.original_text IS NOT NULL THEN 1 ELSE 0 END AS has_original_text,
|
|
|
|
|
|
CASE WHEN d.translated_text IS NOT NULL THEN 1 ELSE 0 END AS has_translated_text
|
|
|
|
|
|
FROM dc_project_documents d
|
|
|
|
|
|
WHERE d.id = :doc_id
|
|
|
|
|
|
]'
|
2026-04-27 14:57:33 +02:00
|
|
|
|
);
|
|
|
|
|
|
|
2026-04-28 14:32:45 +02:00
|
|
|
|
-- 9b. PUT /api/dc/documents/:doc_id/texts
|
2026-04-27 14:57:33 +02:00
|
|
|
|
-- Body: {"original_text": "## ...", "translated_text": "## ..."}
|
|
|
|
|
|
-- Beide Felder sind optional: NULL-Wert ueberschreibt nicht (nur wenn im Body vorhanden)
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'documents/:doc_id/texts',
|
|
|
|
|
|
p_method => 'PUT',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
|
|
|
|
|
p_comments => 'Schreibt original_text und/oder translated_text als CLOB',
|
|
|
|
|
|
p_source => q'[
|
|
|
|
|
|
DECLARE
|
|
|
|
|
|
v_body CLOB;
|
|
|
|
|
|
v_original_text CLOB;
|
|
|
|
|
|
v_translated CLOB;
|
|
|
|
|
|
v_rows INTEGER;
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
v_body := :body_text;
|
|
|
|
|
|
-- RETURNING CLOB erlaubt Werte groesser als 32767 Bytes (wichtig fuer OCR-Output)
|
|
|
|
|
|
v_original_text := JSON_VALUE(v_body, '$.original_text' RETURNING CLOB);
|
|
|
|
|
|
v_translated := JSON_VALUE(v_body, '$.translated_text' RETURNING CLOB);
|
|
|
|
|
|
|
|
|
|
|
|
UPDATE dc_project_documents
|
|
|
|
|
|
SET original_text = CASE
|
|
|
|
|
|
WHEN JSON_EXISTS(v_body, '$.original_text')
|
|
|
|
|
|
THEN v_original_text
|
|
|
|
|
|
ELSE original_text
|
|
|
|
|
|
END,
|
|
|
|
|
|
translated_text = CASE
|
|
|
|
|
|
WHEN JSON_EXISTS(v_body, '$.translated_text')
|
|
|
|
|
|
THEN v_translated
|
|
|
|
|
|
ELSE translated_text
|
|
|
|
|
|
END
|
|
|
|
|
|
WHERE id = :doc_id;
|
|
|
|
|
|
|
|
|
|
|
|
v_rows := SQL%ROWCOUNT;
|
|
|
|
|
|
IF v_rows = 0 THEN
|
|
|
|
|
|
:status_code := 404;
|
|
|
|
|
|
HTP.P('{"error":"Dokument nicht gefunden","doc_id":' || :doc_id || '}');
|
|
|
|
|
|
RETURN;
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
|
|
:status_code := 200;
|
|
|
|
|
|
HTP.P('{"doc_id":' || :doc_id || ',"updated":true}');
|
|
|
|
|
|
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN OTHERS THEN
|
|
|
|
|
|
ROLLBACK;
|
|
|
|
|
|
:status_code := 500;
|
|
|
|
|
|
HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
|
|
|
|
|
|
END;
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
-- TEMPLATE 10+11: projects/:project_id/results
|
|
|
|
|
|
-- (POST = Einfuegen, DELETE = Alle loeschen)
|
|
|
|
|
|
-- ===========================================================================
|
|
|
|
|
|
ORDS.DEFINE_TEMPLATE(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/results',
|
|
|
|
|
|
p_priority => 0,
|
|
|
|
|
|
p_comments => 'Prüfergebnisse: POST einfuegen, DELETE alle loeschen'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 10. POST /api/dc/projects/:project_id/results
|
|
|
|
|
|
-- Body: {
|
|
|
|
|
|
-- "question_id": 5,
|
|
|
|
|
|
-- "doc_id": 101,
|
|
|
|
|
|
-- "answer": "Ja, §305 BGB ist erfuellt.",
|
|
|
|
|
|
-- "score": 85,
|
|
|
|
|
|
-- "result_type": "OK",
|
|
|
|
|
|
-- "deviation": 0,
|
|
|
|
|
|
-- "warning": 0,
|
|
|
|
|
|
-- "the_comment": "Klare Einbeziehungsklausel vorhanden"
|
|
|
|
|
|
-- }
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/results',
|
|
|
|
|
|
p_method => 'POST',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
|
|
|
|
|
p_comments => '201 Created mit Location-Header; 400 bei fehlendem question_id/doc_id/result_type',
|
|
|
|
|
|
p_source => q'[
|
|
|
|
|
|
DECLARE
|
|
|
|
|
|
v_body CLOB;
|
|
|
|
|
|
v_question_id NUMBER;
|
|
|
|
|
|
v_doc_id NUMBER;
|
|
|
|
|
|
v_answer CLOB;
|
|
|
|
|
|
v_score NUMBER;
|
|
|
|
|
|
v_result_type VARCHAR2(20);
|
|
|
|
|
|
v_deviation NUMBER;
|
|
|
|
|
|
v_warning NUMBER;
|
|
|
|
|
|
v_comment CLOB;
|
|
|
|
|
|
v_new_id NUMBER;
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
v_body := :body_text;
|
|
|
|
|
|
v_question_id := TO_NUMBER(JSON_VALUE(v_body, '$.question_id'));
|
|
|
|
|
|
v_doc_id := TO_NUMBER(JSON_VALUE(v_body, '$.doc_id'));
|
|
|
|
|
|
v_answer := JSON_VALUE(v_body, '$.answer' RETURNING CLOB);
|
|
|
|
|
|
v_score := TO_NUMBER(JSON_VALUE(v_body, '$.score'));
|
|
|
|
|
|
v_result_type := JSON_VALUE(v_body, '$.result_type');
|
|
|
|
|
|
v_deviation := NVL(TO_NUMBER(JSON_VALUE(v_body, '$.deviation')), 0);
|
|
|
|
|
|
v_warning := NVL(TO_NUMBER(JSON_VALUE(v_body, '$.warning')), 0);
|
|
|
|
|
|
v_comment := JSON_VALUE(v_body, '$.the_comment' RETURNING CLOB);
|
|
|
|
|
|
|
|
|
|
|
|
-- Pflichtfelder prüfen
|
|
|
|
|
|
IF v_question_id IS NULL OR v_doc_id IS NULL THEN
|
|
|
|
|
|
:status_code := 400;
|
|
|
|
|
|
HTP.P('{"error":"question_id und doc_id sind Pflichtfelder"}');
|
|
|
|
|
|
RETURN;
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
|
|
IF v_result_type NOT IN ('OK', 'UNKLAR', 'NOK') THEN
|
|
|
|
|
|
:status_code := 400;
|
|
|
|
|
|
HTP.P('{"error":"result_type muss OK, UNKLAR oder NOK sein",'
|
|
|
|
|
|
|| '"received":"' || NVL(v_result_type, 'NULL') || '"}');
|
|
|
|
|
|
RETURN;
|
|
|
|
|
|
END IF;
|
|
|
|
|
|
|
|
|
|
|
|
INSERT INTO dc_results (
|
|
|
|
|
|
project_id,
|
|
|
|
|
|
question_id,
|
|
|
|
|
|
doc_id,
|
|
|
|
|
|
answer,
|
|
|
|
|
|
score,
|
|
|
|
|
|
result_type,
|
|
|
|
|
|
deviation,
|
|
|
|
|
|
warning,
|
|
|
|
|
|
the_comment,
|
|
|
|
|
|
evaluated_at
|
|
|
|
|
|
) VALUES (
|
|
|
|
|
|
:project_id,
|
|
|
|
|
|
v_question_id,
|
|
|
|
|
|
v_doc_id,
|
|
|
|
|
|
v_answer,
|
|
|
|
|
|
v_score,
|
|
|
|
|
|
v_result_type,
|
|
|
|
|
|
v_deviation,
|
|
|
|
|
|
v_warning,
|
|
|
|
|
|
v_comment,
|
|
|
|
|
|
SYSDATE
|
|
|
|
|
|
)
|
|
|
|
|
|
RETURNING id INTO v_new_id;
|
|
|
|
|
|
|
|
|
|
|
|
:status_code := 201;
|
|
|
|
|
|
HTP.P('{"id":' || v_new_id
|
|
|
|
|
|
|| ',"project_id":' || :project_id
|
|
|
|
|
|
|| ',"question_id":' || v_question_id
|
|
|
|
|
|
|| ',"doc_id":' || v_doc_id
|
|
|
|
|
|
|| ',"result_type":"' || v_result_type || '"'
|
|
|
|
|
|
|| ',"score":' || NVL(TO_CHAR(v_score), 'null')
|
|
|
|
|
|
|| '}');
|
|
|
|
|
|
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN OTHERS THEN
|
|
|
|
|
|
ROLLBACK;
|
|
|
|
|
|
:status_code := 500;
|
|
|
|
|
|
HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
|
|
|
|
|
|
END;
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
-- 11. DELETE /api/dc/projects/:project_id/results
|
|
|
|
|
|
ORDS.DEFINE_HANDLER(
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_pattern => 'projects/:project_id/results',
|
|
|
|
|
|
p_method => 'DELETE',
|
|
|
|
|
|
p_source_type => ORDS.SOURCE_TYPE_PLSQL,
|
|
|
|
|
|
p_comments => 'Loescht alle Ergebnisse eines Projekts (fuer Re-Processing)',
|
|
|
|
|
|
p_source => q'[
|
|
|
|
|
|
DECLARE
|
|
|
|
|
|
v_deleted INTEGER;
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
DELETE FROM dc_results
|
|
|
|
|
|
WHERE project_id = :project_id;
|
|
|
|
|
|
|
|
|
|
|
|
v_deleted := SQL%ROWCOUNT;
|
|
|
|
|
|
|
|
|
|
|
|
:status_code := 200;
|
|
|
|
|
|
HTP.P('{"project_id":' || :project_id
|
|
|
|
|
|
|| ',"deleted_count":' || v_deleted || '}');
|
|
|
|
|
|
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN OTHERS THEN
|
|
|
|
|
|
ROLLBACK;
|
|
|
|
|
|
:status_code := 500;
|
|
|
|
|
|
HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
|
|
|
|
|
|
END;
|
|
|
|
|
|
]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
COMMIT;
|
|
|
|
|
|
END;
|
|
|
|
|
|
/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
-- BLOCK 2: Auth / Privilege (fuer Produktion)
|
|
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
-- In der Entwicklung kann dieser Block auskommentiert bleiben – das Modul
|
|
|
|
|
|
-- ist dann ohne Auth-Pruefung erreichbar (STATUS = PUBLISHED genuegt).
|
|
|
|
|
|
--
|
|
|
|
|
|
-- Fuer Produktion einkommentieren und danach den OAuth2-Client anlegen:
|
|
|
|
|
|
--
|
|
|
|
|
|
-- SELECT client_id, client_secret
|
|
|
|
|
|
-- FROM user_ords_clients
|
|
|
|
|
|
-- WHERE name = 'quarkus_dc_backend';
|
|
|
|
|
|
--
|
|
|
|
|
|
-- Den client_id/client_secret in die Quarkus application.properties eintragen.
|
|
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
/*
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
-- Rolle anlegen (falls noch nicht vorhanden)
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
ORDS.CREATE_ROLE(p_role_name => 'dc_api_user');
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN OTHERS THEN NULL;
|
|
|
|
|
|
END;
|
|
|
|
|
|
|
|
|
|
|
|
-- Privilege loeschen falls vorhanden
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
ORDS.DROP_PRIVILEGE(p_name => 'dc.api.privilege');
|
|
|
|
|
|
EXCEPTION
|
|
|
|
|
|
WHEN OTHERS THEN NULL;
|
|
|
|
|
|
END;
|
|
|
|
|
|
|
|
|
|
|
|
-- Privilege definieren: mappt Rolle auf alle /api/dc/-Endpunkte
|
|
|
|
|
|
ORDS.DEFINE_PRIVILEGE(
|
|
|
|
|
|
p_privilege_name => 'dc.api.privilege',
|
|
|
|
|
|
p_roles => ORDS_TYPES.T_ORDS_VARCHARS('dc_api_user'),
|
|
|
|
|
|
p_patterns => ORDS_TYPES.T_ORDS_VARCHARS('/api/dc/*'),
|
|
|
|
|
|
p_module_name => 'frigosped.dc',
|
|
|
|
|
|
p_label => 'Dokumenten-Check API',
|
|
|
|
|
|
p_description => 'Zugriff auf alle /api/dc/-Endpunkte',
|
|
|
|
|
|
p_comments => 'Wird dem OAuth2-Client quarkus_dc_backend zugewiesen'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
COMMIT;
|
|
|
|
|
|
END;
|
|
|
|
|
|
/
|
|
|
|
|
|
|
|
|
|
|
|
-- OAuth2-Client fuer den Quarkus-Server (einmalig ausfuehren)
|
|
|
|
|
|
BEGIN
|
|
|
|
|
|
OAUTH.CREATE_CLIENT(
|
|
|
|
|
|
p_name => 'quarkus_dc_backend',
|
|
|
|
|
|
p_grant_type => 'CLIENT_CREDENTIALS',
|
|
|
|
|
|
p_owner => 'Frigosped',
|
|
|
|
|
|
p_description => 'Quarkus-Verarbeitungsserver Service-Account',
|
|
|
|
|
|
p_redirect_uri => NULL,
|
|
|
|
|
|
p_support_email => 'admin@frigosped.de',
|
|
|
|
|
|
p_privilege_names => 'dc.api.privilege'
|
|
|
|
|
|
);
|
|
|
|
|
|
COMMIT;
|
|
|
|
|
|
END;
|
|
|
|
|
|
/
|
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
-- Test-Aufrufe (curl)
|
|
|
|
|
|
-- =============================================================================
|
|
|
|
|
|
-- Ersetze <BASE> und <SCHEMA> mit deinen ORDS-Werten, z.B.:
|
|
|
|
|
|
-- BASE="https://apex.example.com/ords/FRIGOSPED_APP"
|
|
|
|
|
|
--
|
|
|
|
|
|
-- Ohne Auth (Entwicklung):
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 1. Alle Kataloge
|
|
|
|
|
|
-- curl -sS "$BASE/api/dc/catalogs/" | jq .
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 2. Fragen fuer Katalog 1
|
|
|
|
|
|
-- curl -sS "$BASE/api/dc/catalogs/1/questions/" | jq .
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 3. Projektdetail
|
|
|
|
|
|
-- curl -sS "$BASE/api/dc/projects/42" | jq .
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 4. Projekt starten
|
|
|
|
|
|
-- curl -sS -X POST "$BASE/api/dc/projects/42/start" | jq .
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 5. Fortschritt setzen
|
|
|
|
|
|
-- curl -sS -X PUT -H "Content-Type: application/json" \
|
|
|
|
|
|
-- -d '{"progress":35}' "$BASE/api/dc/projects/42/progress" | jq .
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 6. Projekt abschliessen
|
|
|
|
|
|
-- curl -sS -X POST "$BASE/api/dc/projects/42/complete" | jq .
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 7. Dokumente auflisten
|
|
|
|
|
|
-- curl -sS "$BASE/api/dc/projects/42/documents/" | jq .
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 8. Datei herunterladen
|
|
|
|
|
|
-- curl -sS -o original.pdf "$BASE/api/dc/documents/101/file"
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 9. OCR-Text + Uebersetzung zurueckschreiben
|
|
|
|
|
|
-- curl -sS -X PUT -H "Content-Type: application/json" \
|
|
|
|
|
|
-- -d '{"original_text":"## Vertrag\n\nAbsatz 1...",
|
|
|
|
|
|
-- "translated_text":"## Vertrag (DE)\n\nAbsatz 1..."}' \
|
|
|
|
|
|
-- "$BASE/api/dc/documents/101/texts" | jq .
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 10. Ergebnis einfuegen
|
|
|
|
|
|
-- curl -sS -X POST -H "Content-Type: application/json" \
|
|
|
|
|
|
-- -d '{"question_id":5,"doc_id":101,"answer":"Ja, §305 erfuellt.",
|
|
|
|
|
|
-- "score":85,"result_type":"OK","deviation":0,"warning":0,
|
|
|
|
|
|
-- "the_comment":"Klare Einbeziehungsklausel vorhanden"}' \
|
|
|
|
|
|
-- "$BASE/api/dc/projects/42/results" | jq .
|
|
|
|
|
|
--
|
|
|
|
|
|
-- # 11. Alle Ergebnisse loeschen (Re-Processing)
|
|
|
|
|
|
-- curl -sS -X DELETE "$BASE/api/dc/projects/42/results" | jq .
|
|
|
|
|
|
-- =============================================================================
|