Files
Homme/HOEMMA_AENDERUNGSPLAN.md
T
2026-07-21 23:43:39 +02:00

274 lines
9.5 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HÖMMA Änderungsplan (Backend an Plugin anpassen)
خطة التعديل لمطابقة الباك-إند مع إضافة **HÖMMA Human Library**.
الفكرة: نضيف **Controller جديد واحد** يوفّر النقاط الثلاث تماماً كما يتوقّعها الـ plugin،
ويستدعي الخدمات الموجودة أصلاً. لا نلمس الـ Controllers الشغّالة.
الطريقة المختارة: **تعديل الباك-إند** (وليس الـ plugin).
النقاط الثلاث النهائية التي ستضعها في ووردبريس:
| خانة ووردبريس | الرابط النهائي (مثال Azure) |
|---|---|
| Speichern-Endpunkt | `https://<azure-url>/human-library` |
| Transkriptions-Endpunkt | `https://<azure-url>/transcribe` |
| Extraktions-Endpunkt | `https://<azure-url>/extract` |
---
## 1) pom.xml — إضافة دعم PDF
الـ plugin يسمح برفع Word **و PDF**، لكن المشروع يعالج `.docx` فقط.
أضف مكتبة PDFBox داخل `<dependencies>`:
```xml
<dependency>
<groupId>org.apache.pdfbox</groupId>
<artifactId>pdfbox</artifactId>
<version>2.0.31</version>
</dependency>
```
---
## 2) WordExportService.java — تفعيل قراءة PDF
الملف: `src/main/java/com/homme/demo/service/WordExportService.java`
داخل دالة `leseDatei(MultipartFile file)`، **قبل** سطر `return knowledgeIngestService.decodeBytes(bytes);`
أضف فرع PDF:
```java
if (name.endsWith(".pdf")) {
try (org.apache.pdfbox.pdmodel.PDDocument doc =
org.apache.pdfbox.pdmodel.PDDocument.load(bytes)) {
return new org.apache.pdfbox.text.PDFTextStripper().getText(doc);
}
}
```
بذلك: `.docx` عبر POI، `.pdf` عبر PDFBox، والباقي يبقى كما هو.
---
## 3) DTO جديد — طلب الحفظ (JSON)
ملف جديد: `src/main/java/com/homme/demo/dto/HumanLibraryRequest.java`
الـ plugin يرسل JSON بهذه الحقول: `source, name, gender, city, text, consent`.
```java
package com.homme.demo.dto;
import lombok.Getter;
import lombok.Setter;
@Getter
@Setter
public class HumanLibraryRequest {
private String source;
private String name;
private String gender;
private String city;
private String text;
private boolean consent;
}
```
---
## 4) DTO جديد — الرد الموحّد `{ "text": "..." }`
ملف جديد: `src/main/java/com/homme/demo/dto/TextResponse.java`
الـ plugin يتوقّع من الاستخراج والتفريغ رداً بصيغة `{"text":"..."}`.
```java
package com.homme.demo.dto;
public record TextResponse(String text) {
}
```
---
## 5) Controller جديد — النقاط الثلاث للـ plugin
ملف جديد: `src/main/java/com/homme/demo/controller/HumanLibraryController.java`
هذا هو قلب التعديل. يوفّر `/extract`, `/transcribe`, `/human-library`
بنفس أسماء الحقول والصيغ التي يتوقّعها الـ plugin، ويستدعي الخدمات الموجودة.
```java
package com.homme.demo.controller;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import com.homme.demo.dto.HumanLibraryRequest;
import com.homme.demo.dto.TextResponse;
import com.homme.demo.dto.UploadWordFileResponseDto;
import com.homme.demo.service.AzureSpeechService;
import com.homme.demo.service.KnowledgeIngestService;
import com.homme.demo.service.WordExportService;
import java.time.LocalDate;
@RestController
public class HumanLibraryController {
@Autowired private WordExportService wordExportService;
@Autowired private AzureSpeechService azureSpeechService;
@Autowired private KnowledgeIngestService knowledgeIngestService;
// (1) Extraktion: Word/PDF -> { "text": "..." }
@PostMapping(value = "/extract", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<TextResponse> extract(@RequestParam("file") MultipartFile file) throws Exception {
if (file == null || file.isEmpty()) {
return ResponseEntity.badRequest().body(new TextResponse(""));
}
String text = wordExportService.leseDatei(file);
return ResponseEntity.ok(new TextResponse(text == null ? "" : text));
}
// (2) Transkription: audio -> { "text": "..." }
@PostMapping(value = "/transcribe", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public ResponseEntity<TextResponse> transcribe(@RequestParam("audio") MultipartFile audio) throws Exception {
if (audio == null || audio.isEmpty()) {
return ResponseEntity.badRequest().body(new TextResponse(""));
}
String text = azureSpeechService.transcribe(audio.getBytes(), audio.getOriginalFilename());
return ResponseEntity.ok(new TextResponse(text == null ? "" : text));
}
// (3) Speichern: JSON { source, name, gender, city, text, consent } -> 201
@PostMapping(value = "/human-library", consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<?> saveContribution(@RequestBody HumanLibraryRequest req) {
try {
if (!req.isConsent()) {
return ResponseEntity.badRequest().body("Einwilligung (consent) erforderlich.");
}
if (req.getText() == null || req.getText().isBlank()) {
return ResponseEntity.badRequest().body("Text erforderlich.");
}
String title = (req.getName() == null || req.getName().isBlank())
? "Anonymous" : req.getName().trim().replace(" ", "_");
String city = (req.getCity() == null || req.getCity().isBlank())
? "Dortmund" : req.getCity();
// Text -> DOCX -> Humbee-Upload -> RAG-Verarbeitung (wie save-new-interview)
byte[] docx = wordExportService.createInterviewDocx(req.getText());
String fileName = "%s_%s_%s.docx".formatted(LocalDate.now(), title, city);
UploadWordFileResponseDto upload = knowledgeIngestService.uploadWordFile(fileName, docx);
if (!upload.isSuccess()) {
return ResponseEntity.status(HttpStatus.BAD_GATEWAY)
.body("Upload fehlgeschlagen: " + upload.getMessage());
}
knowledgeIngestService.processOneFile(upload.getLink());
return ResponseEntity.status(HttpStatus.CREATED).body("{\"status\":\"ok\"}");
} catch (Exception e) {
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body("Fehler beim Speichern: " + e.getMessage());
}
}
}
```
> ملاحظة: هذا يعيد استخدام نفس منطق `save-new-interview` الموجود.
> إذا أردت لاحقاً مرحلة **مراجعة تحريرية (ausstehend)** قبل RAG، نضيف حالة/جدول قبل استدعاء `processOneFile`.
> حقلا `gender` و `source` يُستقبلان لكن لا يُخزَّنان حالياً — أضِفهما للـ entity لاحقاً إن لزم.
---
## 6) CORS — (اختياري، موصى به)
الملف: `src/main/java/com/homme/demo/config/WebConfig.java`
حالياً مفتوح للكل (`allowedOriginPatterns("*")`) — يعمل.
للحصر على نطاقك فقط (كما نصح صاحب العمل) استبدله بـ:
```java
registry.addMapping("/**")
.allowedOrigins("https://hmmahumanlibraryruhr1314.live-website.com")
.allowedMethods("GET", "POST", "OPTIONS")
.allowedHeaders("*")
.allowCredentials(false)
.maxAge(3600);
```
(اترك `*` إن أردت تبسيط الاختبار الآن.)
---
## 7) Dockerfile — جديد في جذر المشروع
ملف جديد: `Dockerfile` (بجانب `pom.xml`)
```dockerfile
FROM maven:3.9-eclipse-temurin-17 AS build
WORKDIR /app
COPY pom.xml .
RUN mvn -q dependency:go-offline
COPY src ./src
RUN mvn -q clean package -DskipTests
FROM eclipse-temurin:17-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 9192
ENTRYPOINT ["java", "-jar", "app.jar"]
```
ملف جديد: `.dockerignore`
```
target/
.git/
.settings/
.classpath
.project
*.iml
```
---
## 8) متغيّرات البيئة (Azure) — ليست تعديل كود
عند الرفع على Azure App Service اضبط:
```
WEBSITES_PORT=9192
DB_PASSWORD=...
OPENAI_API_MISTRAL_KEY=...
AZURE_EMBEDDING_KEY=...
AZURE_SPEECH_KEY=...
HUMBEE_PASSWORD=...
```
وفعّل **Managed Identity** للتطبيق (بسبب `DefaultAzureCredential`
وامنحها صلاحية على مورد `homme-foundry-dev`،
واسمح لـ PostgreSQL بالاتصال (Allow Azure services).
---
## ملخّص الملفات
| # | الملف | النوع |
|---|---|---|
| 1 | `pom.xml` | تعديل (dependency) |
| 2 | `service/WordExportService.java` | تعديل (فرع PDF) |
| 3 | `dto/HumanLibraryRequest.java` | جديد |
| 4 | `dto/TextResponse.java` | جديد |
| 5 | `controller/HumanLibraryController.java` | جديد |
| 6 | `config/WebConfig.java` | تعديل (اختياري) |
| 7 | `Dockerfile` + `.dockerignore` | جديد |
| 8 | متغيّرات Azure | إعداد (لا كود) |
بعد هذه التعديلات: تبني بـ Docker → ترفع على Azure → تضع الروابط الثلاثة في صفحة إعدادات HÖMMA Human Library → يبدأ النموذج يعمل بالكامل (استخراج، تفريغ، حفظ).