Skip to content

Commit 0aa6688

Browse files
committed
feat: add API entry for createTrainingBlank
1 parent f8a599c commit 0aa6688

5 files changed

Lines changed: 136 additions & 7 deletions

File tree

doc/Grobid-service.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,19 @@ curl --location 'http://localhost:8070/api/createTraining' \
916916

917917
The zip file will contain the training data as described in the [general principles](General-principles.md) of GROBID training data.
918918

919+
### createTrainingBlank
920+
921+
Generate blank training data (raw features + empty TEI template) from a PDF without requiring a trained model. This is useful for creating training data from scratch.
922+
923+
```bash
924+
curl --location 'http://localhost:8070/api/createTrainingBlank' \
925+
--form 'input=@"PATH_DOCUMENT"' \
926+
--form 'flavor=article/dh-law-footnotes-token' \
927+
--output output_name.zip
928+
```
929+
930+
The `flavor` parameter is optional. When set, the generated raw feature files will use the feature extraction strategy of that flavor (e.g., token-level features for `article/dh-law-footnotes-token`).
931+
919932
## Parallel mode
920933

921934
The Grobid REST API provides a very efficient way to use the library out of the box, because the service exploits multithreading.

grobid-core/src/main/java/org/grobid/core/engines/Engine.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -539,6 +539,10 @@ public void createTrainingBlank(File inputFile, String pathRaw, String pathTEI,
539539
parsers.getSegmentationParser().createBlankTrainingData(inputFile, pathRaw, pathTEI, id);
540540
}
541541

