diff --git a/backend/src/main/java/org/raddatz/familienarchiv/service/DocumentService.java b/backend/src/main/java/org/raddatz/familienarchiv/service/DocumentService.java index 95cd0921..7d08444a 100644 --- a/backend/src/main/java/org/raddatz/familienarchiv/service/DocumentService.java +++ b/backend/src/main/java/org/raddatz/familienarchiv/service/DocumentService.java @@ -65,7 +65,7 @@ public class DocumentService { // New uploads from the drop zone always start as incomplete document = Document.builder() .originalFilename(originalFilename) - .title(stripExtension(originalFilename)) + .title(titleFromFilename(originalFilename)) .status(DocumentStatus.UPLOADED) .metadataComplete(false) .build(); @@ -356,6 +356,57 @@ public class DocumentService { return dot > 0 ? filename.substring(0, dot) : filename; } + /** + * Derives a human-readable title from a structured filename. + * Supports patterns (full match only): + * YYYY-MM-DD_Lastname_Firstname.ext + * YYYYMMDD_Lastname_Firstname.ext + * Lastname_Firstname_YYYY-MM-DD.ext + * Lastname_Firstname_YYYYMMDD.ext + * Falls back to stripExtension for unrecognised names. + */ + private static final java.util.regex.Pattern FN_DATE_ISO_NAME = + java.util.regex.Pattern.compile("^(\\d{4}-\\d{2}-\\d{2})_(\\p{L}+)_(\\p{L}+)\\.[^.]+$"); + private static final java.util.regex.Pattern FN_DATE_COMPACT_NAME = + java.util.regex.Pattern.compile("^(\\d{8})_(\\p{L}+)_(\\p{L}+)\\.[^.]+$"); + private static final java.util.regex.Pattern FN_NAME_DATE_ISO = + java.util.regex.Pattern.compile("^(\\p{L}+)_(\\p{L}+)_(\\d{4}-\\d{2}-\\d{2})\\.[^.]+$"); + private static final java.util.regex.Pattern FN_NAME_DATE_COMPACT = + java.util.regex.Pattern.compile("^(\\p{L}+)_(\\p{L}+)_(\\d{8})\\.[^.]+$"); + + static String titleFromFilename(String filename) { + if (filename == null) return null; + java.util.regex.Matcher m; + String dateIso, lastName, firstName; + + if ((m = FN_DATE_ISO_NAME.matcher(filename)).matches()) { + dateIso = m.group(1); + lastName = m.group(2); + firstName = m.group(3); + } else if ((m = FN_DATE_COMPACT_NAME.matcher(filename)).matches()) { + String compact = m.group(1); + dateIso = compact.substring(0, 4) + "-" + compact.substring(4, 6) + "-" + compact.substring(6, 8); + lastName = m.group(2); + firstName = m.group(3); + } else if ((m = FN_NAME_DATE_ISO.matcher(filename)).matches()) { + lastName = m.group(1); + firstName = m.group(2); + dateIso = m.group(3); + } else if ((m = FN_NAME_DATE_COMPACT.matcher(filename)).matches()) { + lastName = m.group(1); + firstName = m.group(2); + String compact = m.group(3); + dateIso = compact.substring(0, 4) + "-" + compact.substring(4, 6) + "-" + compact.substring(6, 8); + } else { + return stripExtension(filename); + } + + // Format date as DD.MM.YYYY for the title + LocalDate date = LocalDate.parse(dateIso); + String dateDisplay = String.format("%02d.%02d.%d", date.getDayOfMonth(), date.getMonthValue(), date.getYear()); + return firstName + " " + lastName + " (" + dateDisplay + ")"; + } + private static String sha256Hex(byte[] bytes) { try { MessageDigest digest = MessageDigest.getInstance("SHA-256"); diff --git a/backend/src/test/java/org/raddatz/familienarchiv/service/DocumentServiceTest.java b/backend/src/test/java/org/raddatz/familienarchiv/service/DocumentServiceTest.java index 195810ab..ff87981d 100644 --- a/backend/src/test/java/org/raddatz/familienarchiv/service/DocumentServiceTest.java +++ b/backend/src/test/java/org/raddatz/familienarchiv/service/DocumentServiceTest.java @@ -518,4 +518,40 @@ class DocumentServiceTest { assertThat(count).isEqualTo(2); } + + // ─── titleFromFilename ──────────────────────────────────────────────────── + + @Test + void titleFromFilename_dateIso_name() { + assertThat(DocumentService.titleFromFilename("1965-03-12_Mueller_Hans.pdf")) + .isEqualTo("Hans Mueller (12.03.1965)"); + } + + @Test + void titleFromFilename_dateCompact_name() { + assertThat(DocumentService.titleFromFilename("19650312_Mueller_Hans.pdf")) + .isEqualTo("Hans Mueller (12.03.1965)"); + } + + @Test + void titleFromFilename_name_dateIso() { + assertThat(DocumentService.titleFromFilename("Mueller_Hans_1965-03-12.pdf")) + .isEqualTo("Hans Mueller (12.03.1965)"); + } + + @Test + void titleFromFilename_name_dateCompact() { + assertThat(DocumentService.titleFromFilename("Mueller_Hans_19650312.pdf")) + .isEqualTo("Hans Mueller (12.03.1965)"); + } + + @Test + void titleFromFilename_fallsBackToStripExtension() { + assertThat(DocumentService.titleFromFilename("scan_001.pdf")).isEqualTo("scan_001"); + } + + @Test + void titleFromFilename_null_returnsNull() { + assertThat(DocumentService.titleFromFilename(null)).isNull(); + } }