Soumik Bose commited on
Commit
e04decc
·
0 Parent(s):

initial commit

Browse files
.idea/.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Default ignored files
2
+ /shelf/
3
+ /workspace.xml
.idea/compiler.xml ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="CompilerConfiguration">
4
+ <annotationProcessing>
5
+ <profile name="Maven default annotation processors profile" enabled="true">
6
+ <sourceOutputDir name="target/generated-sources/annotations" />
7
+ <sourceTestOutputDir name="target/generated-test-sources/test-annotations" />
8
+ <outputRelativeToContentRoot value="true" />
9
+ <module name="pdf-parser" />
10
+ </profile>
11
+ </annotationProcessing>
12
+ </component>
13
+ <component name="JavacSettings">
14
+ <option name="ADDITIONAL_OPTIONS_OVERRIDE">
15
+ <module name="pdf-parser" options="-parameters" />
16
+ </option>
17
+ </component>
18
+ </project>
.idea/encodings.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="Encoding">
4
+ <file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
5
+ </component>
6
+ </project>
.idea/jarRepositories.xml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="RemoteRepositoriesConfiguration">
4
+ <remote-repository>
5
+ <option name="id" value="central" />
6
+ <option name="name" value="central" />
7
+ <option name="url" value="https://repo.maven.apache.org/maven2" />
8
+ </remote-repository>
9
+ <remote-repository>
10
+ <option name="id" value="central" />
11
+ <option name="name" value="Maven Central repository" />
12
+ <option name="url" value="https://repo1.maven.org/maven2" />
13
+ </remote-repository>
14
+ <remote-repository>
15
+ <option name="id" value="vera-dev" />
16
+ <option name="name" value="vera-dev" />
17
+ <option name="url" value="https://artifactory.openpreservation.org/artifactory/vera-dev" />
18
+ </remote-repository>
19
+ <remote-repository>
20
+ <option name="id" value="jboss.community" />
21
+ <option name="name" value="JBoss Community repository" />
22
+ <option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
23
+ </remote-repository>
24
+ </component>
25
+ </project>
.idea/misc.xml ADDED
@@ -0,0 +1,12 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="ExternalStorageConfigurationManager" enabled="true" />
4
+ <component name="MavenProjectsManager">
5
+ <option name="originalFiles">
6
+ <list>
7
+ <option value="$PROJECT_DIR$/pom.xml" />
8
+ </list>
9
+ </option>
10
+ </component>
11
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="ms-21" project-jdk-type="JavaSDK" />
12
+ </project>
.idea/vcs.xml ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project version="4">
3
+ <component name="VcsDirectoryMappings">
4
+ <mapping directory="" vcs="Git" />
5
+ </component>
6
+ </project>
Dockerfile ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Stage 1: Build the application
2
+ FROM maven:3.9.6-eclipse-temurin-17-alpine AS build
3
+ WORKDIR /app
4
+
5
+ # Copy only the pom.xml first to cache dependencies
6
+ COPY pom.xml .
7
+ RUN mvn dependency:go-offline -B
8
+
9
+ # Copy the source code and build the application
10
+ COPY src ./src
11
+ RUN mvn clean package -DskipTests
12
+
13
+ # Stage 2: Run the application
14
+ FROM eclipse-temurin:17-jre-alpine
15
+ WORKDIR /app
16
+
17
+ # Create a non-root user for security
18
+ RUN addgroup -S spring && adduser -S spring -G spring
19
+ USER spring:spring
20
+
21
+ # Copy the jar from the build stage
22
+ # The name matches your <artifactId>-<version> from pom.xml
23
+ COPY --from=build /app/target/pdf-parser-1.0.0.jar app.jar
24
+
25
+ # Koyeb usually uses port 8080 by default
26
+ EXPOSE 8080
27
+
28
+ ENTRYPOINT ["java", "-jar", "app.jar"]
pom.xml ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <project xmlns="http://maven.apache.org/POM/4.0.0"
3
+ xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
+ xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
5
+ <modelVersion>4.0.0</modelVersion>
6
+
7
+ <parent>
8
+ <groupId>org.springframework.boot</groupId>
9
+ <artifactId>spring-boot-starter-parent</artifactId>
10
+ <version>3.2.5</version>
11
+ <relativePath/>
12
+ </parent>
13
+
14
+ <groupId>com.example</groupId>
15
+ <artifactId>pdf-parser</artifactId>
16
+ <version>1.0.0</version>
17
+
18
+ <properties>
19
+ <java.version>17</java.version>
20
+ </properties>
21
+
22
+ <dependencies>
23
+ <dependency>
24
+ <groupId>org.springframework.boot</groupId>
25
+ <artifactId>spring-boot-starter-web</artifactId>
26
+ </dependency>
27
+ <dependency>
28
+ <groupId>org.opendataloader</groupId>
29
+ <artifactId>opendataloader-pdf-core</artifactId>
30
+ <version>1.11.0</version>
31
+ </dependency>
32
+ </dependencies>
33
+
34
+ <repositories>
35
+ <repository>
36
+ <id>central</id>
37
+ <url>https://repo.maven.apache.org/maven2</url>
38
+ </repository>
39
+ <repository>
40
+ <id>vera-dev</id>
41
+ <url>https://artifactory.openpreservation.org/artifactory/vera-dev</url>
42
+ <snapshots><enabled>true</enabled></snapshots>
43
+ </repository>
44
+ </repositories>
45
+
46
+ <build>
47
+ <plugins>
48
+ <plugin>
49
+ <groupId>org.springframework.boot</groupId>
50
+ <artifactId>spring-boot-maven-plugin</artifactId>
51
+ </plugin>
52
+ </plugins>
53
+ </build>
54
+ </project>
src/main/java/com/example/pdfparser/PdfParserApplication.java ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.example.pdfparser;
2
+
3
+ import org.springframework.boot.SpringApplication;
4
+ import org.springframework.boot.autoconfigure.SpringBootApplication;
5
+ import org.springframework.scheduling.annotation.EnableScheduling;
6
+
7
+ @SpringBootApplication
8
+ @EnableScheduling
9
+ public class PdfParserApplication {
10
+
11
+ public static void main(String[] args) {
12
+ SpringApplication.run(PdfParserApplication.class, args);
13
+ }
14
+ }
src/main/java/com/example/pdfparser/README.md ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: ParseFlow
3
+ emoji: 📄
4
+ colorFrom: yellow
5
+ colorTo: indigo
6
+ sdk: docker
7
+ app_port: 7860
8
+ ---
src/main/java/com/example/pdfparser/controller/PdfController.java ADDED
@@ -0,0 +1,33 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.example.pdfparser.controller;
2
+
3
+ import com.example.pdfparser.model.ParseResult;
4
+ import com.example.pdfparser.service.PdfParserService;
5
+ import org.slf4j.Logger;
6
+ import org.slf4j.LoggerFactory;
7
+ import org.springframework.http.MediaType;
8
+ import org.springframework.http.ResponseEntity;
9
+ import org.springframework.web.bind.annotation.*;
10
+ import org.springframework.web.multipart.MultipartFile;
11
+
12
+ import java.io.IOException;
13
+
14
+ @RestController
15
+ @RequestMapping("/api/pdf")
16
+ public class PdfController {
17
+
18
+ private static final Logger log = LoggerFactory.getLogger(PdfController.class);
19
+ private final PdfParserService parserService;
20
+
21
+ public PdfController(PdfParserService parserService) {
22
+ this.parserService = parserService;
23
+ }
24
+
25
+ @PostMapping(value = "/parse", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
26
+ public ResponseEntity<ParseResult> parse(
27
+ @RequestPart("file") MultipartFile file,
28
+ @RequestParam(defaultValue = "markdown,json,html") String formats
29
+ ) throws IOException {
30
+ log.info("Received parse request file={} formats={}", file.getOriginalFilename(), formats);
31
+ return ResponseEntity.ok(parserService.parse(file, formats));
32
+ }
33
+ }
src/main/java/com/example/pdfparser/controller/PingController.java ADDED
@@ -0,0 +1,26 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.example.pdfparser.controller;
2
+
3
+ import org.springframework.web.bind.annotation.GetMapping;
4
+ import org.springframework.web.bind.annotation.RestController;
5
+
6
+ import java.util.Map;
7
+
8
+ @RestController
9
+ public class PingController {
10
+
11
+ @GetMapping("/")
12
+ public Map<String, String> root() {
13
+ return Map.of(
14
+ "status", "ok",
15
+ "message", "PDF Parser API is running"
16
+ );
17
+ }
18
+
19
+ @GetMapping("/ping")
20
+ public Map<String, String> ping() {
21
+ return Map.of(
22
+ "status", "ok",
23
+ "message", "pong"
24
+ );
25
+ }
26
+ }
src/main/java/com/example/pdfparser/model/ParseResult.java ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.example.pdfparser.model;
2
+
3
+ import java.util.Map;
4
+
5
+ public record ParseResult(
6
+ String fileName,
7
+ String markdown,
8
+ String html,
9
+ Map<String, Object> json
10
+ ) {}
src/main/java/com/example/pdfparser/service/PdfParserService.java ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.example.pdfparser.service;
2
+
3
+ import com.example.pdfparser.model.ParseResult;
4
+ import com.fasterxml.jackson.databind.ObjectMapper;
5
+ import jakarta.annotation.PreDestroy;
6
+ import org.opendataloader.pdf.api.Config;
7
+ import org.opendataloader.pdf.api.OpenDataLoaderPDF;
8
+ import org.slf4j.Logger;
9
+ import org.slf4j.LoggerFactory;
10
+ import org.springframework.stereotype.Service;
11
+ import org.springframework.web.multipart.MultipartFile;
12
+
13
+ import java.io.File;
14
+ import java.io.IOException;
15
+ import java.nio.file.Files;
16
+ import java.nio.file.Path;
17
+ import java.util.Map;
18
+ import java.util.Objects;
19
+
20
+ @Service
21
+ public class PdfParserService {
22
+
23
+ private static final Logger log = LoggerFactory.getLogger(PdfParserService.class);
24
+ private final ObjectMapper objectMapper = new ObjectMapper();
25
+
26
+ public ParseResult parse(MultipartFile file, String formats) throws IOException {
27
+ log.info("Parsing file={} size={} formats={}", file.getOriginalFilename(), file.getSize(), formats);
28
+
29
+ Path inputDir = Files.createTempDirectory("odl-in-");
30
+ Path outputDir = Files.createTempDirectory("odl-out-");
31
+
32
+ File pdfFile = inputDir.resolve(Objects.requireNonNull(file.getOriginalFilename())).toFile();
33
+ file.transferTo(pdfFile);
34
+
35
+ Config config = new Config();
36
+ config.setOutputFolder(outputDir.toString());
37
+ config.setGenerateMarkdown(formats.contains("markdown"));
38
+ config.setGenerateHtml(formats.contains("html"));
39
+ config.setGeneratePDF(formats.contains("pdf"));
40
+
41
+ OpenDataLoaderPDF.processFile(pdfFile.getAbsolutePath(), config);
42
+ log.info("Processing complete for file={}", file.getOriginalFilename());
43
+
44
+ String markdown = null;
45
+ String html = null;
46
+ Map<String, Object> json = null;
47
+
48
+ for (File out : Objects.requireNonNull(outputDir.toFile().listFiles())) {
49
+ if (out.getName().endsWith(".md")) markdown = Files.readString(out.toPath());
50
+ if (out.getName().endsWith(".html")) html = Files.readString(out.toPath());
51
+ if (out.getName().endsWith(".json")) json = objectMapper.readValue(out, objectMapper.getTypeFactory().constructMapType(Map.class, String.class, Object.class));
52
+ }
53
+
54
+ return new ParseResult(file.getOriginalFilename(), markdown, html, json);
55
+ }
56
+
57
+ @PreDestroy
58
+ public void shutdown() {
59
+ log.info("Shutting down OpenDataLoader PDF engine");
60
+ OpenDataLoaderPDF.shutdown();
61
+ }
62
+ }
src/main/java/com/example/pdfparser/service/SelfPingService.java ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package com.example.pdfparser.service;
2
+
3
+ import org.springframework.beans.factory.annotation.Value;
4
+ import org.springframework.scheduling.annotation.Scheduled;
5
+ import org.springframework.stereotype.Service;
6
+
7
+ import java.net.HttpURLConnection;
8
+ import java.net.URL;
9
+
10
+ @Service
11
+ public class SelfPingService {
12
+
13
+ @Value("${app.base-url}")
14
+ private String baseUrl;
15
+
16
+ @Scheduled(fixedDelay = 300000)
17
+ public void selfPing() {
18
+ try {
19
+ String pingUrl = baseUrl + "/ping";
20
+
21
+ URL url = new URL(pingUrl);
22
+ HttpURLConnection conn = (HttpURLConnection) url.openConnection();
23
+ conn.setRequestMethod("GET");
24
+ conn.setConnectTimeout(5000);
25
+ conn.setReadTimeout(5000);
26
+
27
+ int responseCode = conn.getResponseCode();
28
+ System.out.println("Self-ping success: " + responseCode);
29
+
30
+ } catch (Exception e) {
31
+ System.err.println("Self-ping failed: " + e.getMessage());
32
+ }
33
+ }
34
+ }
src/main/resources/application.properties ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ server.port=8000
2
+
3
+ spring.servlet.multipart.max-file-size=100MB
4
+ spring.servlet.multipart.max-request-size=100MB
5
+ logging.level.com.example.pdfparser=INFO
6
+
7
+ app.base-url=https://voluminous-termite-web-assistant-79d73c7a.koyeb.app
target/classes/application.properties ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ server.port=8000
2
+
3
+ spring.servlet.multipart.max-file-size=100MB
4
+ spring.servlet.multipart.max-request-size=100MB
5
+ logging.level.com.example.pdfparser=INFO
6
+
7
+ app.base-url=https://voluminous-termite-web-assistant-79d73c7a.koyeb.app
target/classes/com/example/pdfparser/PdfParserApplication.class ADDED
Binary file (825 Bytes). View file
 
target/classes/com/example/pdfparser/controller/PdfController.class ADDED
Binary file (2.36 kB). View file
 
target/classes/com/example/pdfparser/controller/PingController.class ADDED
Binary file (1 kB). View file
 
target/classes/com/example/pdfparser/model/ParseResult.class ADDED
Binary file (2.24 kB). View file
 
target/classes/com/example/pdfparser/service/PdfParserService.class ADDED
Binary file (4.31 kB). View file
 
target/classes/com/example/pdfparser/service/SelfPingService.class ADDED
Binary file (2.08 kB). View file