542+
public void createTrainingBlank(File inputFile, String pathRaw, String pathTEI, int id, GrobidModels.Flavor flavor) {
543+
parsers.getSegmentationParser(flavor).createBlankTrainingData(inputFile, pathRaw, pathTEI, id);
544+
}
545+
542546
/**
543547
* Create training data for all models based on the application of
544548
* the current full text model on a new PDF

grobid-service/src/main/java/org/grobid/service/GrobidPaths.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,4 +172,6 @@ public interface GrobidPaths {
172172
String PATH_TRAINING_RESULT = "trainingResult";
173173

174174
String PATH_CREATE_TRAINING = "createTraining";
175+
176+
String PATH_CREATE_TRAINING_BLANK = "createTrainingBlank";
175177
}

grobid-service/src/main/java/org/grobid/service/GrobidRestService.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,4 +893,20 @@ public Response createTraining_post(
893893
);
894894
}
895895

896+
@Path(PATH_CREATE_TRAINING_BLANK)
897+
@Consumes(MediaType.MULTIPART_FORM_DATA)
898+
@Produces("application/zip")
899+
@POST
900+
public Response createTrainingBlank_post(
901+
@FormDataParam(INPUT) InputStream inputStream,
902+
@FormDataParam(INPUT) FormDataBodyPart inputBodyPart,
903+
@FormDataParam(FLAVOR) String flavor
904+
) {
905+
GrobidModels.Flavor validatedModelFlavor = validateModelFlavor(flavor);
906+
String fileName = inputBodyPart.getFormDataContentDisposition().getFileName();
907+
return restProcessTraining.createTrainingBlank(
908+
inputStream, fileName, validatedModelFlavor
909+
);
910+
}
911+
896912
}

grobid-service/src/main/java/org/grobid/service/process/GrobidRestProcessTraining.java

Lines changed: 101 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -490,12 +490,106 @@ public Response createTraining(final InputStream inputStream, final String filen
490490

491491
return response;
492492
}
493-
// GrobidMainArgs pGbdArgs = new GrobidMainArgs();
494-
// pGbdArgs.setPath2Input(inputPath);
495-
//
496-
// try(ProcessEngine processEngine = new ProcessEngine()) {
497-
// processEngine.createTraining(pGbdArgs);
498-
// }
499-
// }
493+
public Response createTrainingBlank(final InputStream inputStream, final String filename, final GrobidModels.Flavor flavor) {
494+
Response response = null;
495+
File originFile = null;
496+
Engine engine = null;
497+
String outputPath = null;
498+
try {
499+
engine = Engine.getEngine(true);
500+
if (engine == null) {
501+
throw new GrobidServiceException(
502+
"No GROBID engine available", Status.SERVICE_UNAVAILABLE);
503+
}
504+
505+
MessageDigest md = MessageDigest.getInstance("MD5");
506+
DigestInputStream dis = new DigestInputStream(inputStream, md);
507+
508+
originFile = IOUtilities.writeInputFile(dis);
509+
if (originFile == null) {
510+
LOGGER.error("The input file cannot be written.");
511+
throw new GrobidServiceException(
512+
"The input file cannot be written.", Status.INTERNAL_SERVER_ERROR);
513+
}
514+
515+
outputPath = GrobidProperties.getTempPath().getPath() + File.separator + KeyGen.getKey();
516+
Files.createDirectories(Path.of(outputPath));
517+
engine.createTrainingBlank(originFile, outputPath, outputPath, -1, flavor);
518+
519+
// Rename all the generated output files with the original filename as suffix
520+
File[] outputFileList = new File(outputPath).listFiles();
521+
if (ArrayUtils.isNotEmpty(outputFileList)) {
522+
String[] split = outputFileList[0].getName().split(".training");
523+
String trainingDataBaseName = split[0];
524+
String inputFileBaseName = FilenameUtils.getBaseName(filename);
525+
for (File file : outputFileList) {
526+
if (file.isFile() && file.getName().startsWith(trainingDataBaseName)) {
527+
String newFileName = file.getName().replace(trainingDataBaseName, inputFileBaseName);
528+
File newFile = new File(outputPath, newFileName);
529+
Files.move(file.toPath(), newFile.toPath());
530+
}
531+
}
532+
} else {
533+
LOGGER.warn("No training files generated.");
534+
}
535+
536+
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
537+
ZipOutputStream out = new ZipOutputStream(outputStream);
538+
539+
File outputPathDir = new File(outputPath);
540+
if (outputPathDir.exists()) {
541+
File[] files = outputPathDir.listFiles();
542+
if (files != null) {
543+
byte[] buffer = new byte[1024];
544+
for (final File currFile : files) {
545+
try {
546+
ZipEntry ze = new ZipEntry(currFile.getName());
547+
out.putNextEntry(ze);
548+
FileInputStream in = new FileInputStream(currFile);
549+
int len;
550+
while ((len = in.read(buffer)) > 0) {
551+
out.write(buffer, 0, len);
552+
}
553+
in.close();
554+
out.closeEntry();
555+
} catch (IOException e) {
556+
throw new GrobidServiceException("IO Exception when zipping", e, Status.INTERNAL_SERVER_ERROR);
557+
}
558+
}
559+
}
560+
}
561+
out.finish();
562+
563+
String outputFilename = StringUtils.replaceIgnoreCase(filename, "pdf", "zip");
564+
565+
response = Response
566+
.ok()
567+
.type("application/zip")
568+
.entity(outputStream.toByteArray())
569+
.header("Content-Disposition", "attachment; filename=\""+ outputFilename +"\"")
570+
.build();
571+
out.close();
572+
573+
} catch (NoSuchElementException nseExp) {
574+
LOGGER.error("Could not get an engine from the pool within configured time. Sending service unavailable.");
575+
response = Response.status(Status.SERVICE_UNAVAILABLE).build();
576+
} catch (Exception exp) {
577+
LOGGER.error("An unexpected exception occurs. ", exp);
578+
response = Response.status(Status.INTERNAL_SERVER_ERROR).entity(exp.getMessage()).build();
579+
} finally {
580+
if (originFile != null)
581+
IOUtilities.removeTempFile(originFile);
582+
583+
if (outputPath != null) {
584+
IOUtilities.removeTempDirectory(outputPath);
585+
}
586+
587+
if (engine != null) {
588+
GrobidPoolingFactory.returnEngine(engine);
589+
}
590+
}
591+
592+
return response;
593+
}
500594
}
501595

0 commit comments

Comments
 (0)